文章目录

  • Zygote 概述
  • Zygote 进程的初始化
    • app_process 的 main 函数
    • main函数启动流程
    • 启动虚拟机 AndroidRuntime类
    • 启动虚拟机
    • 初始化工作 ZygoteInit 类
  • 总结一下 Zygote 进程启动过程

Zygote 概述

  • Zygote 时android中非常重要的一个进程,它和 init 进程、SystemServer 进程同为支持Android最重要的进程。、
  • Linux 的进程时通过系统调用 fork 产生的,fork 出的子进程除了内核中的一些核心数据结构和父进程不同外,其余的内存影像都是和父进程共享的。只有当子进程需要去修改这些共享内存时,操作系统才会为子进程分配一个新的页面,并将老的页面上的数据复制一份到新的页面,这就是所谓的写时拷贝(Copy On Write)
  • 通常子进程被fork出来后,会继续执行系统调用 exec 。exec将用一个新的可执行文件的内容替换当前的代码块、数据段、堆和栈。fork + exec 是 Linux 启动应用标准做法,init 也是通过这样启动各种服务。
  • Zygote 启动各种应用时却只调用了 fork 没有调用 exec。Android引用中执行的是 java 代码,Java代码不同才造成了应用的不同,而对于Java 运行环境是相同的。
  • Zygote 初始化时会创建虚拟机,同时把需要的系统类库和资源文件加载到内存里。Zygote fork出进程后,这个子进程也继承了能正常工作的虚拟机和各种系统资源,接下来的子进程中需要加在 APK 中的字节码就可以运行了。这样运行时间会大大缩短。
  • 下图时 init 和 Zygote 区别

Zygote 进程的初始化

  • Zygote 进程在 init 进程中以 Service 的方式启动的,5.0 以前是直接放在 init.rc 的代码块中,现在是放在了单独的文件中,然后在 init.rc 通过 import 方式引入文件,如下:
import /init.${ro.zygote}.rc

从import中可以看出,init.rc 并不是直接引入了某个固定的文件而是根据ro.zygote的属性来引入不同的文件,这是因为从 5.0 开始android开始支持64位编译,Zygote 本身也会有 32位和64位的区别,因此通过 ro.zygote 来控制启动不同的版本的 Zygote 进程。属性 ro.zygote 可能有“zygote32”、“zygote32_64”、“zygote64”、“zygote64_32”。所以在 init.rc同级目录下可能有四个zygote.rc文件,我在下载 9.0源码就可以搜索出如下结果:

  • 下面是 zygote32.rc 文件内容
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-serverclass mainpriority -20user rootgroup root readproc reserved_disksocket zygote stream 660 root systemonrestart write /sys/android_power/request_state wakeonrestart write /sys/power/state ononrestart restart audioserveronrestart restart cameraserveronrestart restart mediaonrestart restart netdonrestart restart wificondwritepid /dev/cpuset/foreground/tasks
  • 下面是 zygote32_64.rc 文件内容
service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygoteclass mainpriority -20user rootgroup root readproc reserved_disksocket zygote stream 660 root systemonrestart write /sys/android_power/request_state wakeonrestart write /sys/power/state ononrestart restart audioserveronrestart restart cameraserveronrestart restart mediaonrestart restart netdonrestart restart wificondwritepid /dev/cpuset/foreground/tasksservice zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondaryclass mainpriority -20user rootgroup root readproc reserved_disksocket zygote_secondary stream 660 root systemonrestart restart zygotewritepid /dev/cpuset/foreground/tasks

从上面两个文件中可以看出 zygote32_64.rc 有两个 Zygote 服务,zygote 和 zygote_secondary,这两个服务最大的区别是启动可执行文件不同,一个是app_process32,另外一个是 app_process64。zygote64 和 zygote64_32 也差不带多,只不过把可执行文件换过来了。从这里可以知道Android支持四种运行模式。

  • 纯32位模式:属性为ro.zygote的值为zygote32
  • 以32位为主,64位为辅:属性为ro.zygote的值为zygote32_64
  • 纯64位模式:属性为ro.zygote的值为zygote64
  • 以64位为主,32位为辅:属性为ro.zygote的值为zygote64_32
  • 从上面 Zygote 文件可以看出 Zygote进程可执行文件是 app_process,app_process文件的源文件位于下图:

