在[android启动之zygote进程](https://liqi.site/archives/android%E5%90%AF%E5%8A%A8%E4%B9%8Bzygote%E8%BF%9B%E7%A8%8B?token=5a91f7c31ada40a4a24fb567c7b82a23)这篇文章中我们分析了android的init进程的启动流程,那里我们分析了init进程启动开始,解析了init.rc文件,然后执行了其中的命令,最终通过调用service命令来启动了zygote进程。zygote进程,从名字看受精卵的意思,我们后面分析代码就可以看到,后面的进程都是从这个zygote进程fork出来的,这个名字确实是很生动。我们在上一篇文章中分析到zygote进程启动后,会执行到app_mainc.cpp的文件,我们接下去就从app_main.cpp的main方法开始分析。
在之前的文章中,我们有说到调用到app_main.cpp这里是解析了service命令后执行才触发的,具体的命令是这个:
```cpp
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
```
这个命令的执行才会调用到现在app_main.cpp这里,并且会把参数带过去,参数是从第四个开始的即-Xzygote,这里稍稍解释下这里的四个参数:
-Xzygote:这个参数会传给虚拟机来使用,我们这里不用太关心
/system/bin:这个参数是指执行文件的目录,之前介绍init启动的文章介绍过了,app_process这个模块就是在这个路径下的,app_main就是这个模块的源码文件
--zygote:表示是否是zygote进程,由于后续的进程都会从zygote进程中fork出来,所以这个参数表示是否是zygote进程的"真身"
--start-system-server:表示会在进程里面启动systemServer进程,systemServer进程中包含了一系列的系统模块,比如AMS,PMS等,都是运行咋systemServer里面的
#main方法入口
简单介绍了下这几个参数,下面就开始看main方法了:
```cpp
int main(int argc, char* const argv[])
{
if (!LOG_NDEBUG) {
String8 argv_String;
// 遍历下所有参数保存到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 argv[0]即第一个参数,-Xzygote
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// Process command line arguments
// ignore argv[0]
argc--; // 参数数量-1
argv++; // 指向第二个参数
```
由于main的方法比较多,我们分段来看,这里是第一段。首先打印一下参数,然后创建一个AppRuntime对象,把第一个参数即-Xzygote和参数数量传给AppRuntime。AppRuntime这个对象是非常重要的,每个进程都有一个这个实例,他负责了一个程序运行的基础构件,里面启动了java虚拟机以及初始化了一些基础的系统模块,这个在后面的代码中我们可以看到。之后参数会指向第二个,参数总数量会减一,后面代码会用到第二个开始的参数。我们继续看下一段代码:
```cpp
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;
// 如果遇到第一个非"-"开头或者"--",那么就停止想java虚拟机添加参数
for (i = 0; i < argc; i++) {
if (known_command == true) {
runtime.addOption(strdup(argv[i]));
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) {
known_command = true;
ALOGV("app_process main found known command '%s'", argv[i]);
}
}
// 如果开头不是'-',返回
if (argv[i][0] != '-') {
break;
}
// 如果前2个是'--',没有第三个了,返回
if (argv[i][1] == '-' && argv[i][2] == 0) {
++i; // Skip --.
break;
}
// 把这些参数添加到java虚拟机结构体中
runtime.addOption(strdup(argv[i]));
ALOGV("app_process main add option '%s'", argv[i]);
}
```
这段代码会继续向虚拟机中添加参数。这个方法总的逻辑是,往虚拟机中添加命令字符串,如果遇到不是以"-"开头的,或者只有“--”这样就结束的字符串,就break。这里有一个特殊的处理,有两个命令“-cp”和"-classpath",这两个命令由于后面会跟着一个路径,所以这两个命令除了需要把命令本身给添加进虚拟机外,还需要把后面的一个字符串给添加进去。我们用现在的参数来看下这段代码执行后的效果:
```cpp
-Xzygote /system/bin --zygote --start-system-server
```
前面一段代码的分析,现在参数会从第二个开始,所以就是从/system/bin开始,根据现在添加参数的逻辑,第一个字符不是“-”,那么就break了,那么现在其实一个参数都没往虚拟机添加就结束了。好吧,我们继续看后面一段代码:
```cpp
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName;
String8 className;
// -Xzygote /system/bin --zygote --start-system-server是全部参数
++i; // Skip unused "parent dir" argument.
while (i < argc) {
// 前面添加到虚拟机中i指向/system/bin停止了,但是上面+1了
// 这里第一个参数应该是--zygote
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = ZYGOTE_NICE_NAME; // 进程名字
} 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;
}
}
```
这段代码开头有五个变量,可以看出这里就是处理给这5个变量赋值的。
第一个变量zygote表示启动命令中有没有--zygote这个参数,如果有的话说明这个是zygote进程,会把zygote赋值为true。
同时niceName变量赋值为ZYGOTE_NICE_NAME这个字符串,这个字符串的值为zygote或者zygote64,根据系统是32位还是64位来确定。
startSystemServer变量表示命令中有没有带有--start-system-server参数,如果有的话就赋值true,表示后面要启动systemServer进程,我们知道这里启动zygote的命令中是有的。
还有application这个需要命令中有--application参数,这里启动zygote时候没有这个参数,所以是false。
className需要比较命令中前两个字符是不是“--”,如果是的话就不赋值,这里后面比较的参数前两个都是“--”,所以这里className不会赋值。
我们还是把现在的命令来看下这段代码,之前的代码由于没有往虚拟机中添加过参数,所以现在还是参数还是从第二个开始,上面这段代码开始执行了++i,所以参数指向往后移动了一位,剩余还有2个参数即--zygote和--start-system-server,根据上面代码的逻辑,zygote为true,startSystemServer为true,niceName为zygote(我们假设分析的是32位系统),这样命令中的参数就处理完了。我们继续看下一段代码:
```cpp
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 { // zygote模式会进入这里
// We're in zygote mode.
maybeCreateDalvikCache();
if (startSystemServer) { // 启动systemServer标志
args.add(String8("start-system-server"));
}
// ABI属性的特殊处理
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) { // 把剩余的参数添加到atgs
args.add(String8(argv[i]));
}
}
```
这段代码开始会根据前面处理命令中的参数后,className这个是否有值,如果没有值会进入else分支中,由上面的分析我们知道className为空,所以进入else分支。
这里首先调用maybeCreateDalvikCache方法来创建Dalvik虚拟机的目录和设置权限,这里我们了解下,不展开看。之后如果有指定的abi,会添加到参数中。由于目前android的系统主要运行在arm体系的硬件上,但是android运行的应用程序其实也可以支持其他系统结构的应用的,不同体系结构的应用程序,他们的格式是不一样的,所以为了支持不同cpu体系结构的应用程序,比如我们经常会集成一些三方的so文件,这个so文件运行的系统结构和我们系统不一定一样,所以需要把这些so文件放到指定系统结构的目录下才能运行,这里就是添加了一些初始化的abi的参数,用于之后能够正确找到特定体系结构下的文件,这里先把abi参数加入args容器,后面启动zygote入口方法的时候还要把这些参数传过去。
最后之前init进程也有传过来参数,这里同样把之前传过来的参数也加入到args容器中,后面就要准备调用zygote的初始化方法了。我们继续看这个方法的最后一段代码:
```cpp
if (!niceName.isEmpty()) { // 修改进程名
runtime.setArgv0(niceName.string(), true /* setProcName */);
}
if (zygote) { // 启动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.");
}
```
这里首先判断下niceName是否为空,如果不空的话,会把线程名字设置为niceName,根据上面的分析我们知道niceName的值为zygote或者zygote64。接着如果是zygote进程的话,会运行ZygoteInit文件,否则如果className的值非空,会执行RuntimeInit。我们目前是zygote进程,所以会走ZygoteInit执行的分支,后面启动了从zygote进程fork出来的进程后,会执行RuntimeInit,这个我们后面分析systemServer的时候再说,现在先来看ZygoteInit的执行。
# 准备开启java类
这里我们看到会调用AndroidRuntime的start方法,这个方法其实就是会启动一个java的类了,这里启动的就是ZygoteInit,也就是说马上我们要从c++走到java中了,我们看看这个方法是怎么启动一个java类的。
```cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
// 打印log
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) { // 如果有启动systemServer的参数打印
/* 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)));
}
}
// 看看环境变量中有没有设置根目录
const char* rootDir = getenv("ANDROID_ROOT");
if (rootDir == NULL) {
rootDir = "/system"; // 没有的话设置system为根目录
if (!hasDir("/system")) { // 如果system目录不存在,退出
LOG_FATAL("No root directory specified, and /android does not exist.");
return;
}
setenv("ANDROID_ROOT", rootDir, 1); // 把根目录保存在环境变量中
}
```
这个方法有点长,我们分段来分析。首先这里会判断,是否传过来的参数中有start-system-server,根据前面我们的分析,如果是zygote进程的时候,会添加这个参数,所以这里有这个参数会打印一些参数。
之后会检查下是否有设置根目录的环境变量,如果没有设置就会把ANDROID_ROOT设置为/system目录。正常情况下,在init进程中这个已经设置过了。记得之前分析init进程的文章中有说到init.rc这么初始化命令的文件吗,这个文件里面有这么一行:
```cpp
import /init.environ.rc
```
这里会导入init.environ.rc文件,我们看下这个文件:
```cpp
# set up the global environment
on init
export ANDROID_BOOTLOGO 1
export ANDROID_ROOT /system
export ANDROID_ASSETS /system/app
export ANDROID_DATA /data
export ANDROID_STORAGE /storage
export EXTERNAL_STORAGE /sdcard
export ASEC_MOUNTPOINT /mnt/asec
export BOOTCLASSPATH %BOOTCLASSPATH%
export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
%EXPORT_GLOBAL_ASAN_OPTIONS%
%EXPORT_GLOBAL_GCOV_OPTIONS%
```
可以看到这里有许多export命令,其中有一行export ANDROID_ROOT /system,这里export对应的方法是add_environment,这个查找对应方法的过程这里就不细说了,我们在前面的文章中已经分析过了,不太清楚的同学可以看下分析init进程的那篇文章,我们看一下这个方法就是可以:
```cpp
int add_environment(const char *key, const char *val)
{
size_t n;
size_t key_len = strlen(key);
/* The last environment entry is reserved to terminate the list */
for (n = 0; n < (arraysize(ENV) - 1); n++) {
/* Delete any existing entry for this key */
if (ENV[n] != NULL) {
size_t entry_key_len = strcspn(ENV[n], "=");
if ((entry_key_len == key_len) && (strncmp(ENV[n], key, entry_key_len) == 0)) {
free((char*)ENV[n]);
ENV[n] = NULL;
}
}
/* Add entry if a free slot is available */
if (ENV[n] == NULL) {
char* entry;
asprintf(&entry, "%s=%s", key, val);
ENV[n] = entry;
return 0;
}
}
LOG(ERROR) << "No env. room to store: '" << key << "':'" << val << "'";
return -1;
}
```
这个方法我们只要看下最后asprintf(&entry, "%s=%s", key, val),这里的key和val就是ANDROID_ROOT和/system,最后会存入ENV[n]中。所以这里正常情况已经有根目录/system了。我们回到AndroidRuntime的start方法,接着看下一段代码:
```cpp
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
// 这里创建一个虚拟机的实例,即初始化mJavaVM和env变量
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
// 如果有指定要启动的类的话,这个方法里面会创建jclass实例,zygote没有jclass
onVmCreated(env);
/*
* Register android functions.
*/
// 注册一个native方法
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
```
这部分代码主要就开始要初始化java虚拟机以及jni相关的了。我们这里主要跟着代码看下关键的部分,在说这里的方法前,大致先说下Dalvik和ART。我们知道android早期是使用Dalvik虚拟机来执行java程序的,我们的java程序被编译为dex字节码,然后Dalvik来执行dex字节码。之后为了提升android的性能,android4.4之后,就引入了ART,ART和Dalvik最大的区别在于,ART可以直接执行机器码,这样就少了个翻译的过程,自然性能就提升了。
我们常说C或者C++的性能比java要高,之所以我们这样说,主要是由于C/C++在程序被编译后最终执行的时候就是直接执行的机器码,所以不需要像java那样有翻译的过程(缺点当然就是没有像java那样优秀的跨平台能力),那么ART既然也是执行的机器码,那么是否java程序也是在编译时候被编译成机器码了呢?显然不是的,我们平时开发应用的时候还是用的java那一套东西,如果真的也像C系列那样被翻译为机器码了,那么让开发者用java来开发还有什么意义呢,况且从系统方面看,如果从之前的面向java系的执行直接转为执行机器码,其中牵涉到的流程也比较多,同时对开发者影响也比较大,这样整个android的生态可能会有比较大的影响。所以google选择了一种比较平稳的方法来解决这个问题,即在一个程序安装的时候,把程序编译为机器码,这样在安装过程的时间会略长一点,以及安装大小也会略大一点,但是都在可接受的状态,所以目前我们分析的是android 8.0版本,用的是ART来作为虚拟机的。
我们回到AndroidRuntime的start方法,现在调用的是JniInvocation的init方法,我们看下这个方法:
```cpp
bool JniInvocation::Init(const char* library) {
#ifdef __ANDROID__
char buffer[PROP_VALUE_MAX];
#else
char* buffer = NULL;
#endif
library = GetLibrary(library, buffer);
const int kDlopenFlags = RTLD_NOW | RTLD_NODELETE;
handle_ = dlopen(library, kDlopenFlags);
if (handle_ == NULL) {
......................
}
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;
}
```
这个方法我们看到先会调用GetLibrary来读取加载ART库,GetLibrary方法我们稍后看下, 这里ART库是个so文件,这个会在art目录下的mk文件里面配置,之后调用dlopen来获取ART库的链接,如果一切都正常的话,最后会调用FindSymbol方法来把so库中的三个方法给导出到JniInvocation这个类中,这样后面就可以调用这三个ART库中的方法了,FindSymbol这个方法我们也会在后面看下,这里先看下上面的GetLibrary方法。
```cpp
static const char* kLibrarySystemProperty = "persist.sys.dalvik.vm.lib.2";
const char* JniInvocation::GetLibrary(const char* library, char* buffer) {
#ifdef __ANDROID__
const char* default_library;
char debuggable[PROP_VALUE_MAX];
__system_property_get(kDebuggableSystemProperty, debuggable);
if (strcmp(debuggable, "1") != 0) {
.............
} else {
if (buffer != NULL) {
if (__system_property_get(kLibrarySystemProperty, buffer) > 0) {
default_library = buffer;
} else {
default_library = kLibraryFallback;
}
} else {
// No buffer given, just use default fallback.
default_library = kLibraryFallback;
}
}
#else
UNUSED(buffer);
const char* default_library = kLibraryFallback;
#endif
if (library == NULL) {
library = default_library;
}
return library;
}
```
这里可以看到调用__system_property_get方法获取kLibrarySystemProperty这个属性的系统配置,kLibrarySystemProperty是值是persist.sys.dalvik.vm.lib.2,这个就是在art目录的mk文件里面配置的art库的so:
```cpp
use-art:
adb root
adb wait-for-device shell stop
adb shell setprop persist.sys.dalvik.vm.lib.2 libart.so
adb shell start
```
可以看到这里使用的是libart.so,得到了这个so后就会返回去。好了,获取ART的so库就是这个方法,我们回到前面看下另一个方法FindSymbol:
```cpp
bool JniInvocation::FindSymbol(void** pointer, const char* symbol) {
*pointer = dlsym(handle_, symbol);
if (*pointer == NULL) {
ALOGE("Failed to find symbol %s: %s\n", symbol, dlerror());
dlclose(handle_);
handle_ = NULL;
return false;
}
return true;
}
```
前面有分析过,获取了so的文件后,我们需要把里面的方法导出来,这样后面就可以使用了,这里调用了dlsym方法把so库handle_中的方法symbol导出给pointer参数,这里一共导出了三个方法,JNI_GetDefaultJavaVMInitArgs_,JNI_CreateJavaVM_,JNI_GetCreatedJavaVMs_。这里从名字就可以看出三个方法分别是获得java虚拟机的初始化参数,创建java虚拟机,获得java虚拟机,这里具体的方法就不深入分析了,虚拟机本身也是一个非常复杂系统,我们这里主要还是分析android为主,这里知道方法是做什么的就可以了,后面有机会再来研究虚拟机。
我们回到AndroidRuntime的start方法,接着会调用startVm方法,这个方法就是调用上面导出的JNI_CreateJavaVM_方法来创建java虚拟机了:
```cpp
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{
............
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
ALOGE("JNI_CreateJavaVM failed\n");
return -1;
}
return 0;
}
```
这个方法开始部分都是些参数的初始化,最后我们看到调用了JNI_CreateJavaVM来创建java虚拟机,其中创建完成后每个进程就会获得JNIEnv指针,后面通过这个指针就可以进行JNI的调用了,这里JNIEnv指针是前面start方法调用时候传进来的,下面我们看到就可以使用这个指针了。
我们接着看AndroidRuntime的start方法,后面调用onVmCreated方法,这个方法在zygote这里没做什么事情,我们先不管这个。后面会调用startReg方法来注册和jni调用相关的方法:
```cpp
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
ATRACE_NAME("RegisterAndroidNatives");
// 设置线程创建的方法
androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
ALOGV("--- registering native functions ---\n");
env->PushLocalFrame(200);
// 注册jni各种方法
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
env->PopLocalFrame(NULL);
return -1;
}
env->PopLocalFrame(NULL);
//createJavaThread("fubar", quickTest, (void*) "hello");
return 0;
}
```
这里首先设置一个通过jni创建java线程的方法,通过调用androidSetCreateThreadFunc方法。之后调用register_jni_procs方法把jni必要的方法都注册给传进来的JNIEnv指针,我们大概看一眼有些什么方法:
```cpp
REG_JNI(register_com_android_internal_os_RuntimeInit),
REG_JNI(register_com_android_internal_os_ZygoteInit),
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_app_admin_SecurityLog),
REG_JNI(register_android_content_AssetManager),
REG_JNI(register_android_content_StringBlock),
REG_JNI(register_android_content_XmlBlock),
................
```
这些方法我们好像都有点眼熟,比如什么Log,SystemClock等,尤其是register_com_android_internal_os_ZygoteInit这个我们现在正在启动的就是这个,现在把这个注册到jni中了,后面我们可以看到调用它注册的方法。
我们回到AndroidRuntime的start继续看下一段代码:
```cpp
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
// 初始化一个string的类
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
// 初始化一个string数组
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
// 赋值类名
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
// 把类名设置到string数组第一个元素
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);
}
```
经过了上面的JNI初始化,这里这段代码就比较容易理解了。这里声明了一个String数组,数组的大小是传入的参数大小加一。数组的第一个元素是将要启动的类名,这里就是com.android.internal.os.ZygoteInit,其余元素是前面传过来的参数。好了,这段代码还是比较容易的,剩下还有最后一段代码,我们来看一下:
```cpp
// 获取类名
char* slashClassName = toSlashClassName(className);
// 获取jclass
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
// 获取这个类的main方法ID
jmethodID 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 {
// 这个类的main方法,即com.android.internal.os.ZygoteInit的main方法
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (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");
```
这段代码逻辑也比较清晰,调用jni的FindClass方法找到启动类的class,然后调用这个类的main方法,最后做一些清理工作。至此,接下去的流程就走到ZygoteInit的main方法里面了,并且从c++开始进入java了,我们就进入ZygoteInit中继续看代码。
# 进入java类ZygoteInit
```java
public static void main(String argv[]) {
ZygoteServer zygoteServer = new ZygoteServer();
// Mark zygote start. This ensures that thread creation will throw
// an error.
// 禁止创建线程。因为后序的进程都是fork zygote进程的
// 如果zygote里面还开启的好多线程,那么线程只有的资源在fork时候
// 就不好控制,比如持有了锁,那如果没有fork到新线程中,容易有死锁等风险
// 所以还是单线程的好
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);
}
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";
BootTimingsTraceLog bootTimingsTraceLog = new BootTimingsTraceLog(bootTimeTag,
Trace.TRACE_TAG_DALVIK);
bootTimingsTraceLog.traceBegin("ZygoteInit");
// 启动DDMS,调式相关的
RuntimeInit.enableDdms();
// Start profiling the zygote initialization.
SamplingProfilerIntegration.start(); // 开启性能监测
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; // 是否要启动systemServer进程,第一次启动会接着启动
} 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)) {
// socket名字,是之前的ANDROID_SOCKET_ENV_PREFIX_zygote这个名字
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.");
}
```
ZygoteInit的main方法也比较长,这里也同样分几段来看。这里首先new了一个ZygoteServer类,这个类下面我们在注册socket的时候会调用里面的方法。然后调用startZygoteNoThreadCreation方法,这个方法是禁止创建线程,我们知道zygote进程是一个父进程,后续的进程都是从zygote进场fork出来的,所以如果zygote进程里有很多线程,有些线程会持有锁,那么fork后的情况就比较复杂了,很可能会出现死锁的问题,所以为了不出现问题,这里禁止zygote进程创建线程。之后就启动DDMS,这是和调试相关的。然后再启动性能统计。最后初始化一些参数变量,比如是否要启动systemServer,abi的列表,以及socket的名字。这段代码主要是开始做一些初始化东西,接着我们看下一段:
```java
// 注册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);
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
bootTimingsTraceLog.traceEnd(); // ZygotePreload
} else {
Zygote.resetNicePriority();
}
```
这里第二段代码,主要做了两件事。第一是注册一个socket,之前我们在分析init进程的时候,在那里已经创建了一个socket,并把他注册到环境变量里面了,由于后面要创建systemServer进程了,所以需要与进程进行通信,由于在android中常用的进程通信模块binder这时候还没有起来,所以现在只有使用socket来进行进程间通信,zygote这里需要先注册到之前init进程创建的socket后才能使用,这会调用registerServerSocket方法来注册,这个方法我们等一下看。这段代码除了注册socket外,还加载了一些类和系统资源,我们先看看加载的类和资源的方法,之后再看注册socket的方法。
```java
// 预加载一些类和资源
static void preload(BootTimingsTraceLog bootTimingsTraceLog) {
Log.d(TAG, "begin preload");
bootTimingsTraceLog.traceBegin("BeginIcuCachePinning");
beginIcuCachePinning();
bootTimingsTraceLog.traceEnd(); // BeginIcuCachePinning
bootTimingsTraceLog.traceBegin("PreloadClasses");
// 预加载类,这些类都是后面fork子进程中必要的一些类
// 这里在zygote中加载好了,后面fork的子进程就可以直接用了,不用加载了
preloadClasses();
bootTimingsTraceLog.traceEnd(); // PreloadClasses
bootTimingsTraceLog.traceBegin("PreloadResources");
// 预加载资源
preloadResources();
bootTimingsTraceLog.traceEnd(); // PreloadResources
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
// 预加载openGL
preloadOpenGL();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
// 预加载动态库
preloadSharedLibraries();
// 预加载和text相关的一些资源
preloadTextResources();
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
// for memory sharing purposes.
// webview相关的加载
WebViewFactory.prepareWebViewInZygote();
endIcuCachePinning();
warmUpJcaProviders();
Log.d(TAG, "end preload");
// 预加载完成标志
sPreloadComplete = true;
}
```
这里加载的类和资源有不少,我们大概了解下有哪些内容就可以了。首先会调用preloadClasses方法来加载一些类,这里加载的都是一些通用的类,比如framework层要用到的context,view等等诸多的类,我们稍微扫一眼这样方法:
```java
private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";
private static void preloadClasses() {
// 获得VM
final VMRuntime runtime = VMRuntime.getRuntime();
InputStream is;
try {
// 打开文件流
is = new FileInputStream(PRELOADED_CLASSES);
} catch (FileNotFoundException e) {
Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
return;
}
......................
float defaultUtilization = runtime.getTargetHeapUtilization();
runtime.setTargetHeapUtilization(0.8f);
try {
// 包装输入流
BufferedReader br
= new BufferedReader(new InputStreamReader(is), 256);
int count = 0;
String line;
// 遍历文件每行
while ((line = br.readLine()) != null) {
// Skip comments and blank lines.
line = line.trim();
// 如果这行是注释,或者是空,跳过
if (line.startsWith("#") || line.equals("")) {
continue;
}
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line);
try {
Class.forName(line, true, null);
count++;
}
...............
}
```
这里可以看到,会读取/system/etc/preloaded-classes这个目录下的文件,然后通过Class.forName来加载这些类。/system/etc/preloaded-classes这个目录是系统编译完成后的目录,这个文件的源码在frameworks/base/preloaded-classes这里,具体代码就不提出来了,有兴趣的同学,可以到这个目录下面去看看,里面有哪些类。我们回到preload方法,接着会调用preloadResources方法来加载资源,我们看下这个方法:
```java
private static void preloadResources() {
final VMRuntime runtime = VMRuntime.getRuntime();
try {
mResources = Resources.getSystem();
mResources.startPreloading();
if (PRELOAD_RESOURCES) {
Log.i(TAG, "Preloading resources...");
long startTime = SystemClock.uptimeMillis();
// 加载图片资源
TypedArray ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_drawables);
int N = preloadDrawables(ar);
ar.recycle();
Log.i(TAG, "...preloaded " + N + " resources in "
+ (SystemClock.uptimeMillis()-startTime) + "ms.");
startTime = SystemClock.uptimeMillis();
// 加载颜色资源
ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_color_state_lists);
N = preloadColorStateLists(ar);
ar.recycle();
Log.i(TAG, "...preloaded " + N + " resources in "
+ (SystemClock.uptimeMillis()-startTime) + "ms.");
if (mResources.getBoolean(
com.android.internal.R.bool.config_freeformWindowManagement)) {
startTime = SystemClock.uptimeMillis();
ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_freeform_multi_window_drawables);
N = preloadDrawables(ar);
ar.recycle();
Log.i(TAG, "...preloaded " + N + " resource in "
+ (SystemClock.uptimeMillis() - startTime) + "ms.");
}
}
mResources.finishPreloading();
} catch (RuntimeException e) {
Log.w(TAG, "Failure preloading resources", e);
}
}
```
这个方法主要是加载一些图片和颜色资源,这些会是启动时候用到的,具体资源可以自己去看看,这里方法中给出了资源的名字。再回到preload方法,接着会陆续加载openGL,共享库的一些so,字体相关的资源,webview等等,这些资源都加完了就返回继续后面的执行。
以上这些资源涉及到方方面面,这里不一一深入分析,对于哪方面资源感兴趣的同学可以跟进这一方面的资源代码看看,我们这里就大概知道这里在做什么事情就可以了。接着我们主要看下前面注册socket的方法ZygoteServer的registerServerSocket。
# 注册socket
```java
void registerServerSocket(String socketName) {
if (mServerSocket == null) {
int fileDesc;
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try {
// fullSocketName是之前放入环境变量socket对象的名字
String env = System.getenv(fullSocketName);
// 得到之前socket的句柄
fileDesc = Integer.parseInt(env);
} catch (RuntimeException ex) {
throw new RuntimeException(fullSocketName + " unset or invalid", ex);
}
try {
// 根据之前创建socket的句柄创建FileDescriptor
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc);
// 根据fileDesc创建一个FileDescriptor类
// 然后创建一个socket类,就可以监听任务了
mServerSocket = new LocalServerSocket(fd);
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
}
```
这个方法传入的参数是之前init进程中在init.rc中解析socket命令时候解析出的socket名字zygote,这里fullSocketName也是之前init进程创建后发布到环境变量中的key,他的值是"ANDROID_SOCKET_zygote"。所以这个方法首先调用System.getenv取出socket的文件描述符fileDesc。之后这里会创建一个LocalServerSocket,他绑定了一个文件描述符,也就是上面从环境变量中取出的socket的文件描述符,之后只要有其他进程和socket有通信,这里就会监听到,在下面创建systemServer的时候,我们就会看到,这里只要记住有一个LocalServerSocket监听着init那边创建的socket就可以了。我们回到main方法,接着往下看代码:
```java
Seccomp.setPolicy(); // 安全规则的一些设定
// 取消禁止线程的创建
ZygoteHooks.stopZygoteNoThreadCreation();
// 启动system server进程
if (startSystemServer) {
startSystemServer(abiList, socketName, zygoteServer);
}
Log.i(TAG, "Accepting command socket connections");
// 把socket的文件描述符,用select方法阻塞住,一旦有数据就被唤醒执行
zygoteServer.runSelectLoop(abiList);
zygoteServer.closeServerSocket();
} catch (Zygote.MethodAndArgsCaller caller) {
// systemServer进程最后会抛出异常,在这里被捕获
// 这里会执行systemServer的main方法
// 之所以用这种方式,是因为上面startSystemServer调用的,
// 所以回到这里的话,会把之前堆栈中的内容都弹出,所以就
// 好像是调用startSystemServer后就进入了main方法一样上面
// 另外,由于是fork的,所以zygote进场会返回到startSystemServer
// 方法后继续执行,但是新进程不能执行上面后面的代码,所以通过
// 抛出异常就只会执行catch里面的代码了
caller.run();
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
zygoteServer.closeServerSocket();
throw ex;
}
```
这里是main方法剩余的一些代码。首先设置下安全规则方面的配置,之后取消禁止线程的创建,因为zygote进程这里的初始化已经完成了,马上要fork子进程了,子进程是需要有创建线程的能力的,所以这里解除禁止创建子线程的设置。
接着如果要启动systemServer进程的话,就会调用startSystemServer方法来启动,这个方法我们等下来看。接着会执行ZygoteServer的runSelectLoop方法,这个方法里面就会监听socket文件描述符的变化了,一旦监听到这个socket的访问,那就意味了有创建新进程的请求的到来,那么这个方法就会处理新进程的请求了,这个方法我们等下也会继续分析。
这里值得注意的是最后catch中有执行caller.run()这个方法,这个方法是新进程被创建后,会执行新进程的入口方法,比如main方法,之所以会在catch中执行也是启动流程特地这样设计的,同样这个我们后面分析到这里的时候再说。
至此,main方法目前也就执行完毕了,我们现在有3个方法需要分析。一个是请求创建SystemServer进程的startSystemServer方法,一个是监听socket变化从而创建进程的zygoteServer.runSelectLoop方法,还有一个是在catch中执行的调用新进程入口方法的caller.run()。我们先说startSystemServer方法。
# 请求创建SystemServer进程入口方法startSystemServer
```java
private static boolean startSystemServer(String abiList, String socketName, ZygoteServer zygoteServer)
throws Zygote.MethodAndArgsCaller, RuntimeException {
// linxu的Capabilities机制,和权限有关一个集合,根据系统配置来设定这个集合
long capabilities = posixCapabilitiesAsBits(
OsConstants.CAP_IPC_LOCK,
OsConstants.CAP_KILL,
OsConstants.CAP_NET_ADMIN,
OsConstants.CAP_NET_BIND_SERVICE,
OsConstants.CAP_NET_BROADCAST,
OsConstants.CAP_NET_RAW,
OsConstants.CAP_SYS_MODULE,
OsConstants.CAP_SYS_NICE,
OsConstants.CAP_SYS_PTRACE,
OsConstants.CAP_SYS_TIME,
OsConstants.CAP_SYS_TTY_CONFIG,
OsConstants.CAP_WAKE_ALARM
);
/* Containers run without this capability, so avoid setting it in that case */
// 是否需要设置Capabilities
if (!SystemProperties.getBoolean(PROPERTY_RUNNING_IN_CONTAINER, false)) {
capabilities |= posixCapabilitiesAsBits(OsConstants.CAP_BLOCK_SUSPEND);
}
/* Hardcoded command line to start the system server */
// 下面的数组是启动system server进程的参数
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1032,3001,3002,3003,3006,3007,3009,3010",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"com.android.server.SystemServer",
};
```
这个方法我们分为两段来讲。首先这里是第一段,先获取capabilities权限的列表,capabilities是linux中的概念,我们可以把他看成是执行某些功能的权限,这个和android中的权限比较像,比如有些模块的访问需要要有权限才行,在android中这些权限会在minifest中申请,如果有权限了才会给与相关的功能。同样这里capabilities我们可以看做在linux中类似的一个功能,如果某个进程需要执行访问某个模块,需要申请对应的权限,上面代码中线获取这些权限,然后经过为运算返回一个数值,接着根据系统配置中需不需要设置这些权限来赋值。这块capabilities相关的大家了解一下意义就可以,感兴趣的同学可以自己去linux中看下相关的内容,这里我们主要还是往下分析zygote的流程。
接着这里声明了一个数组,里面是后面启动systemServer进程传过去的参数,这里我们看到有uid,gid,还有上面刚讲的capabilities权限组,以及进程名等等。这段代码主要是为后面将要启动的systemServer进程的参数做准备,下面我们看后半段代码:
```java
ZygoteConnection.Arguments parsedArgs = null;
int pid;
try {
// 将启动参数封装为Arguments对象
parsedArgs = new ZygoteConnection.Arguments(args);
// 设置调式相关
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
// 获取InvokeWith参数
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
/* Request to fork the system server process */
// fork出systemServer进程
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.debugFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
/* For child process */
if (pid == 0) { // 复制出的systemServer进程走这里
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
// 关闭zygote的socket
zygoteServer.closeServerSocket();
// fork出systemServer后,继续处理
handleSystemServerProcess(parsedArgs);
}
return true;
```
这里首先声明了一个ZygoteConnection.Arguments变量,这个变量是封装传给systemServer进程的参数的,这个变量主要就是遍历前面的参数数组args,然后封装为ZygoteConnection.Arguments对象,具体这里遍历方法不贴出来的,一方面不算短,另一方面也比较简单,大家可以自己去看一下。
接着首先设置下调试相关的属性,接着调用applyInvokeWithSystemProperty方法获取InvokeWith这个参数的值,方法如下:
```java
public static void applyInvokeWithSystemProperty(Arguments args) {
if (args.invokeWith == null && args.niceName != null) {
String property = "wrap." + args.niceName;
args.invokeWith = SystemProperties.get(property);
if (args.invokeWith != null && args.invokeWith.length() == 0) {
args.invokeWith = null;
}
}
}
```
如果invokeWith是空的话,会根据niceName从系统属性中获取invokeWith。不过正常情况下,invokeWith这个参数会是空,一般如果我们不是正常的启动,比如通过某些命令启动系统,这时候可能需要做一些额外的操作,比如在命令行执行一些命令,这个时候invokeWith参数可能是有值的,大部分我们正常启动的时候,这个参数可以不用管,这里我们大概知道下,后面遇到也不会陌生。
再接着代码往后执行Zygote.forkSystemServer方法开始fork新的进程了,这个我们马上会跟进去分析,我们先把下面代码看完。如果返回pid为0,说明是子进程,这里也就是systemServer,这个时候会继续调用handleSystemServerProcess方法来处理systemServer,这个方法我们等流程分析到这里后会继续跟进看。这个需要提一下的是,我们看到在执行handleSystemServerProcess前有个hasSecondZygote方法,之所以有这个方法是因为有可能会启动2个zygote,一个是主模式,一个是辅助模式,这里会启动2个zygote是因为在android 5.0后系统支持64位模式,在64位模式下运行启动一个32位模式的进程来做兼容。之前分析init的时候,我们知道启动zygote进程是通过解析init.rc的文件命令来启动的,那个时候我们看到init.rc有好几个文件,具体使用哪个是根据import /init.${ro.zygote}.rc这个命令来决定的,这里ro.zygote是系统配置的,其中有一个值是zygote64_32,这样import的时候会找到init.zygote64_32.rc这个文件,我们看下这个文件:
```cpp
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
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
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
socket zygote_secondary stream 660 root system
onrestart restart zygote
writepid /dev/cpuset/foreground/tasks
```
可以看到这里启动了2个service,后面一个是zygote_secondary,这个就是32位模式下的zygote了。好了,我们知道了两个zygote是怎么回事就行,实际我们分析代码主要看32位模式的,对其余有兴趣的同学可以自己再去研究。我们回到startSystemServer方法中,现在我们开始分析Zygote.forkSystemServer方法:
```java
public static int forkSystemServer(int uid, int gid, int[] gids, int debugFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
// 这个方法会把Daemon进程stop,其余的线程yield
VM_HOOKS.preFork();
// Resets nice priority for zygote process.
resetNicePriority(); // 设置线程优先级
// fork进程
int pid = nativeForkSystemServer(
uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities);
// Enable tracing as soon as we enter the system_server.
if (pid == 0) {
Trace.setTracingEnabled(true);
}
// 恢复其他线程
VM_HOOKS.postForkCommon();
return pid;
}
```
这个方法主要三个步骤,首先调用VM_HOOKS.preFork()方法会停止zygote的几个线程。之后调用nativeForkSystemServer方法来fork进程。最后调用VM_HOOKS.postForkCommon()方法恢复停止的线程。我们先看下VM_HOOKS.preFork()和VM_HOOKS.postForkCommon()这两个方法,最后再看fork新进程的方法。
```java
public void preFork() {
Daemons.stop();
waitUntilAllThreadsStopped();
token = nativePreFork();
}
```
这里首先会调用Daemons的stop方法:
```java
public static void stop() {
HeapTaskDaemon.INSTANCE.stop();
ReferenceQueueDaemon.INSTANCE.stop();
FinalizerDaemon.INSTANCE.stop();
FinalizerWatchdogDaemon.INSTANCE.stop();
}
```
可以看到这里有四个守护线程,之前我们说了,要fork一个新进程,为了保证运行的安全性,防止zygote要保持单线程,所以这里把守护进程也停止了。然后回到preFork方法,接着调用waitUntilAllThreadsStopped方法:
```java
private static void waitUntilAllThreadsStopped() {
File tasks = new File("/proc/self/task");
// All Java daemons are stopped already. We're just waiting for their OS counterparts to
// finish as well. This shouldn't take much time so spinning is ok here.
while (tasks.list().length > 1) {
Thread.yield();
}
}
```
这个方法中当前线程会等待所有任务都完成,如果还有任务的话,线程就会yield。然后还是回到preFork方法,最后调用nativePreFork方法,这个方法主要是对新进程的堆栈初始化,我们知道新进程是从zygote中fork的,所以目前的数据都是zygote的,既然要被新进程使用了,那么先清理下,这个方法比较长,最后会执行到art/runtime/gc/heap.cc的Heap::PreZygoteFork()方法,这里是比较基础的底层内容了,对于我们分析zygote流程关系不大,有兴趣研究的同学可以自己再去看看。
到这里在fork新进程前的准备工作就都做好了,我们回到前面forkSystemServer方法,再看下fork进程后调用的方法VM_HOOKS.postForkCommon():
```java
public void postForkCommon() {
Daemons.startPostZygoteFork();
}
public static void startPostZygoteFork() {
ReferenceQueueDaemon.INSTANCE.startPostZygoteFork();
FinalizerDaemon.INSTANCE.startPostZygoteFork();
FinalizerWatchdogDaemon.INSTANCE.startPostZygoteFork();
HeapTaskDaemon.INSTANCE.startPostZygoteFork();
}
```
这个方法可以看到把之前停止的四个守护进程重新开启,这样在新进程中又开始运行了。说完了这fork新进程前后的两个方法,现在我们开始看nativeForkSystemServer这个方法了,这个方法是个naivte方法,我们看下他的实现:
```cpp
static jint com_android_internal_os_Zygote_nativeForkSystemServer(
JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
jint debug_flags, jobjectArray rlimits, jlong permittedCapabilities,
jlong effectiveCapabilities) {
// for子进程
pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids,
debug_flags, rlimits,
permittedCapabilities, effectiveCapabilities,
MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true, NULL,
NULL, NULL, NULL);
if (pid > 0) { // pid大于0,说明是父进程,这里即为zygote进程
// The zygote process checks whether the child process has died or not.
ALOGI("System server process %d has been created", pid);
gSystemServerPid = pid; // 保存下systemServer的pid
// There is a slight window that the system server process has crashed
// but it went unnoticed because we haven't published its pid yet. So
// we recheck here just to make sure that all is well.
int status;
// 在上面ForkAndSpecializeCommon方法中会设置sigaddset(&sigchld, SIGCHLD),这个方法是子进程终止的
// 时候会发生给父进程一个表示终止的信号,所以下面的waitpid函数会接收到信号
// wait函数等待pid进程结束,如果pid进程被终止了,即这里fork的systemServer进程终止了,返回该进程的pid
if (waitpid(pid, &status, WNOHANG) == pid) {
ALOGE("System server process %d has died. Restarting Zygote!", pid);
RuntimeAbort(env, __LINE__, "System server process has died. Restarting Zygote!");
}
}
return pid;
}
```
这个方法首先调用ForkAndSpecializeCommon来继续fork进程,这个方法我们等下跟进去看。下面if分支中pid大于0会执行,我们知道pid大于0表示是父进程,也就是zygote进程,在这个分支里会调用waitpid来等待子进程的终止,在接下去我们要看的方法ForkAndSpecializeCommon中,如果子进程被终止了,会发送信号给父进程,父进程如果在waitpid这里接收到子进程被终止的信号,说明systemServer没有启动成功,我们知道systemServer是个非常重要的进程,很多系统模块都会在这个进程中初始化,所以如果他启动失败了,整个系统肯定是不能正常运行的,所以如果有接收到他被终止的信号,就会重启zygote进程,进而重启systemServer进程,重启zygote的方法是设置在信号处理函数中的,我们进入ForkAndSpecializeCommon方法就可以看到这个方法的设置:
```cpp
// fork一个子进程
static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
jint debug_flags, jobjectArray javaRlimits,
jlong permittedCapabilities, jlong effectiveCapabilities,
jint mount_external,
jstring java_se_info, jstring java_se_name,
bool is_system_server, jintArray fdsToClose,
jintArray fdsToIgnore,
jstring instructionSet, jstring dataDir) {
// 设置进程终止时的处理函数
SetSigChldHandler();
sigset_t sigchld;
// 清空信号集
sigemptyset(&sigchld);
// SIGCHLD表示当进程终止的时候会发送信号给父进程
sigaddset(&sigchld, SIGCHLD);
// Temporarily block SIGCHLD during forks. The SIGCHLD handler might
// log, which would result in the logging FDs we close being reopened.
// This would cause failures because the FDs are not whitelisted.
//
// Note that the zygote process is single threaded at this point.
// sigprocmask是将一个信号集加入到当前进程的阻塞信号集中,这里即屏蔽sigchld信号集里面的信号,SIGCHLD信号
// 在这里屏蔽SIGCHLD信号,是希望在fork子进程期间不要处理子进程终止事件,根据上面的注释,终止可能导致log
// 被重新打开,从而导致一些错误,而下面我们就要先关闭log了,所以还是等fork成功后再说吧
if (sigprocmask(SIG_BLOCK, &sigchld, nullptr) == -1) {
ALOGE("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno));
RuntimeAbort(env, __LINE__, "Call to sigprocmask(SIG_BLOCK, { SIGCHLD }) failed.");
}
// Close any logging related FDs before we start evaluating the list of
// file descriptors.
// 关闭log
__android_log_close();
// If this is the first fork for this zygote, create the open FD table.
// If it isn't, we just need to check whether the list of open files has
// changed (and it shouldn't in the normal case).
std::vector<int> fds_to_ignore;
FillFileDescriptorVector(env, fdsToIgnore, &fds_to_ignore);
// 如果文件描述符表是空,需要创建文件描述符表
if (gOpenFdTable == NULL) {
gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore);
if (gOpenFdTable == NULL) {
RuntimeAbort(env, __LINE__, "Unable to construct file descriptor table.");
}
} else if (!gOpenFdTable->Restat(fds_to_ignore)) {
RuntimeAbort(env, __LINE__, "Unable to restat file descriptor table.");
}
```
这个方法里面就是正式fork一个子进程了,这个方法比较长,我们分段来讲。首先会调用SetSigChldHandler这个方法,这个方法里面就是我们前面说的当子进程终止时,发送信号时处理的方法,我们跟进去看一下:
```cpp
// 设置子进程终止时的处理函数
static void SetSigChldHandler() {
// 创建一个信号结构体
struct sigaction sa;
// 初始化清0
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SigChldHandler; // 子进程终止时候的处理函数
// 设置子进程终止信号的处理函数
int err = sigaction(SIGCHLD, &sa, NULL);
if (err < 0) {
ALOGW("Error setting SIGCHLD handler: %s", strerror(errno));
}
}
```
这个方法首先声明一个sigaction结构体,之后把SigChldHandler方法设置给这个信号,最后把这个信号结构体设置给SIGCHLD信号,这个信号是子进程停止或者退出时候发信号给父进程。我们看下这里的处理方法SigChldHandler:
```cpp
static void SigChldHandler(int /*signal_number*/) {
pid_t pid;
int status;
int saved_errno = errno;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { // 子进程pid挂了
if (WIFEXITED(status)) {
if (WEXITSTATUS(status)) {
ALOGI("Process %d exited cleanly (%d)", pid, WEXITSTATUS(status));
}
} else if (WIFSIGNALED(status)) {
if (WTERMSIG(status) != SIGKILL) {
ALOGI("Process %d exited due to signal (%d)", pid, WTERMSIG(status));
}
if (WCOREDUMP(status)) {
ALOGI("Process %d dumped core.", pid);
}
}
if (pid == gSystemServerPid) { // 如果是子进程systemServer挂了
ALOGE("Exit zygote because system server (%d) has terminated", pid);
// zygote就会自杀,然后zygote会重启,之后systemServer也会重启
kill(getpid(), SIGKILL);
}
}
....................
errno = saved_errno;
}
```
这个方法中也会接收到终止进程的pid,如果pid是systemServer,那么就会调用kill方法杀死zygote自己,然后会重启,最后再把systemServer起来,这样就保证systemServer只有正常系统才能继续运行下去。回到ForkAndSpecializeCommon方法,设置好了子进程终止时的处理方法后,接着声明一个sigset_t变量,这个是一个信号集,里面保存着需要被设置的信号,这里我们需要的是子进程终止时通知父进程的信号,前面说过了这个信号是SIGCHLD,所以把他设置到信号集中。
之后调用sigprocmask方法先把这个信号集给屏蔽掉,这里要屏蔽这个信号是因为后面会先把日志系统关闭,因为日志这里还没有初始化所以一旦fork的进程中有需要日志的就会报错,从而会重启zygote,最后会变成一个死循环,所以这里暂时关闭,fork后再打开。之后会把日志相关的文件描述符关闭,之后如果这是zygtoe第一次fork进程,那么会创建一张文件描述符表,文件描述符表中保存着所有者这个进程有关的"文件",这里的文件不是指我们一般概念中的文件,我们知道linux中一切皆文件,比如像日志驱动也是一个文件,一个进程也是一个文件,这里保存着所有打开的文件。这样在fork前的准备工作就做好了,下面要开始fork了:
```cpp
// fork进程
pid_t pid = fork();
if (pid == 0) { // 走这里是zygote的子进程,比如systemServer进程
// The child process.
gMallocLeakZygoteChild = 1;
// Set the jemalloc decay time to 1.
mallopt(M_DECAY_TIME, 1);
// Clean up any descriptors which must be closed immediately
// 清除那些必须关闭的文件描述符
DetachDescriptors(env, fdsToClose);
// Re-open all remaining open file descriptors so that they aren't shared
// with the zygote across a fork.
if (!gOpenFdTable->ReopenOrDetach()) {
RuntimeAbort(env, __LINE__, "Unable to reopen whitelisted descriptors.");
}
// 恢复之前被阻塞的信号,即监听子进程是否挂了的信号
if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
ALOGE("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno));
RuntimeAbort(env, __LINE__, "Call to sigprocmask(SIG_UNBLOCK, { SIGCHLD }) failed.");
}
// Keep capabilities across UID change, unless we're staying root.
if (uid != 0) {
// 如果是非root用户,保持capabilities
EnableKeepCapabilities(env);
}
SetInheritable(env, permittedCapabilities);
DropCapabilitiesBoundingSet(env);
bool use_native_bridge = !is_system_server && (instructionSet != NULL)
&& android::NativeBridgeAvailable();
if (use_native_bridge) {
ScopedUtfChars isa_string(env, instructionSet);
use_native_bridge = android::NeedsNativeBridge(isa_string.c_str());
}
if (use_native_bridge && dataDir == NULL) {
// dataDir should never be null if we need to use a native bridge.
// In general, dataDir will never be null for normal applications. It can only happen in
// special cases (for isolated processes which are not associated with any app). These are
// launched by the framework and should not be emulated anyway.
use_native_bridge = false;
ALOGW("Native bridge will not be used because dataDir == NULL.");
}
// 挂载external storage
if (!MountEmulatedStorage(uid, mount_external, use_native_bridge)) {
ALOGW("Failed to mount emulated storage: %s", strerror(errno));
if (errno == ENOTCONN || errno == EROFS) {
// When device is actively encrypting, we get ENOTCONN here
// since FUSE was mounted before the framework restarted.
// When encrypted device is booting, we get EROFS since
// FUSE hasn't been created yet by init.
// In either case, continue without external storage.
} else {
RuntimeAbort(env, __LINE__, "Cannot continue without emulated storage");
}
}
if (!is_system_server) { // 非system_server,创建进程组
int rc = createProcessGroup(uid, getpid());
if (rc != 0) {
if (rc == -EROFS) {
ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
} else {
ALOGE("createProcessGroup(%d, %d) failed: %s", uid, pid, strerror(-rc));
}
}
}
// 设置group id
SetGids(env, javaGids);
SetRLimits(env, javaRlimits);
if (use_native_bridge) {
ScopedUtfChars isa_string(env, instructionSet);
ScopedUtfChars data_dir(env, dataDir);
android::PreInitializeNativeBridge(data_dir.c_str(), isa_string.c_str());
}
// 设置真实(ruid),有效(euid),保存的(suid)的groupId
int rc = setresgid(gid, gid, gid);
if (rc == -1) {
ALOGE("setresgid(%d) failed: %s", gid, strerror(errno));
RuntimeAbort(env, __LINE__, "setresgid failed");
}
// 设置真实(ruid),有效(euid),保存的(suid)的uid
rc = setresuid(uid, uid, uid);
if (rc == -1) {
ALOGE("setresuid(%d) failed: %s", uid, strerror(errno));
RuntimeAbort(env, __LINE__, "setresuid failed");
}
if (NeedsNoRandomizeWorkaround()) {
// Work around ARM kernel ASLR lossage (http://b/5817320).
int old_personality = personality(0xffffffff);
int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
if (new_personality == -1) {
ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
}
}
// 设置Capabilities权限相关
SetCapabilities(env, permittedCapabilities, effectiveCapabilities, permittedCapabilities);
// 设置调度策略
SetSchedulerPolicy(env);
const char* se_info_c_str = NULL;
ScopedUtfChars* se_info = NULL;
if (java_se_info != NULL) {
se_info = new ScopedUtfChars(env, java_se_info);
se_info_c_str = se_info->c_str();
if (se_info_c_str == NULL) {
RuntimeAbort(env, __LINE__, "se_info_c_str == NULL");
}
}
const char* se_name_c_str = NULL;
ScopedUtfChars* se_name = NULL;
if (java_se_name != NULL) {
se_name = new ScopedUtfChars(env, java_se_name);
se_name_c_str = se_name->c_str();
if (se_name_c_str == NULL) {
RuntimeAbort(env, __LINE__, "se_name_c_str == NULL");
}
}
// 设置上下文相关
rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
if (rc == -1) {
ALOGE("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
is_system_server, se_info_c_str, se_name_c_str);
RuntimeAbort(env, __LINE__, "selinux_android_setcontext failed");
}
// Make it easier to debug audit logs by setting the main thread's name to the
// nice name rather than "app_process".
// 如果是systemServer,设置线程名字是system_server
if (se_info_c_str == NULL && is_system_server) {
se_name_c_str = "system_server";
}
if (se_info_c_str != NULL) {
SetThreadName(se_name_c_str);
}
delete se_info;
delete se_name;
// 恢复子进程的SIGCHLD信号为默认的处理
UnsetSigChldHandler();
// 调用zygote.callPostForkChildHooks()
// 完成一些扫尾工作
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, debug_flags,
is_system_server, instructionSet);
if (env->ExceptionCheck()) {
RuntimeAbort(env, __LINE__, "Error calling post fork hooks.");
}
}
```
这段代码是fork出一个子进程后,如果pid为0,说明是子进程,这里的话就是systemServer。虽然这里看上去很多代码,但是我们大部分只要了解下就可以,主要都是些系统方面有关的设置,比如之前屏蔽了子进程终止时候向父进程发送信号,这里调用igprocmask(SIG_UNBLOCK, &sigchld, nullptr)重新把信号打开。再比如这里会挂在外存储设备,设置uid,gid,设置Capabilities相关的权限,设置线程名等等。这里最后会调用UnsetSigChldHandler这个方法,记得这个方法开头调用SetSigChldHandler方法吗,当时这个方法是这个SIGCHLD信号的处理方法的,方法是父进程接收到子进程终止的信号后会杀死自己,这个方法主要是针对zygote进程的,现在这里是systemServer,不需要和zygote通用的处理方法,所以调用UnsetSigChldHandler方法把接收到信号后处理方法修改为默认的,我们看下方法:
```cpp
static void UnsetSigChldHandler() {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_DFL;
int err = sigaction(SIGCHLD, &sa, NULL);
if (err < 0) {
ALOGW("Error unsetting SIGCHLD handler: %s", strerror(errno));
}
}
```
这个方法和SetSigChldHandler方法类似,区别在于默认的方法是SIG_DFL,这是空定义的方法,指向地址0,其实也就是不执行。最后还会调用CallStaticVoidMethod方法,这个方法会调用到Zygote的callPostForkChildHooks方法,做一些清理方法的工作,这里也不多叙说了,有兴趣的同学可以跟过去看看。这样一个systemServer就fork出来,之后systemServer会回到ZygoteInit中的startSystemServer方法继续执行,注意这时候ZygoteInit中执行的是在systemServer进程中,我们稍后回去看。
目前我们还是回到ForkAndSpecializeCommon方法,还有一段代码没有看完:
```cpp
else if (pid > 0) { // zygote64
// the parent process
// We blocked SIGCHLD prior to a fork, we unblock it here.
// 父进程即zygote进程,不阻塞SIGCHLD信号,因为systemserver起来后,如果挂了,需要zygote来处理
if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
ALOGE("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno));
RuntimeAbort(env, __LINE__, "Call to sigprocmask(SIG_UNBLOCK, { SIGCHLD }) failed.");
}
}
return pid;
```
这里是最后一段代码,是pid大于0的时候走的分支,所以这里是zygote进程,这里的处理很简单,在fork前面把SIGCHLD信号屏蔽了,fork后再子进程中已经恢复了,但是把接受信号的处理方法修改为默认的了,也就是一个空方法。但是zygote进程这里是需要接受的,并且方法还是杀死自己,所以这里只要把信号取消屏蔽就可以了。这样的话,ForkAndSpecializeCommon方法就看完了, 这里主要就是fork一个进程,然后设置子进程终止时候的处理方法,其余都是系统属性,权限方面的设置,我们了解一下就好。
经过一系列的分析,整个流程已经有些复杂了。我们看看现在接下去还有哪些方法还没分析。之前的ZygoteInit类的main方法中,前面我们说了要分析三个方法,现在ForkAndSpecializeCommon方法分析完了,还剩下两个方法,同时上面我们分析的ForkAndSpecializeCommon方法,fork出新进程后,如果是systemServer进程会返回到ZygoteInit类的startSystemServer方法,会继续调用handleSystemServerProcess方法,如果是zygote进程则会一直返回到ZygoteInit的main方法。所以接下去我们需要分析的方法是ZygoteInit的main中调用的ZygoteServer的runSelectLoop方法和Zygote的MethodAndArgsCaller方法,和systemServer进程继续会调用的handleSystemServerProcess方法。正常情况下,runSelectLoop方法只有zygote进程才会执行。而MethodAndArgsCaller和handleSystemServerProcess方法会在systemServer进程中执行,所以下面我们会接着讲runSelectLoop方法。
```java
void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
// 把之前创建的socket文件描述符加入到数组
fds.add(mServerSocket.getFileDescriptor());
peers.add(null);
while (true) {
// 有多少socket就创建几个数组,每个socket都会被监听,一旦有信息了,socket就会执行相关任务
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
}
try {
// 监听这些文件描述符
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
// 一旦有数据可读了,就会走到这里。如果i==0,说明是第一个
// 第一个就是zygote的socket的文件描述符,所以说明是新创建进程的命令
// 那么就加入一个文件描述符继续监听
for (int i = pollFds.length - 1; i >= 0; --i) {
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
// 到这里说明是第一个文件描述符有可读数据,肯定是创建新进程的命令
// 正常来说Process.start方法调用后,会调用到connect来触发这里
// 之后会往数据流中写输入,会触发else的
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
// 如果走到这里,说明是一个已经创建的进程有数据可能
// 那么就触发这个进程的初始化方法
boolean done = peers.get(i).runOnce(this);
// 新进程完成初始化了,清除掉之前的保存
if (done) {
peers.remove(i);
fds.remove(i);
}
}
}
}
}
```
首先这里的mServerSocket之前有过分析,他是一个LocalServerSocket类,他持有在init进程中创建的socket的文件描述符,这里把他的文件描述符fd取出后,保存在fds这个集合中。同时我们可以看到这里有一个peers集合,这个集合保存的是向socket进程发出请求的数据,我们知道后面的进程都是由zygote通过fork来创建的,所以当有一个创建进程的请求向socket发出后,这里的peers就会保存创建进程的数据,之后取出这个对象后就会开始创建新进程,之后我们会通过实际例子来理解的,这个先大致有个印象。
我们继续看这个方法,这里有个while死循环,循环里面有个pollFds数组,这个数组保存着所有被创建的socket的文件描述符,之后会通过linux的poll方法来监听这些文件描述符所代表的socket,如果有socket被链接的,那么就可以从监听的集合中获取被链接的那个对象,否则poll会睡眠在进程上,cpu控制权会交给其他进程。
这里对poll方法稍做点介绍,poll是linux中IO的多路复用监听方式,之前我们分析binder模块的时候也看到过select的用法,其实select和poll是类似的,都是可以同时监听多个文件描述符,他们的区别是select中监听一个文件描述符的集合,一旦有数据被监听到,可以通过方法获取那个文件描述符被简体拿到。 而poll这里我们可以看到监听的数组中每个元素是一个对象,这个对象的数据结构大致是这样的:
```java
struct pollfd {
int fd;
short events;
short revents;
};
```
这里有3个字段,fd自然不用说是文件描述符,events表示的是监听的类型,比如POLLIN表示这个数据可读,POLLOUT表示这个数据可写,其他还有好多种,这里不一一列出了,上面这段代码可以看到数据类型是POLLIN表示监听数据是否可读。最后一个字段revents表示当前这个数据是否已经可读了,表示这个数据的当前状态。当前代码中其实就一个socket的文件描述符,所以如果这个socket有链接的话,就会唤醒这个线程继续往下执行,如果没有链接,那么zygote就会阻塞在这里。具体是怎么向这个socket发请求的,后面我们会说,这里假如poll已经被唤醒了,我们继续看后面的代码。
下面会遍历poll监听的对象数组,如果数组中的对象的revents字段不是POLLIN值的话,说明这个元素不可读,否则就说明该元素已经可读的,首先会调用acceptCommandPeer方法创建一个ZygoteConnection对象,这个对象中包含着socket,文件描述符等诸多的信息,我们后面再来看这个方法,然后把ZygoteConnection这个对象加入peers集合中,后面会从这个集合中取出来创建新进程。然后把socket的文件描述符添加到fds集合中,这样最外层的while
循环就又重新执行了,此时监听的数组pollFds就会多了一项,也就是ZygoteConnection这个对象中取出的文件描述符。由于这个数组的第一项永远代表init进程那里创建的socket,所以后面的for训练会根据可读元素是否是第0项,如果是第0项则是连接到socket的命令,不是第0项则是开始创建新进程。
这里在接收到连接socket的命令后,重新执行while循环,然后继续睡眠在这个线程上,直到有新命令的到来,此时由于pollFds数组中已经大于一项了,所以后面会执行else的代码,这里会取出之前添加的ZygoteConnection对象,执行他的runOnce方法。
这个方法的整个流程如上所述,现在我们看下前面说到的创建ZygoteConnection这个类的方法acceptCommandPeer:
```java
private ZygoteConnection acceptCommandPeer(String abiList) {
try {
// 这里mServerSocket.accept()是在ZygoteState.connect方法里new出来的那个LocalSocket
// 即AMS那里的请求,ZygoteProcess方法中的
return createNewConnection(mServerSocket.accept(), abiList);
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
}
}
protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
throws IOException {
return new ZygoteConnection(socket, abiList);
}
ZygoteConnection(LocalSocket socket, String abiList) throws IOException {
mSocket = socket;
this.abiList = abiList;
mSocketOutStream
= new DataOutputStream(socket.getOutputStream());
mSocketReader = new BufferedReader(
new InputStreamReader(socket.getInputStream()), 256);
mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS);
try {
peer = mSocket.getPeerCredentials();
} catch (IOException ex) {
Log.e(TAG, "Cannot read peer credentials", ex);
throw ex;
}
}
```
这个方法最后会调用到createNewConnection这个方法。这个方法的第一个参数是LocalSocket,这个LocalSocket是在请求创建新进行那个地方创建的,比如后面我们会讲到AMS请求创建一个新进程,这里的LocalSocket就是在那里创建的,同时还会创建输入和输出流,我们在上面的方法中看到,这里从LocalSocket中取出的输入和输出流,后面会通过输入流来读取创建新进程的数据,最后会通过getPeerCredentials方法创建一个Credentials,这个是一个代表身份的类类,里面保存着pid,uid,gid等,我们看下他的创建方法:
```java
public Credentials getPeerCredentials() throws IOException {
return impl.getPeerCredentials();
}
public Credentials getPeerCredentials() throws IOException {
return getPeerCredentials_native(fd);
}
static jobject socket_get_peer_credentials(JNIEnv *env,
jobject object, jobject fileDescriptor)
{
int err;
int fd;
if (fileDescriptor == NULL) {
jniThrowNullPointerException(env, NULL);
return NULL;
}
fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
if (env->ExceptionCheck()) {
return NULL;
}
struct ucred creds;
memset(&creds, 0, sizeof(creds));
socklen_t szCreds = sizeof(creds);
// 获取socket的peercred属性
err = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);
if (err < 0) {
jniThrowIOException(env, errno);
return NULL;
}
if (szCreds == 0) {
return NULL;
}
return env->NewObject(class_Credentials, method_CredentialsInit,
creds.pid, creds.uid, creds.gid);
}
```
我们可以看到,这个方法最终会调用jni的socket_get_peer_credentials方法。这个方法最后会调用env->NewObject来创建类,这个类就是Credentials,首先通过getsockopt方法从socket的fd中获取相关的pid,uid,gid,最后通过NewObject方法来创建Credentials类,这个方法的细节就不多说了,大家可以自己去看看。
我们回到前面runSelectLoop方法,这样ZygoteConnection对象就创建好了,后面会调用他的runOnce来创建新进程,这里我们就暂时不继续往下分析了,因为后面会通过一个实际创建进程的例子结合代码分析,这样从创建进程的请求到进程的创建就可以连起来看,理解起来就比较连贯了,这里我们就说到socket在等待新进程的创建链接,目前zygote进程睡眠在这里,至此整个zygote进程的流程就分析完了。可以看到最终zygote进程就是在等待创建新进程的命令了,除此之外没有任务的话,他就会一直睡眠在这里。
到这里,这篇文章基本就要结束了,我们通过整篇文章的分析,大致已经知道了zygote进程是怎么回事,目前我们的代码分析还遗留了一些没有分析,比如zygote进程中fork出了systemServer进程后,会继续调用handleSystemServerProcess来处理systemServer。以及由于systemSever进程是由zygote进程fork出来的,所以它最终还会调用到zygote的MethodAndArgsCaller,这两个方法我们会在下面的分析systemServer进程文章中继续分析。另外目前zygote进程是睡眠了,但是一旦有新进程创建命令的到来,上面分析的runSelectLoop方法中还会调用ZygoteConnection的runOnce方法来创建新进程,这个我们后面的文章中会通过一个创建进程的实例来继续分析这个方法。好了,这篇文章就到这里了,我们看下zygote进程的时序图。

android启动之zygote进程