Android(安卓)9 (P) Zygote进程启动源码分析指南一
Android P Zygote进程启动源码分析指南一
前言
在前面的篇章Android P 核心服务和关键进程启动中我们简要的概括了Android P核心服务和关键进程的启动,这其中就包括我们这个篇章需要重点讲解的zygote的启动。对Android系统有一定了解的童靴应该知道,zygote进程属于Native service进程,它是由我们先前的篇章init进程在解析init.xxx.rc文件中得到的service服务,这些服务即Android的核心Native服务,并且通常这些服务被称为守护进程(dameon)运行于后台,为Android系统的运行守驾护航而操劳着。zygote进程的重要性我想就不必要我在这里大书特书了,zygote被成为Android Java世界的孵化器,是所有 Android java 进程的父进程,是 Java 世界的入口。既然zygote进程这么重要,看来我们必须get她深入了解一番才行啊,要得开干。
由于zygote进程启动干的事情太多了,本篇章只分析如下部分:
- Zygote进程启动流程整体概括
- Zygote 进程从何而来
- zygote创建参数解析
- 创建虚拟机
- 注册JNI函数
注意:本文演示的代码是Android P高通msm8953平台源码。其中涉及的源码路径如下:
frameworks/base/cmds/app_process/app_main.cppframeworks/base/include/android_runtime/AndroidRuntime.hframeworks/base/core/jni/AndroidRuntime.cppframeworks/base//core/java/com/android/internal/os/ZygoteInit.javaframeworks//base/core/jni/com_android_internal_os_Zygote.cppsystem/core/rootdir/init.rcsystem/core/rootdir/init.zygoteXX.rc
一. Zygote进程启动流程整体概括
Zygote进程详细分解下来东西比较多,同时也为了本篇文章有一个清晰的知识脉络我们先奉上Zygote进程的启动整体流程图,这样对于读者可以首先站在上帝视角审视然后详细品读。
从上面的流程图可以看到Zygote进程主要干了如下几件事情,可不要小瞧了这几件事件,正是这几件事情撑起了Android Java世界的一片天:
- startVm创建vm虚拟机
- startReg注册JNI函数
- registerServerSocketFromEnv注册zygote通信通道
- preload预加载类和资源
- forkSystemServer创建system_server
在接下的章节我们以上述为目录,进行Zygote进程启动的讲解。
二. Zygote 进程从何而来
Zygote进程出身名门,当然不是从天上掉下来的,更加不是从石头缝里蹦出来的,而是有国际通用出生证的。让我们追查一番出生证明,看看zygote进程的前世今日如何。
2.1 加载init.zygoteXX.rc
我们知道在Android 7及以后对于Android Native service会有一个单独的对应的rc配置文件,当然对于zygote进程也不例外,可是我们按照国际惯例在其源码目录下frameworks/base/cmds/app_process下面没有找到:
[email protected]-YFErbu-01:~/ssd/qcom_64/msm8953-9/frameworks/base/cmds/app_process$ lsAndroid.mk app_main.cpp MODULE_LICENSE_APACHE2 NOTICE
Zygote进程是个特例,它对应的rc文件在system/core/rootdir/下面,是的这个下面是有不少zygote的rc文件,还不少,有蛮多的如下:
[email protected]:~/ssd/qcom_64/msm8953-9/system/core/rootdir$ ls | grep init.zygoteinit.zygote32_64.rcinit.zygote32.rcinit.zygote64_32.rcinit.zygote64.rc
那么zygote的rc文件是怎么加载进入init进程然后解析成service的呢,这个我们在init.rc有如下的import加载流程,Android正是通过ro.zygote来判断是加载那个zygote的rc文件的。
import /init.${ro.zygote}.rc
当前我用来分析的终端的ro.zygote属性的值的配置如下,即我Android终端加载的是init.zygote64_32.rc配置。
msm8953_64:/ # getprop | grep ro.zygote[ro.zygote]: [zygote64_32]
而我另外的终端的情况如下,为什么会有不同的配置情况呢,这个我们下面来聊聊。
A77:/ # getprop ro.zygotezygote32A77:/ #
2.1.1 init.zygoteXX.rc文件解读
通过前面的分析指导,我们知道在Android源码下存在四种init.zygoteXX.rc文件,那么这四个rc文件的功能是什么,以及什么时候使用呢?这个我想是大伙关心的。这几个rc文件有如下不同:
- init.zygote32.rc:zygote 进程对应的执行程序是 app_process (纯 32bit 模式),这个在Android 5版本以前基本是这个模式
- init.zygote64.rc:zygote 进程对应的执行程序是 app_process64 (纯 64bit 模式),这个现阶段比较少,很少有Android终端完全运行64位的,但是看最近谷歌的策略好像有强制对Android高版本必须64位运行的要求
- init.zygote32_64.rc:启动两个 zygote 进程 (名为 zygote 和 zygote_secondary),对应的执行程序分别是 app_process32 (主模式)、app_process64
- init.zygote64_32.rc:启动两个 zygote 进程 (名为 zygote 和 zygote_secondary),对应的执行程序分别是 app_process64 (主模式)、app_process32,这种情况比较常见
2.1.2 init.zygoteXX.rc存在多种配置的原因
说了这么多,童靴们有思考过一个问题没有为啥会要四个rc文件呢,直接使用一个不就有了吗。这个吗,当然不行!原因主要有如下几个方面:
- 因为随着硬件和科技的发展,以及Android版本的迭代,同时谷歌也为了追上苹果的用户体验而推出了64的Android版本,但是不是所有的Android终端都是高配版本,也不是所有的App都已经做好了适配64的准备,这就导致了Android必须兼容各种模式
- 同时Android设备厂商,也有旗舰机型和屌丝机型,这种针对不同机型导致了Android可能运行的位数也不同,必须正确搭配好。
其实不同的zygote.rc内容大致相同,主要区别体现在启动的是32位,还是64位的进程。init.zygote32_64.rc和init.zygote64_32.rc会启动两个进程,且存在主次之分.。我这里演示的机型配置尚可,所以ro.zygote为zygote64_32,这里我们以init.zygote64_32.rc为例来说明:
#service服务的正常操作,在讲解init进程中有详细讲解过service的参数配置,至少要有两个,一个是服务名,一个路径,另外的以这里举例就是启动参数了为-Xzygote /system/bin --zygote --start-system-server --socket-name=zygote#这里有一点需要注意,虽然这里的服务名叫zygote,但不是是运行终端中ps查看的zygoteservice zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote class main priority -20 user root group root readproc reserved_disk socket zygote stream 660 root system #创建一个socket,名字叫zygote,以tcp形式 onrestart write /sys/android_power/request_state wake #onrestart 指当进程重启时执行后面的命令 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 #创建子进程时,向/dev/cpuset/foreground/tasks 写入pid onrestart restart vendor.servicetracker-1-0#创建一个service,次zygote进程名字叫做zygote_secondary ,你会发现在ps中查看不到,因为它在运行中换脸了service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload class main priority -20 user root group root readproc reserved_disk socket zygote_secondary stream 660 root system onrestart restart zygote writepid /dev/cpuset/foreground/tasks
2.1.3 zygote进程在Android终端中的实际运行情况
上面rc配置中的zygote 和zygote_secondary 实际运行终端中的zygote是不匹配的,且在实际运行的终端中你通过ps是根本是看不到zygote_secondary的。
实际Android终端运行的情况如下,纳尼咋回事(zygote进程运行中会改头换面的)?
msm8953_64:/ # ps | grep zygoteroot 757 1 1599284 71092 poll_sched 00edd646d4 S zygoteroot 2901 1 2172472 83324 poll_sched 7f79b88a10 S zygote64
让我们根据进程号PID看看上面两个zygote进程的执行文件是啥:
msm8953_64:/ # cd /proc/757/msm8953_64:/proc/757 # ls -ali | grep exe 5971 lrwxrwxrwx 1 root root 0 2020-06-04 15:38 exe -> /system/bin/app_process32msm8953_64:/proc/757 #msm8953_64:/proc/757 # cd /proc/2901msm8953_64:/proc/2901 # ls -ali | grep exe 36181 lrwxrwxrwx 1 root root 0 2020-06-04 15:39 exe -> /system/bin/app_process64msm8953_64:/proc/2901 #
接着让我们看看上述两个zygote孵化了那些进程,可以看到绝大部分的App进程是由zygote64孵化的,部分的App是由zygote孵化的。
130|msm8953_64:/proc/2901 # ps | grep 757root 757 1 1599284 71092 poll_sched 00edd646d4 S zygotemediacodec 774 1 37576 8104 binder_thr 00f3dc262c S media.codecmsm8953_64:/proc/2901 # ps | grep 2901root 2901 1 2172472 83324 poll_sched 7f79b88a10 S zygote64system 2910 2901 2377004 138172 SyS_epoll_ 7f79b888f0 S system_serveru0_a9 3009 2901 1666376 115352 SyS_epoll_ 7f79b888f0 S com.android.systemuisystem 3133 2901 1639560 65604 SyS_epoll_ 7f79b888f0 S com.android.settingsradio 3142 2901 1632580 73492 SyS_epoll_ 7f79b888f0 S com.android.phoneradio 3194 2901 1592440 45936 SyS_epoll_ 7f79b888f0 S com.qualcomm.qcrilmsgtunnelu0_a1 3313 2901 1592136 49968 SyS_epoll_ 7f79b888f0 S android.ext.servicessystem 3380 2901 1589472 44184 SyS_epoll_ 7f79b888f0 S com.zebra.sdlsystem 3394 2901 1611480 63872 SyS_epoll_ 7f79b888f0 S .dataservicesu0_a25 3466 2901 1595844 46100 SyS_epoll_ 7f79b888f0 S com.oma.drm.serveru0_a2 3507 2901 1598564 58116 SyS_epoll_ 7f79b888f0 S android.process.mediau0_a22 3529 2901 1683368 85564 SyS_epoll_ 7f79b888f0 S com.android.launcher3system 3548 2901 1588116 44560 SyS_epoll_ 7f79b888f0 S com.android.keychainu0_a30 3572 2901 1590164 46244 SyS_epoll_ 7f79b888f0 S com.android.printspoolersystem 3603 2901 1593092 46708 SyS_epoll_ 7f79b888f0 S com.qualcomm.location.XTsystem 3624 2901 1588456 43980 SyS_epoll_ 7f79b888f0 S com.meig.logkitsystem 3672 2901 1589112 45212 SyS_epoll_ 7f79b888f0 S com.qualcomm.displayu0_a5 3717 2901 1590032 49944 SyS_epoll_ 7f79b888f0 S com.android.providers.calendarsystem 3743 2901 1588376 43300 SyS_epoll_ 7f79b888f0 S com.qualcomm.telephonysystem 3756 2901 1588552 45112 SyS_epoll_ 7f79b888f0 S com.qualcomm.qti.RIDLsystem 3779 2901 1588780 45848 SyS_epoll_ 7f79b888f0 S org.simalliance.openmobileapi.servicesystem 3800 2901 1590396 47156 SyS_epoll_ 7f79b888f0 S org.simalliance.openmobileapi.service:remotesystem 3879 2901 1587816 43668 SyS_epoll_ 7f79b888f0 S com.qualcomm.timeserviceu0_a27 4480 2901 1599076 49052 SyS_epoll_ 7f79b888f0 S com.android.inputmethod.latinu0_a2 4935 2901 1590416 48280 SyS_epoll_ 7f79b888f0 S com.android.mtpmsm8953_64:/proc/2901 #
三. Zygote 进程的启动
在前面的章节里面,我们解决了zygote进程的出身问题,那么在这个章节我们将要解决zygote进程何时启动的问题。
3.1 Zygote进程启动触发流程
在前面的篇章Android 9之init进程启动源码分析指南之三我们讲解了Trigger触发顺序的触发顺序,我们知道在init进程启动的第三阶段会在最后调用如下的逻辑代码添加触发逻辑如下:
std::string bootmode = GetProperty("ro.bootmode", ""); if (bootmode == "charger") { am.QueueEventTrigger("charger"); } else { am.QueueEventTrigger("late-init"); }
并且我这里演示的Android终端模式是没有开启加密模式的,所以ro.crypto.state的值为unencrypted,所以综上所述会在on late-init 中触发的。
import /init.${ro.zygote}.rc...# 挂载文件系统并启动核心系统服务on late-init ... # 调用zygote-start trigger zygote-start ... trigger early-boot trigger boot# 在init.rc中调用zygote-start解除zygote的启动阻塞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 zygoteon 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_secondary
3.2 Zygote正式启动
经过如上的步骤,最终调用命令start启动了zygote的两个进程形式,在前面Android 9之init进程启动源码分析指南之三我们知道了start对应的命令如下所示,其代码路径在system/core/init/builtins.cpp中。
{"start", {1, 1, {false, do_start}}},static Result<Success> do_start(const BuiltinArguments& args) { Service* svc = ServiceList::GetInstance().FindService(args[1]); if (!svc) return Error() << "service " << args[1] << " not found"; if (auto result = svc->Start(); !result) { return Error() << "Could not start service: " << result.error(); } return Success();}
这个代码也很简单就是ServiceList查找前面在init进程中前面已经解析好的service section列表,然后将之启动。
zygote和zygote_secondary的源码路径在frameworks/base/cmds/app_process中,通过Android.mk我们发现这两个进程的源文件是同一个,只是通过Android.mk生成了不同的执行文件而已,这种做饭在Android中非常常见,譬如adb啊,同一份代码可以编译出windows版本和linux版本的出来。这个不细说了。
app_process_src_files := \ app_main.cpp \LOCAL_MODULE:= app_processLOCAL_MULTILIB := bothLOCAL_MODULE_STEM_32 := app_process32LOCAL_MODULE_STEM_64 := app_process64
四. Zygote进程main函数分析
在前面的章节中中我们了解的zygote的出身何处,以及如何发家的。在这个章节中我们将要分析zygote进程的main函数,其源码路径为frameworks/base/cmds/app_process/app_main.cpp。
app_main.cpp中的main函数做的工作不是很多,主要就是解析传递进来的参数,然后根据解析得到的参数启动不同的模式,这里的启动模式分为如下两种:
-
––zygote模式,这个就是我们今天要讲解的模式了,即初始化zygote进程模式,其中传递的参数为-Xzygote /system/bin --zygote --start-system-server --socket-name=zygote,–start-system-server表示启动的是SystemServier,–socket-name=zygote表示指定socket名称。
-
--application模式,这个就是我们通常所说的Zygote孵化应用程序模式,传递的参数有class名字以及class带的参数
-
最好通过解析出来的参数来决定,是调用AppRuntime的start函数启动ZygoteInit还是RuntimeInit,我们这里分析的是zygote进程启动流程,所以根据传递进来的参数走的是ZygoteInit这个分支
4.1 解析zygote参数
int main(int argc, char* const argv[]){ //这里传入的参数是-Xzygote /system/bin --zygote --start-system-server --socket-name=zygote std::string bootmode = GetProperty("ro.bootmode", ""); if ((strncmp(bootmode.c_str(), "ffbm-00", 7) == 0) || (strncmp(bootmode.c_str(), "ffbm-01", 7) == 0)) { return 0; }//这个主要是为了调试使用,打印传入的参数信息 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()); } //zygote启动打印出来的日志如下 //app_process main with argv: "/system/bin/app_process64" "-Xzygote" "/system/bin" "--zygote" "--start-system-server" "--socket-name=zygote"//以传入的参数构建构建AppRuntime对象 AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv)); // Process command line arguments // ignore argv[0] argc--; argv++; // Everything up to '--' or first non '-' arg goes to the vm. // // The first argument after the VM args is the "parent dir", which // is currently unused. // // After the parent dir, we expect one or more the following internal // arguments : // // --zygote : Start in zygote mode // --start-system-server : Start the system server. // --application : Start in application (stand alone, non zygote) mode. // --nice-name : The nice name for this process. // // For non zygote starts, these arguments will be followed by // the main class name. All remaining arguments are passed to // the main method of this class. // // For zygote starts, all remaining arguments are passed to the zygote. // main function. // // Note that we must copy argument string values since we will rewrite the // entire argument block when we apply the nice name to argv0. // // As an exception to the above rule, anything in "spaced commands" // goes to the vm even though it has a space in it. /* *下面让我用我蹩脚的中文,错了蹩脚的英文来大概描述下上面语句的意思: *就是将--后面的非-开头的参数都传入vm,后面可以看到以-开头的会传入runtime了 */ const char* spaced_commands[] = { "-cp", "-classpath" };//这两个参数是Java程序需要依赖的Jar包,相当于import // 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]));//将spaced_commands中的参数额外加入AppRuntime // 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<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0])); ++j) { if (strcmp(argv[i], spaced_commands[j]) == 0) {//比较参数是否是spaced_commands中的参数 known_command = true; ALOGV("app_process main found known command '%s'", argv[i]); } } if (argv[i][0] != '-') {//如果参数的第一个字母不是''则跳出循环,这里的参数是-Xzygote所以满足条件, 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]); //打印出来的日志如下 //app_process main add option '-Xzygote' } // Parse runtime arguments. Stop at first unrecognized option. bool zygote = false; bool startSystemServer = false; bool application = false; String8 niceName; String8 className; ++i; // Skip unused "parent dir" argument. //前面已经消耗掉了一个参数-Xzygote,所以接着解析其余参数,这里++i,跳过了第二参数/system/bin while (i < argc) { const char* arg = argv[i++]; if (strcmp(arg, "--zygote") == 0) {//第三个参数为--zygote符合zygote启动模式 zygote = true; niceName = ZYGOTE_NICE_NAME;//这个即我在前面讲解的,zygote进程在运行过程中换脸切换名称的别名 } else if (strcmp(arg, "--start-system-server") == 0) {//需要启动SystemServer startSystemServer = true; } else if (strcmp(arg, "--application") == 0) {//表示是application启动模式,也就是普通应用程序 application = true; } else if (strncmp(arg, "--nice-name=", 12) == 0) {//进程别名 niceName.setTo(arg + 12); } else if (strncmp(arg, "--", 2) != 0) {//application启动的class className.setTo(arg); break; } else { --i; break; } } Vector<String8> args; if (!className.isEmpty()) {//判断className是否为空 // 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);//将className和参数设置给runtime 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 {//zygote启动模式,即我们这个篇章分析的 // We're in zygote mode. maybeCreateDalvikCache();//新建Dalvik的缓存目录 if (startSystemServer) { args.add(String8("start-system-server"));//加入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=");//加入--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])); } } if (!niceName.isEmpty()) {//设置进程别名,这个就是在前面所说的app_process后面为啥叫做zygote的由来 runtime.setArgv0(niceName.string(), true /* setProcName */); } if (zygote) {//如果是zygote启动模式,就调用ZygoteInit runtime.start("com.android.internal.os.ZygoteInit", args, zygote); } else if (className) {//如果是application启动模式,则加载RuntimeInit 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_main.cpp的main分析到这里就告一段落了,接下来我们将要继续分析的就是AppRuntime 的start的启动流程,如果给位看得有点累了可以喝喝茶,上上测试未完待续。
五. AndroidRuntime::start
看到这个章节的标题,细心的读者会要问了不是分析AppRuntime 的start的流程吗,咋现在分析的是AndroidRuntime::start呢?真不是我在瞎忽悠大伙儿,大伙细看代码我们会发现AppRuntime 并没有start的实现体,而AppRuntime 又继承了AndroidRuntime类,所以儿子欠了钱找不到儿子只有找老子了。
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote){...... /* start the virtual machine */ JniInvocation jni_invocation; jni_invocation.Init(NULL);//初始化JNI,加载libart.so if (startVm(&mJavaVM, &env, zygote) != 0) {//创建VM虚拟机 return; } if (startReg(env) < 0) {//注册系统JNI ALOGE("Unable to register all android natives\n"); return; } 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 { jmethodID startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V");//通过反射调用ZygoteInit的main方法 if (startMeth == NULL) { ALOGE("JavaVM unable to find main() in '%s'\n", className); /* keep going */ } else { env->CallStaticVoidMethod(startClass, startMeth, strArray);#if 0 if (env->ExceptionCheck()) threadExitUncaughtException(env);#endif } }......}
通过对start代码的主干简要分析我们可知start函数主要干了如下几个方面的工作:
-
调用JniInvocation.Init初始化JNI
-
创建虚拟机,虽然只有简简单的几个字但是其中涉及的内容非常多,这个后面专门章节细述
-
注册系统JNI函数
-
通过JNI调用ZygoteInit类的main函数
5.1 JniInvocation.Init
JniInvocation.Init的代码比较少,所以放在第五章讲解,其它的单独成章,该该代码定义在frameworks/base/core/jni/AndroidRuntime.cpp中,startVM该函数中主要是初始化 VM 的参数,然后接着调用JNI_CreateJavaVM创建 VM。下面我们详细分析一番。该代码定义在libnativehelper/JniInvocation.cppInit函数主要作用是初始化JNI,具体工作是首先通过dlopen加载libart.so获得其句柄,然后调用dlsym从libart.so中找到JNI_GetDefaultJavaVMInitArgs、JNI_CreateJavaVM、JNI_GetCreatedJavaVMs三个函数地址,赋值给对应成员属性,这三个函数会在后续虚拟机创建中调用。
bool JniInvocation::Init(const char* library) {#ifdef __ANDROID__ char buffer[PROP_VALUE_MAX];#else char* buffer = NULL;#endif library = GetLibrary(library, buffer);//默认返回 libart.so // Load with RTLD_NODELETE in order to ensure that libart.so is not unmapped when it is closed. // This is due to the fact that it is possible that some threads might have yet to finish // exiting even after JNI_DeleteJavaVM returns, which can lead to segfaults if the library is // unloaded. const int kDlopenFlags = RTLD_NOW | RTLD_NODELETE; /* * 1.dlopen功能是以指定模式打开指定的动态链接库文件,并返回一个句柄 * 2.RTLD_NOW表示需要在dlopen返回前,解析出所有未定义符号,如果解析不出来,在dlopen会返回NULL * 3.RTLD_NODELETE表示在dlclose()期间不卸载库,并且在以后使用dlopen()重新加载库时不初始化库中的静态变量 */ handle_ = dlopen(library, kDlopenFlags); // 获取libart.so的句柄 if (handle_ == NULL) { //获取失败打印错误日志并尝试再次打开libart.so if (strcmp(library, kLibraryFallback) == 0) { // Nothing else to try. ALOGE("Failed to dlopen %s: %s", library, dlerror()); return false; } // Note that this is enough to get something like the zygote // running, we can't property_set here to fix this for the future // because we are root and not the system user. See // RuntimeInit.commonInit for where we fix up the property to // avoid future fallbacks. http://b/11463182 ALOGW("Falling back from %s to %s after dlopen error: %s", library, kLibraryFallback, dlerror()); library = kLibraryFallback; handle_ = dlopen(library, kDlopenFlags); if (handle_ == NULL) { ALOGE("Failed to dlopen %s: %s", library, dlerror()); return false; } } /* * 1.FindSymbol函数内部实际调用的是dlsym * 2.dlsym作用是根据 动态链接库 操作句柄(handle)与符号(symbol),返回符号对应的地址 * 3.这里实际就是从libart.so中将JNI_GetDefaultJavaVMInitArgs等对应的地址存入&JNI_GetDefaultJavaVMInitArgs_中 */ if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_), "JNI_GetDefaultJavaVMInitArgs")) { return false; } if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_), "JNI_CreateJavaVM")) { return false; } if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_), "JNI_GetCreatedJavaVMs")) { return false; } return true;}
这里大伙有没有一个疑问为啥要大费周章的通过一些列的dlopen查找函数,而不是直接引用libart的库。我想这这样是为了更好的扩展,厂商可以自行实现libart中类似的函数,有点HAL的味道。
六. AndroidRuntime::startVm创建虚拟机
该代码定义在frameworks/base/core/jni/AndroidRuntime.cpp中,startVM该函数中主要是初始化 VM 的参数,然后接着调用JNI_CreateJavaVM创建 VM。下面让我们详细分析之。
6.1 创建VM的涉及的参数
对于没有分析过这段代码经验的童靴来说,一看到这个代码估计懵了,卧槽这么多参数需要解析的,其实我们如果不是专门做系统调优或者非常底层的可以不需要太过于关注,其实就是从各种系统属性中读取一些参数,然后通过addOption设置到AndroidRuntime的mOptions数组中存起来,另外就是调用之前从libart.so中找到JNI_CreateJavaVM函数,并将这些参数传入,下面就是解析完参数以后打印出来的相关的具体值,看开vm创建的参数是有点多啊,老实说这些参数我不是很懂,对于虚拟机有兴趣的可以深入研究研究。
01-01 10:44:02.938 717 717 I zygote64: option[0]=-Xzygote01-01 10:44:02.939 717 717 I zygote64: option[1]=-Xusetombstonedtraces01-01 10:44:02.939 717 717 I zygote64: option[2]=exit01-01 10:44:02.939 717 717 I zygote64: option[3]=vfprintf01-01 10:44:02.939 717 717 I zygote64: option[4]=sensitiveThread01-01 10:44:02.939 717 717 I zygote64: option[5]=-verbose:gc01-01 10:44:02.939 717 717 I zygote64: option[6]=-Xms16m01-01 10:44:02.939 717 717 I zygote64: option[7]=-Xmx512m01-01 10:44:02.939 717 717 I zygote64: option[8]=-XX:HeapGrowthLimit=192m01-01 10:44:02.939 717 717 I zygote64: option[9]=-XX:HeapMinFree=4m01-01 10:44:02.939 717 717 I zygote64: option[10]=-XX:HeapMaxFree=8m01-01 10:44:02.939 717 717 I zygote64: option[11]=-XX:HeapTargetUtilization=0.7501-01 10:44:02.939 717 717 I zygote64: option[12]=-Xusejit:true01-01 10:44:02.939 717 717 I zygote64: option[13]=-Xjitsaveprofilinginfo01-01 10:44:02.939 717 717 I zygote64: option[14]=-XjdwpOptions:suspend=n,server=y01-01 10:44:02.939 717 717 I zygote64: option[15]=-XjdwpProvider:default01-01 10:44:02.939 717 717 I zygote64: option[16]=-Xlockprofthreshold:50001-01 10:44:02.939 717 717 I zygote64: option[17]=-Ximage-compiler-option01-01 10:44:02.939 717 717 I zygote64: option[18]=--runtime-arg01-01 10:44:02.939 717 717 I zygote64: option[19]=-Ximage-compiler-option01-01 10:44:02.939 717 717 I zygote64: option[20]=-Xms64m01-01 10:44:02.939 717 717 I zygote64: option[21]=-Ximage-compiler-option01-01 10:44:02.939 717 717 I zygote64: option[22]=--runtime-arg01-01 10:44:02.939 717 717 I zygote64: option[23]=-Ximage-compiler-option01-01 10:44:02.939 717 717 I zygote64: option[24]=-Xmx64m01-01 10:44:02.939 717 717 I zygote64: option[25]=-Ximage-compiler-option01-01 10:44:02.939 717 717 I zygote64: option[26]=--profile-file=/system/etc/boot-image.prof01-01 10:44:02.939 717 717 I zygote64: option[27]=-Ximage-compiler-option01-01 10:44:02.939 717 717 I zygote64: option[28]=--compiler-filter=speed-profile01-01 10:44:02.939 717 717 I zygote64: option[29]=-Xcompiler-option01-01 10:44:02.939 717 717 I zygote64: option[30]=--runtime-arg01-01 10:44:02.939 717 717 I zygote64: option[31]=-Xcompiler-option01-01 10:44:02.939 717 717 I zygote64: option[32]=-Xms64m01-01 10:44:02.939 717 717 I zygote64: option[33]=-Xcompiler-option01-01 10:44:02.939 717 717 I zygote64: option[34]=--runtime-arg01-01 10:44:02.939 717 717 I zygote64: option[35]=-Xcompiler-option01-01 10:44:02.939 717 717 I zygote64: option[36]=-Xmx512m01-01 10:44:02.939 717 717 I zygote64: option[37]=-Ximage-compiler-option01-01 10:44:02.939 717 717 I zygote64: option[38]=--instruction-set-variant=generic01-01 10:44:02.939 717 717 I zygote64: option[39]=-Xcompiler-option01-01 10:44:02.939 717 717 I zygote64: option[40]=--instruction-set-variant=generic01-01 10:44:02.939 717 717 I zygote64: option[41]=-Ximage-compiler-option01-01 10:44:02.939 717 717 I zygote64: option[42]=--instruction-set-features=default01-01 10:44:02.939 717 717 I zygote64: option[43]=-Xcompiler-option01-01 10:44:02.939 717 717 I zygote64: option[44]=--instruction-set-features=default01-01 10:44:02.939 717 717 I zygote64: option[45]=-Duser.locale=zh-CN01-01 10:44:02.940 717 717 I zygote64: option[46]=--cpu-abilist=arm64-v8a01-01 10:44:02.940 717 717 I zygote64: option[47]=-Xfingerprint:qti/msm8953_64/msm8953_64:9/PKQ1.190504.001/Data.BU06081049:userdebug/dev-keys
6.2 startVm代码简要分析
在前面的章节我们主要讲了startVm主要是解析vm参数,然后将相关参数收集保存最最好通过JNI_CreateJavaVM创建虚拟机。下面分析一下:
void AndroidRuntime::addOption(const char* optionString, void* extraInfo){ JavaVMOption opt; opt.optionString = optionString; opt.extraInfo = extraInfo; mOptions.add(opt);}int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote){ JavaVMInitArgs initArgs; ... std::string fingerprint = GetProperty("ro.build.fingerprint", ""); if (!fingerprint.empty()) { fingerprintBuf = "-Xfingerprint:" + fingerprint; addOption(fingerprintBuf.c_str());//将各种参数加入mOptions列表中 } ... initArgs.version = JNI_VERSION_1_4; initArgs.options = mOptions.editArray();//将mOptions赋值给initArgs initArgs.nOptions = mOptions.size(); initArgs.ignoreUnrecognized = JNI_FALSE; if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {//调用前面6.1章节里面获取的libart.so的JNI_CreateJavaVM函数 ALOGE("JNI_CreateJavaVM failed\n"); return -1; } return 0;}JniInvocation& JniInvocation::GetJniInvocation() { LOG_ALWAYS_FATAL_IF(jni_invocation_ == NULL, "Failed to create JniInvocation instance before using JNI invocation API"); return *jni_invocation_;}extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { return JniInvocation::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args);//通过前面获取的相关vm参数创建vm}jint JniInvocation::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { return JNI_CreateJavaVM_(p_vm, p_env, vm_args);//调用之前初始化的JNI_CreateJavaVM_}
6.3 startVm参数配置注意点
随着Android版本的升级和现在硬件水平的提高,现在主流的Android终端配置基本都是6+128的相关配置了。但是随之而来的就是现在的App越来越吃内存了,要重点关注其中 dalvik heapsize 的初始化,如果没有配置正确的参数或者使用默认的参数很有可能导致手机无法进入 Launcher。因为目前APP 占用的 heapsize 都比较大,使用默认参数很容易出现 OOM 导致应用不断重启。
parseRuntimeOption("dalvik.vm.heapstartsize", heapstartsizeOptsBuf, "-Xms", "4m"); parseRuntimeOption("dalvik.vm.heapsize", heapsizeOptsBuf, "-Xmx", "16m"); parseRuntimeOption("dalvik.vm.heapgrowthlimit", heapgrowthlimitOptsBuf, "-XX:HeapGrowthLimit="); parseRuntimeOption("dalvik.vm.heapminfree", heapminfreeOptsBuf, "-XX:HeapMinFree="); parseRuntimeOption("dalvik.vm.heapmaxfree", heapmaxfreeOptsBuf, "-XX:HeapMaxFree="); parseRuntimeOption("dalvik.vm.heaptargetutilization", heaptargetutilizationOptsBuf, "-XX:HeapTargetUtilization=");
这个值要根据具体的Android终端硬件配置决定(以本人现在从事的行业来说,是特殊的Android终端平台2+16都算是高配了,你懂的),我们的终端相关的值如下:
msm8953_64:/ # getprop | grep dalvik.vm.heap[dalvik.vm.heapgrowthlimit]: [192m][dalvik.vm.heapmaxfree]: [8m][dalvik.vm.heapminfree]: [4m][dalvik.vm.heapsize]: [512m][dalvik.vm.heapstartsize]: [16m][dalvik.vm.heaptargetutilization]: [0.75]msm8953_64:/ #
七. AndroidRuntime::startReg
startReg该函数主要有如下几个方面:
- 调用androidSetCreateThreadFunc设置Android创建线程的处理函数
- 然后创建了一个200容量的局部引用作用域,确保不会出现局部引用不会溢出,通常PushLocalFrame 和 PopLocalFrame 是配套使用,它们可以为局部引用创建一个指定数量内嵌的空间,在这个函数对之间的局部引用都会在这个空间内,直到释放后,所有的局部引用都会被释放掉,不用再担心每一个局部引用的释放问题了,可以参见Android JNI 中的引用管理
- register_jni_procs作用是注册 JNI 函数,遍历 gRegJNI 数组中 JNI register 方法注册 JNI method。注册JNI 方法后,会通过 JNI 调用 java class(zygoteInit)的 main 函数进入 java 世界。
/* * Register android native functions with the VM. *//*static*/ int AndroidRuntime::startReg(JNIEnv* env){ ATRACE_NAME("RegisterAndroidNatives"); /* * This hook causes all future threads created in this process to be * attached to the JavaVM. (This needs to go away in favor of JNI * Attach calls.) */ //设置Android创建线程的函数javaCreateThreadEtc,这个函数内部是通过Linux的clone来创建线程的 androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc); ALOGV("--- registering native functions ---\n"); /* * Every "register" function calls one or more things that return * a local reference (e.g. FindClass). Because we haven't really * started the VM yet, they're all getting stored in the base frame * and never released. Use Push/Pop to manage the storage. */ env->PushLocalFrame(200);//创建一个200容量的局部引用作用域,这个局部引用其实就是局部变量 if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {//注册JNI函数 env->PopLocalFrame(NULL); return -1; } env->PopLocalFrame(NULL);//和PushLocalFrame配套使用,释放局部引用作用域 //createJavaThread("fubar", quickTest, (void*) "hello"); return 0;}
7.1 androidSetCreateThreadFunc
该函数定义在system/core//libutils/Threads.cpp中,设置设置线程创建函数指针gCreateThreadFn指向javaCreateThreadEtc.
void androidSetCreateThreadFunc(android_create_thread_fn func){ gCreateThreadFn = func;}
接着我们继续分析javaCreateThreadEtc,该函数定义在frameworks/base/core/jni/AndroidRuntime.cpp之中,这个流程比较多这个不是本篇重点关注的,大伙可以参考理解Android线程创建流程这个博客有非常详细的讲解。
int AndroidRuntime::javaCreateThreadEtc( android_thread_func_t entryFunction, void* userData, const char* threadName, int32_t threadPriority, size_t threadStackSize, android_thread_id_t* threadId){ void** args = (void**) malloc(3 * sizeof(void*)); // javaThreadShell must free int result; LOG_ALWAYS_FATAL_IF(threadName == nullptr, "threadName not provided to javaCreateThreadEtc"); args[0] = (void*) entryFunction; args[1] = userData; args[2] = (void*) strdup(threadName); // javaThreadShell must free result = androidCreateRawThreadEtc(AndroidRuntime::javaThreadShell, args, threadName, threadPriority, threadStackSize, threadId); return result;}
7.2 register_jni_procs
int AndroidRuntime::startReg(JNIEnv* env){...... if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) { env->PopLocalFrame(NULL); return -1; }...... return 0;}static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env){ for (size_t i = 0; i < count; i++) { if (array[i].mProc(env) < 0) {#ifndef NDEBUG ALOGD("----------!!! %s failed to load\n", array[i].mName);#endif return -1; } } return 0;}static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_util_SeempLog), REG_JNI(register_com_android_internal_os_RuntimeInit), REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit), REG_JNI(register_android_os_SystemClock), REG_JNI(register_android_util_EventLog), REG_JNI(register_android_util_Log), REG_JNI(register_android_util_MemoryIntArray), REG_JNI(register_android_util_PathParser), REG_JNI(register_android_util_StatsLog), REG_JNI(register_android_app_admin_SecurityLog), REG_JNI(register_android_content_AssetManager), REG_JNI(register_android_content_StringBlock), REG_JNI(register_android_content_XmlBlock), REG_JNI(register_android_content_res_ApkAssets), REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_Hyphenator), REG_JNI(register_android_text_MeasuredParagraph), REG_JNI(register_android_text_StaticLayout), REG_JNI(register_android_view_InputDevice), REG_JNI(register_android_view_KeyCharacterMap), REG_JNI(register_android_os_Process), REG_JNI(register_android_os_SystemProperties), REG_JNI(register_android_os_Binder), REG_JNI(register_android_os_Parcel), REG_JNI(register_android_os_HidlSupport), REG_JNI(register_android_os_HwBinder), ......}int register_com_android_internal_os_RuntimeInit(JNIEnv* env){ const JNINativeMethod methods[] = { { "nativeFinishInit", "()V", (void*) com_android_internal_os_RuntimeInit_nativeFinishInit }, { "nativeSetExitWithoutCleanup", "(Z)V", (void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup }, }; return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit", methods, NELEM(methods));}
7.2.1 gRegJNI.mProc
前面章节里面的array[i]是指gRegJNI数组, 该数组有100多个成员。其中每一项成员都是通过REG_JNI宏定义的:
static const RegJNIRec gRegJNI[] = { REG_JNI(register_com_android_internal_os_RuntimeInit), REG_JNI(register_android_os_Binder), ...}; #define REG_JNI(name) { name } struct RegJNIRec { int (*mProc)(JNIEnv*); };
可见,调用mProc,就等价于调用其参数名所指向的函数。这里会注册许多的JNI,关于JNI方面的相关知识童靴们可以参考如下的系列篇章JNI/NDK入门指南 。这里我们以REG_JNI(register_com_android_internal_os_RuntimeInit).mProc也就是指进入register_com_android_internal_os_RuntimeInit方法,为啥以这个为例主要是这个的JNI注册比较少,接下来就继续以此为例来说明:
int register_com_android_internal_os_RuntimeInit(JNIEnv* env){ const JNINativeMethod methods[] = { { "nativeFinishInit", "()V", (void*) com_android_internal_os_RuntimeInit_nativeFinishInit }, { "nativeSetExitWithoutCleanup", "(Z)V", (void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup }, }; return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit", methods, NELEM(methods));}
总结
虽然说随着Android版本越高,Android的工作量也是越来越大了,分析起来不得不使出吃奶的力气了。但是zygote启动的流程和原来Android版本的流程还是差不多,只是细节方面有所改动。但是分析起来还是需要花费一些时间的。
写在最后
Android 9 Zygote进程启动源分析指南一就告一段落了,不容易啊分析起来,在接下来的篇章我们将继续讲解Android 9 Zygote进程启动源分析。如果对给位有帮助欢迎点赞一个,如果写得有问题也欢迎多多指正。未完待续,下个篇章再见,下个篇章链接为Android 9 § Zygote进程启动源码分析指南二。
更多相关文章
- Android(安卓)overlay 学习 二 Android(安卓)camera preview and
- Android(安卓)Activity启动流程分析--------基于Android(安卓)O
- Android进阶知识树——Android(安卓)多进程、Binder 你必须知道
- android里的线程和进程
- Android中线程与进程的理解
- android studio for android learning (八)开机启动界面splashAc
- Android(安卓)studio启动后卡在refreshing gradle project(包解决
- 手机管理应用研究【4】—— 手机加速篇
- Android实现深度链接(APP外带动态参数唤醒APP,并跳转指定页面,APP