app_process 的 main 函数

  • main 函数主要功能是解析启动参数。虽然Zygote 进程是通过 app_process 可执行文件创建的,但是app_process 除了可以创建 Zygote 外,还可以创建普通进程。前面介绍 Zygote 进程创建的时候是没有调用 exec ,从执行角度来看是有优点的,但是在某些情况下,例如调试内存分配时,会带来很多麻烦。所以Android也提供了传统方式启动应用。

  • 这两种启动方式是通过参数来区别的

启动格式为:app_process[虚拟机参数] 运行目录 参数 [java 类]

  • 虚拟机参数:以 _ 开头启动虚拟机时传递给虚拟机
  • 运行目录:程序运行目录,通常是 /system/bin
  • 参数:以–开头。参数 --zygote 表示启动 zygote,参数 --application表示以普通进程方式执行Java代码。
  • Java类:将要执行的Java类,必须有一个静态的 main 。使用 -Zygote 时不会执行这个类,而是固定的执行 ZygoteInit 类。

main函数启动流程

  • 创建AppRuntime 对象并保存参数
    AppRuntime是在app_process 中定义的类,继承了系统的 AndroidRuntime类。AndroidRuntime类主要作用是创建初始化虚拟机。
int main(int argc, char* const argv[])
{if (!LOG_NDEBUG) {String8 argv_String;for (int i = 0; i < argc; ++i) {argv_String.append("\"");argv_String.append(argv[i]);argv_String.append("\" ");}ALOGV("app_process main with argv: %s", argv_String.string());}AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));// Process command line arguments// ignore argv[0]argc--;argv++;const char* spaced_commands[] = { "-cp", "-classpath" };// Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).bool known_command = false;int i;for (i = 0; i < argc; i++) {if (known_command == true) {runtime.addOption(strdup(argv[i]));// The static analyzer gets upset that we don't ever free the above// string. Since the allocation is from main, leaking it doesn't seem// problematic. NOLINTNEXTLINEALOGV("app_process main add known option '%s'", argv[i]);known_command = false;continue;}for (int j = 0;j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));++j) {if (strcmp(argv[i], spaced_commands[j]) == 0) {known_command = true;ALOGV("app_process main found known command '%s'", argv[i]);}}if (argv[i][0] != '-') {break;}if (argv[i][1] == '-' && argv[i][2] == 0) {++i; // Skip --.break;}runtime.addOption(strdup(argv[i]));ALOGV("app_process main add option '%s'", argv[i]);}
  • 解析启动参数
