Zygote进程的创建过程(Android(安卓)8.1)
本篇文章的内容中,我将要给大家介绍Android开发中最长见到的一个底层进程——Zygote进程,它是我们开发的所有APP进程的父进程。可以说,Android中所有的Java进程都是由Zygote进程fork出来的。
那面Zygote进程又是如何启动的呢?它的启动过程从init进程启动开始,到Zygote进程真正运行起来,并且进行相关初始化完成为止。
我们来详细分析下Zygote进程的启动过程。
说明:我们的源码是基于Android 8.1系统进行分析的。
init进程的启动
init进程是Android系统启动的第一个进程。我们来看init进程是如何启动的?(源码位置:system/core/init/init.cpp)
Linux内核启动的是从start_kernel函数开始的,start_kernel是所有Linux平台进入系统内核初始化后的入口函数,它主要完成一系列内核初始化相关工作,并且调用第一个用户进程——init进程。init启动之后,也就代表系统已经顺利地启动了Linux内核。
我们来看init进程的入口函数——init.cpp的main()方法:
int main(int argc, char** argv) { if (!strcmp(basename(argv[0]), "ueventd")) {//1、如果文件名是"ueventd",则执行守护进程ueventd的主函数ueventd_main() return ueventd_main(argc, argv); } if (!strcmp(basename(argv[0]), "watchdogd")) {//2、如果文件名是"watchdogd",则执行看门狗守护进程的主函数 return watchdogd_main(argc, argv); } …… add_environment("PATH", _PATH_DEFPATH);//3、设置环境变量 bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr); if (is_first_stage) { // Clear the umask. umask(0); //4、创建一些基本的目录,包括/dev、/porc、/sysfc等。同时把一些文件系统,如tmpfs、devpt、proc、sysfs等mount到项目的目录。 mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"); mkdir("/dev/pts", 0755); mkdir("/dev/socket", 0755); mount("devpts", "/dev/pts", "devpts", 0, NULL); #define MAKE_STR(x) __STRING(x) mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)); // Don't expose the raw commandline to unprivileged processes. chmod("/proc/cmdline", 0440); gid_t groups[] = { AID_READPROC }; setgroups(arraysize(groups), groups); mount("sysfs", "/sys", "sysfs", 0, NULL); mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL); mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)); mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)); mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)); //5、SELinux初始化逻辑 // Set up SELinux, loading the SELinux policy. selinux_initialize(true); // We're in the kernel domain, so re-exec init to transition to the init domain now // that the SELinux policy has been loaded. if (selinux_android_restorecon("/init", 0) == -1) { PLOG(ERROR) << "restorecon failed"; security_failure(); } …… } …… close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));//6、在/dev目录下创建一个空文件".booting"表示初始化正在进行。初始化结束后,这个文件会被删除。 // Propagate the kernel variables to internal variables // used by init as well as the current required properties. export_kernel_boot_props();//7、主要是调用property_init()函数来初始化Android的属性系统。 …… epoll_fd = epoll_create1(EPOLL_CLOEXEC);//8、调用epoll_create1创建epoll句柄,如果创建失败,则退出。 if (epoll_fd == -1) { PLOG(ERROR) << "epoll_create1 failed"; exit(1); } signal_handler_init();//9、调用signal_handler_init()函数,主要是装载进程信号处理器。当子进程被kill之后,会在父进程接受一个信号。防止称为僵尸进程的子进程占用程序表的空间。 …… //10、解析init.rc文件 std::string bootscript = GetProperty("ro.boot.init_rc", ""); if (bootscript.empty()) { parser.ParseConfig("/init.rc"); parser.set_is_system_etc_init_loaded( parser.ParseConfig("/system/etc/init")); parser.set_is_vendor_etc_init_loaded( parser.ParseConfig("/vendor/etc/init")); parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init")); } else { parser.ParseConfig(bootscript); parser.set_is_system_etc_init_loaded(true); parser.set_is_vendor_etc_init_loaded(true); parser.set_is_odm_etc_init_loaded(true); } …… }
执行过程如上,主要步骤我们通过注释可以清楚的了解到。
init进程启动过程中,会执行init.rc脚本文件(位置: /system/core/rootdir/init.rc)。它通过解析init.rc脚本来构建出系统的初始形态,解析init.rc会把一条条命令映射到内存中,然后依次启动。init.rc里面的顺序大致顺序如下:on early-init -> init -> late-init -> boot。
其中,init.rc会执行Zygote相关的启动脚本。所以说,Zygote进程是Linux系统的init进程通过解析配置脚本来进行启动的。脚本位置:/system/core/rootdir/init.zygote64.rc (64位)。
Zygote进程
通常,子进程被fork出来后,会继续执行系统调用exec,exec将用一个新的可执行文件的内容替换当前进程的代码段、数据段、堆和栈段。Fork加exec 是Linux启动应用的标准做法,init进程也是这样来启动的各种服务的。
Zygote创建应用程序时却只使用了fork,没有调用exec。Zygote初始化时会创建创建虚拟机,同时把需要的系统类库和资源文件加载到内存里面。Zygote fork出子进程后,这个子进程也继承了能正常工作的虚拟机和各类系统资源,接下来子进程只需要装载APK文件的字节码文件就可以运行了。这样应用程序的启动时间就会大大缩短。
init.rc脚本
我们来看init.rc脚本是如何启动Zygote进程的:
import /init.${ro.zygote}.rc //这里通过ro.zygote属性来控制启动不同版本的Zyogte进程。例如,ro.zygote=zygote64_32(zygote32/zygote32_64/zygote64)……on late-init # Now we can start zygote for devices with file based encryption trigger zygote-start ……# It is recommended to put unnecessary data/ initialization from post-fs-data# to start-zygote in device's init.rc to unblock zygote start.on zygote-start && property:ro.crypto.state=unencrypted # A/B update verifier that marks a successful boot. exec_start update_verifier_nonencrypted start netd start zygote start zygote_secondaryon zygote-start && property:ro.crypto.state=unsupported # A/B update verifier that marks a successful boot. exec_start update_verifier_nonencrypted start netd start zygote start zygote_secondaryon zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file # A/B update verifier that marks a successful boot. exec_start update_verifier_nonencrypted start netd start zygote start zygote_secondary……
我们可以看到,Zygote进程是通过init.rc脚本,在init进程中启动的(以service的方式启动),详细过程后面会讲到。
另外,在init.rc中,我们可以看到anr文件的权限设置:
mkdir /data/anr 0775 system system
这里可以看到其实anr文件的目录其实对我们正常应用的APP是开放了rx权限(读、执行权限),但是非系统应用在Android 5.0之后就读取不到anr日志了,这个原因是因为在Android5.0之后,Android默认开启了SELinux权限控制。
ro.zygote变量
我们看到,脚本文件使用了另一个.rc文件,文件名称使用了ro.zygote变量,它的值有四种:
-
zygote32
-
zygote32_64
-
zygote64
-
zygote64_32
分别对应了4个.rc文件(在init.rc同目录下):
-
init.zygote32
-
init.zygote64
-
init.zygote32_64
-
init.zygote64_32
为什么会有4个Zygote脚本文件呢?
这其实代表了Andorid系统支持4种运行模式:
-
纯32位模式:属性ro.zygote的值为zygote32
-
混32位模式(即32位为主,64位为辅)模式:属性ro.zygote的值为zygote32_64
-
纯64位模式:属性ro.zygote的值为zygote64
-
混64位模式(即 64位为主,32位为辅)模式:属性ro.zygote值为zygote64_32
Zygote进程的脚本文件
Zygote对应的配置脚本文件,描述了init该如何启动Zygote进程。
我们以64位CPU架构的脚本文件init.zygote64.rc作为示例:
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server class main priority -20 user root group root readproc socket zygote stream 660 root system onrestart write /sys/android_power/request_state wake onrestart write /sys/power/state on onrestart restart audioserver onrestart restart cameraserver onrestart restart media onrestart restart netd onrestart restart wificond writepid /dev/cpuset/foreground/tasks
它使用的是Android init配置脚本的语法,我们重点关注第一行内容:
-
service zygote:它告诉init进程,我们现在要配置一个名为zygote的服务(这里的服务是init内部的概念)。
-
/system/bin/app_process64:指明zygote服务对应的二进制文件的路径。init进程会fork一个子进程来运行指定的程序。对应zygote而言,这个程序就是/system/bin/app_process。
-
-Xzygote /system/bin --zygote --start-system-server:传递给app_process的启动参数。
当init进程真的启动zygote服务的时候,就会走到service_start()函数(init.cpp)。
下面我们来看Zygote真正的启动代码。
Zygote进程启动过程
Zygote进程的启动,涉及到了init.cpp、service.cpp、app_main.cpp以及脚本文件init.rc、init.zygote64.rc等文件。详细过程如图:
启动过程中的相关类及类的功能如下:
init.cpp
-
初始化相关配置。
-
通过handle_control_message()监听消息,执行Zygote服务。
-
调用service.cpp的Restart()方法。
service.cpp
-
调用Start()方法。
-
fork Zygote进程。
app_main.cpp
-
Zygote进程的入口函数main()
-
创建AppRuntime对象
-
调用AppRuntime对象的start方法
AndroidRuntime.cpp
-
AppRuntime的start方法实际指向AndroidRuntime的start方法
-
创建了一个JniInvocation的实例,并且调用它的成员函数init来初始化JNI环境
-
执行startVm,创建虚拟机及其对应的JNI接口
-
执行startReg,注册JNI函数
-
执行env.CallStaticVoidMethod调用到Java层,初始化Zygote进程
Zygoteinit.java
-
执行main方法
-
调用ZygoteServier的registerServerSocket(socketName)方法注册Zygote的socket监听接口,用来启动应用程序的消息
-
调用preload()方法装载系统资源,包括系统预加载类、Framework资源和openGL的资源。这样当程序被fork处理后,应用的进程内已经包含了这些系统资源,大大节省了应用的启动时间。
-
调用forkSystemServer()方法启动SystemServer进程
-
调动runSelectLoop方法来监听和处理启动应用的请求
app_main.cpp的main函数
我们来看重点来看下app_main.cpp的main函数:
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));//1、创建AppRuntime对象 // Process command line arguments // ignore argv[0] argc--; argv++; …… bool known_command = false; //****************2、从init.rc传入的参数 为 -Xzygote /system/bin --zygote --start-system-server 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. NOLINTNEXTLINE ALOGV("app_process main add known option '%s'", argv[i]); known_command = false; continue; } for (int j = 0; j < static_cast(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. NOLINTNEXTLINE ALOGV("app_process main add option '%s'", argv[i]); } // Parse runtime arguments. Stop at first unrecognized option. bool zygote = false; bool startSystemServer = false; bool application = false; String8 niceName; String8 className; //****************3、将上面的内容赋给相应的变量 ++i; // Skip unused "parent dir" argument. while (i < argc) { const char* arg = argv[i++]; if (strcmp(arg, "--zygote") == 0) { zygote = true; niceName = ZYGOTE_NICE_NAME;//niceName将被设置为app_process的进程名,32位机为“zygote”,64位机为“zygote64”。 } else if (strcmp(arg, "--start-system-server") == 0) { startSystemServer = true; } else if (strcmp(arg, "--application") == 0) { application = true; } else if (strncmp(arg, "--nice-name=", 12) == 0) { niceName.setTo(arg + 12); } else if (strncmp(arg, "--", 2) != 0) { className.setTo(arg); break; } else { --i; break; } } //*************4、准备启动ZygoteInit类或者RuntimeInit类,这里根据类名是否存在为空,来区别是非zyoget模式和zyoget模式. Vector 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])); } } //5、进程的名称修改 if (!niceName.isEmpty()) { runtime.setArgv0(niceName.string(), true /* setProcName */); } //6、启动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."); }}
主要过程如下:
1、创建AppRuntime对象。
2、处理从init.rc传入的参数 为 -Xzygote /system/bin --zygote --start-system-server,传递给AppRuntime对象。
-
-Xzygote是传递给虚拟机的参数
-
/system/bin 是 parent dir(程序运行目录)
-
--zygote表示以zygote模式启动
3、将上面的内容赋给相应的变量
以-Xzygote /system/bin --zygote --start-system-server为例,结果如下:
-
变量 parentDir 等于/system/bin
-
变量 niceName 等于 zyoget
-
变量 startSystemServer 等于 true
-
变量 zygote 等于 true
4、准备启动ZygoteInit类或者RuntimeInit类,这里根据类名是否存在为空,来区别是非zyoget模式和zyoget模式.
5、如果niceName不为空,则将本进程的名称修改为参数** --nice-name** 指定的字符串。缺省的情况下,niceName 的值为"zygote"或者"zygote64"。其中set_process_name函数用来改变进程的mi9ngcheng。setArgv0函数是用来替换启动参数串中的"app_process"为参数。
6、启动Java类,如果启动参数带有 "--zygote"。则执行ZygoteInit。
分三种情况:
-
zygote 模式,即启动ZygoteInit
-
非zygote 模式,即启动RuntimeInit
-
以上两种均不是
Java层的ZygoteInit的main()方法
我们再来分析下上述过程中的ZygoteInit的main()方法:
public static void main(String argv[]) { ZygoteServer zygoteServer = new ZygoteServer();//1、创建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 restart if (!"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(); //***********2、解析参数 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."); } //3、调用registerZygoteSocket(socketName)方法注册Zygote的socket监听接口,用来启动应用程序的消息 zygoteServer.registerServerSocket(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()); preload(bootTimingsTraceLog);//4、调用preload()方法装载系统资源,包括系统预加载类、Framework资源和openGL的资源。这样当程序被fork处理后,应用的进程内已经包含了这些系统资源,大大节省了应用的启动时间。 EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, SystemClock.uptimeMillis()); bootTimingsTraceLog.traceEnd(); // ZygotePreload } else { Zygote.resetNicePriority(); } // Do an initial gc to clean up after startup bootTimingsTraceLog.traceBegin("PostZygoteInitGC"); gcAndFinalize(); bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC bootTimingsTraceLog.traceEnd(); // ZygoteInit // Disable tracing so that forked processes do not inherit stale tracing tags from // Zygote. Trace.setTracingEnabled(false, 0); // Zygote process unmounts root storage spaces. Zygote.nativeUnmountStorageOnInit(); // Set seccomp policy Seccomp.setPolicy(); ZygoteHooks.stopZygoteNoThreadCreation(); if (startSystemServer) { Runnable r = forkSystemServer(abiList, socketName, zygoteServer);//5、启动SystemServer进程 // {@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. caller = zygoteServer.runSelectLoop(abiList);//6、调动runSelectLoop方法来监听和处理启动应用的请求 } 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(); }}
1、创建ZygoteServer对象。
2、解析参数。
3、调用registerZygoteSocket(socketName)方法注册Zygote的socket监听接口,用来启动应用程序的消息。
为zygote命令注册一个socket连接的服务端socket。init进程会根据这条选项来创建一个"AF_UNIX"socket,并把它的句柄放到环境变量"ANDROID_SOCKET_zygote"中。同理我们也可以这样得到句柄,得到句柄后,new了一个FileDescriptor对象,并通过调用setInt$()方法来设置其值。最后new了LocalServerSocket对象,来创建本地的服务socket,并将其值保存在全局变量sServerSocket中。
4、调用preload()方法预加载资源。
为了加快应用程序的启动,Android把系统公用的Java类和一部分Framework的资源保存在zygote中了,这样就可以保证zygote进程fork子进程的是共享的。
-
preloadClasses():预加载Java类
-
preloadResources():预加资源
-
preloadOpenGL():预加载OpenGL资源
-
preloadSharedLibraries():预计加载共享库
-
preloadTextResources():预加载文本资源
-
WebViewFactory.prepareWebViewInZygote():初始化WebView
5、启动SystemServer进程。
-
为fork准备参数parsedArgs
-
调用Zygote.forkSystemServer()方法来创建system_server
-
调用handleSystemServerProcess()方法执行system_server的剩余工作
6、调动runSelectLoop方法来监听和处理启动应用的请求。
ZygoteInit类的main()方法调用runSelectLoop()方法来监听和处理启动应用的请求。
执行zygote进程的循环。当来一个新的连接请求时,则建立接受并建立连接,并在连接中读取请求的命令。
至此,Init进程以及Zygote进程的启动过程就分析完成了。
更多相关文章
- Unity 与 Android(安卓)互调用
- app与Android以及IOS的交互
- android的开机启动画面过程分析
- ReactNative学习笔记之调用原生模块(进阶)之Callback、Promise使用
- Android定制:修改开机启动画面
- Android小項目之---時間線程應用(附源碼)
- android的DeepLink
- 让JNI告诉你 你的应用为什么被卸载
- android安全问题(四) 抢先开机启动