int i;for (i = 0; i < argc; i++) {if (known_command == true) {runtime.addOption(strdup(argv[i]));// The static analyzer gets upset that we don't ever free the above// string. Since the allocation is from main, leaking it doesn't seem// problematic. NOLINTNEXTLINEALOGV("app_process main add known option '%s'", argv[i]);known_command = false;continue;}for (int j = 0;j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));++j) {if (strcmp(argv[i], spaced_commands[j]) == 0) {known_command = true;ALOGV("app_process main found known command '%s'", argv[i]);}}if (argv[i][0] != '-') {break;}if (argv[i][1] == '-' && argv[i][2] == 0) {++i; // Skip --.break;}runtime.addOption(strdup(argv[i]));// The static analyzer gets upset that we don't ever free the above// string. Since the allocation is from main, leaking it doesn't seem// problematic. NOLINTNEXTLINEALOGV("app_process main add option '%s'", argv[i]);}

通常是从 init.rc 中传入的参数,“-Xzygote/system/bin --zygote --start-system-server”,解析后得到的结果将是

  • 变量 parentDir 等于 /system/bin
  • 变量 niceName 等于 zygote
  • 变量 startSystemServer 等于 true
  • 变量 zygote 等于 true
  • 准备执行 ZygoteInit 或者 RuntimeInit 类的参数
   Vector<String8> args;if (!className.isEmpty()) {// We're not in zygote mode, the only argument we need to pass// to RuntimeInit is the application argument.//// The Remainder of args get passed to startup class main(). Make// copies of them before we overwrite them with the process name.args.add(application ? String8("application") : String8("tool"));runtime.setClassNameAndArgs(className, argc - i, argv + i);if (!LOG_NDEBUG) {String8 restOfArgs;char* const* argv_new = argv + i;int argc_new = argc - i;for (int k = 0; k < argc_new; ++k) {restOfArgs.append("\"");restOfArgs.append(argv_new[k]);restOfArgs.append("\" ");}ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string());}} else {// We're in zygote mode.maybeCreateDalvikCache();if (startSystemServer) {args.add(String8("start-system-server"));}char prop[PROP_VALUE_MAX];if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",ABI_LIST_PROPERTY);return 11;}String8 abiFlag("--abi-list=");abiFlag.append(prop);args.add(abiFlag);// In zygote mode, pass all remaining arguments to the zygote// main() method.for (; i < argc; ++i) {args.add(String8(argv[i]));}}
  • 将本进程的名称改为–nice-name 指定的字符串。缺省情况下niceName为zygote或者zygote64
    if (!niceName.isEmpty()) {runtime.setArgv0(niceName.string(), true /* setProcName */);}
  • 启动 Java 类,如果参数带有 --zygote 执行 zygoteInit类,否则执行传进来的Java类。
    if (zygote) {runtime.start("com.android.internal.os.ZygoteInit", args, zygote);} else if (className) {runtime.start("com.android.internal.os.RuntimeInit", args, zygote);} else {fprintf(stderr, "Error: no class name or --zygote supplied.\n");app_usage();LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");}

app_process 除了能启动 Zygote进程还可以执行系统的某个Java类。Android手机常用工具 am 就是一个很好的例子。am 是一个通过发送 Intent 来启动应用程序的工具,但是 am 只是一个包含几行代码的脚本文件,实际上启动还是通过 app_process 来完成的。
am基本用法

  • am start -n 包(package)名/包名.活动(activity)名称

启动虚拟机 AndroidRuntime类

  • Android Runtime类是 Android底层很重要的一个类,它负责启动虚拟机以及Java线程。AndroidRuntime在一个进程中只有一个实例对象,保存在全局变量 gCurRuntime中。
  • AndroidRuntime 构造函数如下(android9.0)

AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) :mExitWithoutCleanup(false),mArgBlockStart(argBlockStart),mArgBlockLength(argBlockLength)
{// 初始化 skia 图形系统;skia是个Google的2D向量图形处理函数库,包含字型、座标转换,以及点阵图都有高效能且简洁的表现。SkGraphics::Init();// Pre-allocate enough space to hold a fair number of options.// 预先分配空间来存放虚拟机的参数mOptions.setCapacity(20);// 只能初始化一次 gCurRuntime assert(gCurRuntime == NULL);        // one per processgCurRuntime = this;
}

AndroidRuntime 的构造函数中,5.0以前会对 skia图形系统进行设置,把底层使用的图形格式设置成 RGB565,一种16位图形格式,16位的图像格式没有24位的色彩丰富,但是能节省内存空间。Android5.0以后去调了这个设置,只保留了 mOptions.setCapacity(20); 来设置虚拟机的参数。
最后 AndroidRuntime 指针被放入了 gCurRuntime 全局指针,但是前面的 main() 函数中AndroidRuntime 是作为一个局部变量 runtime 创建的。这是因为如果启动的 Zygote ,在 ZygoteInit 中会进入等待 Socket 事件循环中,这样布局变量并不会被销毁,如果只执行一个 Java 类,执行结束完就结束了,说 runtime是一个局部变量也没什么问题。

启动虚拟机

 if (zygote) {runtime.start("com.android.internal.os.ZygoteInit", args, zygote);} else if (className) {runtime.start("com.android.internal.os.RuntimeInit", args, zygote);} else {fprintf(stderr, "Error: no class name or --zygote supplied.\n");app_usage();LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");}
  • 在main() 函数的结尾,调用了 AndroidRuntime的start函数来执行 Java类。Zygote在运行Java前,还需要初始化整个Java环境,下面看看 start() 函数执行流程。
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{// 打印Log,标志着Android系统已经启动 因为以后的进程都是从 Zygote fork出来的所以不会再执行 start 函数了。// 如果Log反复出现下面Log ,则说明系统出错了如果id是zygote 则说明 zygote正在不断重启。ALOGD(">>>>>> START %s uid %d <<<<<<\n",className != NULL ? className : "(unknown)", getuid());static const String8 startSystemServer("start-system-server");/** 'startSystemServer == true' means runtime is obsolete and not run from* init.rc anymore, so we print out the boot start event here.*/for (size_t i = 0; i < options.size(); ++i) {if (options[i] == startSystemServer) {/* track our progress through the boot sequence */const int LOG_BOOT_PROGRESS_START = 3000;LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,  ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));}}// 系统目录从环境变量 ANDROID_ROOT 中获取,如果没有获取到则默认目录位 /system ;// !hasDir("/system") 如果没有/System  则直接 return 退出了// 系统目录是在 init 进程中创建出来的const char* rootDir = getenv("ANDROID_ROOT");if (rootDir == NULL) {rootDir = "/system";if (!hasDir("/system")) {LOG_FATAL("No root directory specified, and /android does not exist.");return;}setenv("ANDROID_ROOT", rootDir, 1);}//const char* kernelHack = getenv("LD_ASSUME_KERNEL");//ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);/* start the virtual machine */// 启动虚拟机 5.0 以后启动的是 Art虚拟机JniInvocation jni_invocation;jni_invocation.Init(NULL);JNIEnv* env;if (startVm(&mJavaVM, &env, zygote) != 0) {return;}// 调用虚拟机 onVmCreated onVmCreated类实际上是一个虚函数,调用它实际上调用的是继承类 AndroidRuntime 中的重载函数。下面会贴出来onVmCreated(env);/** Register android functions. 注册系统 JNI 函数* startReg 通过调用 register_jni_procs(gRegJNI, NELEM(gRegJNI), env) 函数将全局数组gRegJNI中的JNI本地函数在虚拟机中注册*/if (startReg(env) < 0) {ALOGE("Unable to register all android natives\n");return;}/** We want to call main() with a String array with arguments in it.* At present we have two arguments, the class name and an option string.* Create an array to hold them.*/// 下面是准备 Java 类的 main 函数的参数jclass stringClass;jobjectArray strArray;jstring classNameStr;stringClass = env->FindClass("java/lang/String");assert(stringClass != NULL);// 先通过 NewObjectArray 创建两个元素的数组strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);assert(strArray != NULL);classNameStr = env->NewStringUTF(className);assert(classNameStr != NULL);// 通过 SetObjectArrayElement 为数组赋值 因为要到Java的世界所以会比较麻烦env->SetObjectArrayElement(strArray, 0, classNameStr);for (size_t i = 0; i < options.size(); ++i) {jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());assert(optionsStr != NULL);env->SetObjectArrayElement(strArray, i + 1, optionsStr);}/** Start VM.  This thread becomes the main thread of the VM, and will* not return until the VM exits.*/char* slashClassName = toSlashClassName(className != NULL ? className : "");jclass startClass = env->FindClass(slashClassName);if (startClass == NULL) {ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);/* keep going */} else {// 先通过 GetStaticMethodID 获取 Java main 函数的idjmethodID startMeth = env->GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V");if (startMeth == NULL) {ALOGE("JavaVM unable to find main() in '%s'\n", className);/* keep going */} else {// 然后通过 CallStaticVoidMethod 来调用 Java 层函数了,至此 Zygote初始化将转入 Java 函数了。// 如果不是启动 Zygote 则执行 Java 函数的是 RuntimeInitenv->CallStaticVoidMethod(startClass, startMeth, strArray);#if 0if (env->ExceptionCheck())threadExitUncaughtException(env);
#endif}}free(slashClassName);ALOGD("Shutting down VM\n");if (mJavaVM->DetachCurrentThread() != JNI_OK)ALOGW("Warning: unable to detach main thread\n");if (mJavaVM->DestroyJavaVM() != 0)ALOGW("Warning: VM did not shut down cleanly\n");
}
  • onVmCreated

onVmCreated会在当前虚拟机环境中根据类名来查找类对象,这表明 app_process 将要调用的Java类对象必须是系统Java类,而不能是随意的应用java类,如果调用普通Java类对象则方式不同。

   virtual void onVmCreated(JNIEnv* env){if (mClassName.isEmpty()) {return; // Zygote. Nothing to do here.}/** This is a little awkward because the JNI FindClass call uses the* class loader associated with the native method we're executing in.* If called in onStarted (from RuntimeInit.finishInit because we're* launching "am", for example), FindClass would see that we're calling* from a boot class' native method, and so wouldn't look for the class* we're trying to look up in CLASSPATH. Unfortunately it needs to,* because the "am" classes are not boot classes.** The easiest fix is to call FindClass here, early on before we start* executing boot class Java code and thereby deny ourselves access to* non-boot classes.*/char* slashClassName = toSlashClassName(mClassName.string());mClass = env->FindClass(slashClassName);if (mClass == NULL) {ALOGE("ERROR: could not find class '%s'\n", mClassName.string());}free(slashClassName);mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));}

初始化工作 ZygoteInit 类

  • Zygote 的 main 方法做了大概一下几个事情
    :解析调用参数
    :注册 Zygote 的 socket 监听接口,用来接收启动应用程序监听
    :调用 preload() 方法装载系统资源。这样fork出的应用可以包含这些资源了 加快了启动速度。
    :启动 SystemServer 进程
    :调用 runSelectLoo() 方法进入监听和接收消息的循环
    public static void main(String argv[]) {ZygoteServer zygoteServer = new ZygoteServer();// Mark zygote start. This ensures that thread creation will throw// an error.ZygoteHooks.startZygoteNoThreadCreation();// Zygote goes into its own process group.try {Os.setpgid(0, 0);} catch (ErrnoException ex) {throw new RuntimeException("Failed to setpgid(0,0)", ex);}final Runnable caller;try {// Report Zygote start time to tron unless it is a runtime restartif (!"1".equals(SystemProperties.get("sys.boot_completed"))) {MetricsLogger.histogram(null, "boot_zygote_init",(int) SystemClock.elapsedRealtime());}String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,Trace.TRACE_TAG_DALVIK);bootTimingsTraceLog.traceBegin("ZygoteInit");RuntimeInit.enableDdms();boolean startSystemServer = false;String socketName = "zygote";String abiList = null;boolean enableLazyPreload = false;// 解析调用的参数for (int i = 1; i < argv.length; i++) {if ("start-system-server".equals(argv[i])) {startSystemServer = true;} else if ("--enable-lazy-preload".equals(argv[i])) {enableLazyPreload = true;} else if (argv[i].startsWith(ABI_LIST_ARG)) {abiList = argv[i].substring(ABI_LIST_ARG.length());} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {socketName = argv[i].substring(SOCKET_NAME_ARG.length());} else {throw new RuntimeException("Unknown command line argument: " + argv[i]);}}if (abiList == null) {throw new RuntimeException("No ABI list supplied.");}// 注册 Zygote 的 socket 监听接口,用来接收启动应用程序的消息zygoteServer.registerServerSocketFromEnv(socketName);// In some configurations, we avoid preloading resources and classes eagerly.// In such cases, we will preload things prior to our first fork.if (!enableLazyPreload) {bootTimingsTraceLog.traceBegin("ZygotePreload");EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,SystemClock.uptimeMillis());// 装载系统资源,包括系统预加载类,Framework 资源和 openGL 的资源。// 这样应用程序被 fork 出来后,应用程序内就已经包含了这些系统资源,大大节省了引用启动时间。preload(bootTimingsTraceLog);EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,SystemClock.uptimeMillis());bootTimingsTraceLog.traceEnd(); // ZygotePreload} else {Zygote.resetNicePriority();}// Do an initial gc to clean up after startupbootTimingsTraceLog.traceBegin("PostZygoteInitGC");gcAndFinalize();bootTimingsTraceLog.traceEnd(); // PostZygoteInitGCbootTimingsTraceLog.traceEnd(); // ZygoteInit// Disable tracing so that forked processes do not inherit stale tracing tags from// Zygote.Trace.setTracingEnabled(false, 0);Zygote.nativeSecurityInit();// Zygote process unmounts root storage spaces.Zygote.nativeUnmountStorageOnInit();ZygoteHooks.stopZygoteNoThreadCreation();if (startSystemServer) {// 准备 SystemServer 参数并且 fork 出 SystemServer 进程Runnable r = forkSystemServer(abiList, socketName, zygoteServer);// {@code r == null} in the parent (zygote) process, and {@code r != null} in the// child (system_server) process.if (r != null) {r.run();return;}}Log.i(TAG, "Accepting command socket connections");// The select loop returns early in the child process after a fork and// loops forever in the zygote.// 通过调用 runSelectLoop 进入监听和接收消息的环节 里面有一个 while (true) caller = zygoteServer.runSelectLoop(abiList);} catch (Throwable ex) {Log.e(TAG, "System zygote died with exception", ex);throw ex;} finally {zygoteServer.closeServerSocket();}// We're in the child process and have exited the select loop. Proceed to execute the// command.if (caller != null) {caller.run();}}

总结一下 Zygote 进程启动过程

  • init 进程 fork 出 zygote 进程
  • 启动虚拟机注册 jni 函数
  • 预加载系统资源
  • 启动 SystemServer
  • 进入 Socket Loop 循环等待消息

Android Framework- Zygote进程以及其初始化相关推荐

  1. 从源码解析-Android中Zygote进程是如何fork一个APP进程的

    zygote进程fork子进程 前言 强烈推荐 进程创建流程 APP启动第三方应用 startActivity startService sendBroadcast ContentResolver.q ...

  2. Android Framework——zygote 启动 SystemServer

    概述 在Android系统中,所有的应用程序进程以及系统服务进程SystemServer都是由Zygote进程孕育(fork)出来的,这也许就是为什么要把它称为Zygote(受精卵)的原因吧.由于Zy ...

  3. 从源码角度看Android系统Zygote进程启动过程

    在Android系统中,DVM.ART.应用程序进程和SystemServer进程都是由Zygote进程创建的,因此Zygote又称为"孵化器".它是通过fork的形式来创建应用程 ...

  4. Android系统启动流程—— init进程zygote进程SystemServer进程启动流程

    原文地址:https://blog.csdn.net/qq_30993595/article/details/82714409 Android系统启动流程 Android系统启动过程往细了说可以分为5 ...

  5. 从源码解析-Android系统启动流程概述 init进程zygote进程SystemServer进程启动流程

    Android系统启动流程 启动流程 Loader Kernel Native Framework Application init进程 启动 rc文件规则 Actions Commands Serv ...

  6. Android 9 (P) Zygote进程启动源码分析指南二

         Android 9 Zygote进程启动源码分析指南二 Android 9 (P) 系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 Andro ...

  7. Android Framework学习目录

    Android 第一个用户进程:Init进程(概述) Zygote进程以及其初始化 Zygote 启动应用程序 Android SystemServer 进程 关于 PackageManagerSer ...

  8. 从源码角度看Android系统SystemServer进程启动过程

    SystemServer进程是由Zygote进程fork生成,进程名为system_server,主要用于创建系统服务. 备注:本文将结合Android8.0的源码看SystemServer进程的启动 ...

  9. Android 系统(12)---Zygote进程启动过程

    android系统进程启动流程 android系统的Zygote进程是所有android进程的父进程,包括SystemServer和各种应用进程都是通过Zygote进程fork出来的.Zygote(孵 ...

  10. Android 进阶——系统启动之Android进程造物者Zygote 进程启动详解(六)

    引言 前面系列文章介绍了Android系统的第一个用户进程--init进程由解析init.rc脚本启动,完成属性系统的初始化等工作后紧接着启动Android系统上的造物者--Zygote进程,这篇文章 ...

最新文章

  1. 几个进程管理的命令:pkill,pidof,kill,killall,job
  2. 2020\Simulation_1\3.叶节点数
  3. 200901阶段一C++类和对象
  4. linux+mysql登录日志_Linux查看登录日志
  5. Nginx http 视频点播服务器搭建操作指南
  6. 盖茨转让18亿美元股票给梅琳达,被传有85后新欢?当事人回应...
  7. freecplus框架-MySQL数据库操作
  8. html5的所有标签页,HTML5所有标签总集
  9. 深入理解计算机系统(2.4)---C语言的有符号与无符号、二进制整数的扩展与截断...
  10. html5实现在线动态画板,HTML5 canvas实现一个简易画板
  11. 【011】Excel宏编程相关封装模块(新建文件、关闭文件、新增/删除工作薄)_004_#VBA
  12. PHP存储微信昵称特殊符号过滤方法
  13. 计算机桌面音量键在哪,电脑音量调节快捷键_电脑音量快捷键设置
  14. FRP内网穿透访问家中的NAS和路由器后台
  15. System.Reflection.ReflectionTypeLoadException
  16. 递归实现 1,1,2,3,5,8,….第 30 个数是多少?
  17. Java CompletableFuture.runAfterEither任何一个完成就执行Runnable
  18. Rikka with Travels【换根树dp】
  19. linux怎么看系统内存多大内存频率,linux 系统管理中的查看内存插槽数最大容量和频率...
  20. 一天测血压的最佳时间_一天中什么时间测量血压最准?心血管医生告诉您最佳时间...

热门文章

  1. vue怎么给pc端浏览器设置一个最小屏幕_创新PC应用、打通云端体验,360小程序引发SaaS软件变革...
  2. 支持PHP运行环境和系统防火墙配置管理,1Panel开源面板v1.1.0发布
  3. sql中的左连接、右链接、内连接
  4. Android原生Gps获取定位
  5. Servlet的@WebServlet注解
  6. npm install A complete log of this run can be found in Unexpected end of JSON input while pars时报错
  7. 搜狐收购Go2Map GIS发展有新前途?
  8. apache端口被占用解决方法
  9. windows常用快捷命令
  10. 数字化孪生技术在工业上的应用场景和案例分享