上一博文介绍了init进程启动,在解析init.rc 的时候会把zygote加到service列表中,并最终启动,zygote启动的实际是app_process程序。zygote是init进程的子进程。在Android系统中,所有的应用程序以及系统服务,包括SystemServer都是由Zygote fork出来的,这就是为什么它叫zygote(受精卵)的原因。我们再来看一下.rc文件的描述:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main socket zygote stream 660 root system onrestart write /sys/android_power/request_state wake onrestart write /sys/power/state on onrestart restart media onrestart restart netd服务名称为: zygote
启动该服务执行的命令: /system/bin/app_process
命令的参数: -Xzygote /system/bin --zygote --start-system-server
socket zygote stream 660: 创建一个名为:/dev/socket/zygote 的 socket ,类型为:stream
app_main.cpp
Zygote的main函数在\frameworks\base\cmds\app_process\app_main.cpp,如下:int main(int argc, const char* const argv[]){ ... int i = runtime.addVmArguments(argc, argv); //返回的i一般会是1 while (i < argc) { const char* arg = argv[i++]; if (!parentDir) { //取值应该是/system/bin parentDir = arg; } else if (strcmp(arg, "--zygote") == 0) { zygote = true; niceName = "zygote"; } 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 = arg + 12; } else { className = arg; break; } } if (niceName && *niceName) { setArgv0(argv0, niceName); //把app_process的进程名改为zygote set_process_name(niceName); } runtime.mParentDir = parentDir; if (zygote) { //核心代码 runtime.start("com.android.internal.os.ZygoteInit",startSystemServer ? "start-system-server" : ""); } else if (className) { // Remainder of args get passed to startup class main() runtime.mClassName = className; runtime.mArgC = argc - i; runtime.mArgV = argv + i; runtime.start("com.android.internal.os.RuntimeInit", application ? "application" : "tool"); } else { fprintf(stderr, "Error: no class name or --zygote supplied.\n"); app_usage(); LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); return 10; }}如果运行正常的话,会走到runtime.start("com.android.internal.os.ZygoteInit",startSystemServer ? "start-system-server" : ""),如下:
void AndroidRuntime::start(const char* className, const char* options){ ALOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n", className != NULL ? className : "(unknown)"); //创建一个信号管道 blockSigpipe(); ... //启动虚拟机 JNIEnv* env; if (startVm(&mJavaVM, &env) != 0) { return; } //空函数 onVmCreated(env); //注册android的native函数(JNI的接口),注册了一个用于创建线程的函数(javaCreateThreadEtc) if (startReg(env) < 0) { ALOGE("Unable to register all android natives\n"); return; } //创建java.lang.String数组,其内容值是:[ “com.android.internal.os.ZygoteInit”,“true”] jclass stringClass; jobjectArray strArray; jstring classNameStr; jstring optionsStr; stringClass = env->FindClass("java/lang/String"); assert(stringClass != NULL); strArray = env->NewObjectArray(2, stringClass, NULL); assert(strArray != NULL); classNameStr = env->NewStringUTF(className); assert(classNameStr != NULL); env->SetObjectArrayElement(strArray, 0, classNameStr); optionsStr = env->NewStringUTF(options); env->SetObjectArrayElement(strArray, 1, optionsStr); /* * Start VM. This thread becomes the main thread of the VM, and will * not return until the VM exits. */ char* slashClassName = toSlashClassName(className); //查找com.android.internal.os.ZygoteInit类 jclass startClass = env->FindClass(slashClassName); if (startClass == NULL) { ALOGE("JavaVM unable to locate class '%s'\n", slashClassName); /* keep going */ } else { //查找ZygoteInit类的main函数 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 { //调用ZygoteInit类的main函数,参数是strArray。虚拟机执行的第一个Java类就是ZygoteInit.java 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");}
ZygoteInit.java
虚拟机执行的第一个Java类就是ZygoteInit.java,它的main函数如下:public static void main(String argv[]) { try { // Start profiling the zygote initialization. SamplingProfilerIntegration.start(); //创建了一个socket接口,用来和ActivityManagerService通讯 registerZygoteSocket(); //装载Framework大部分类及资源,并和子进程共享 EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,SystemClock.uptimeMillis()); preload(); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, SystemClock.uptimeMillis()); // Finish profiling the zygote initialization. SamplingProfilerIntegration.writeZygoteSnapshot(); // Do an initial gc to clean up after startup gc(); // If requested, start system server directly from Zygote if (argv.length != 2) { throw new RuntimeException(argv[0] + USAGE_STRING); } //启动SystemServer,此处会启动一个新的进程 if (argv[1].equals("start-system-server")) { startSystemServer(); } else if (!argv[1].equals("")) { throw new RuntimeException(argv[0] + USAGE_STRING); } Log.i(TAG, "Accepting command socket connections"); if (ZYGOTE_FORK_MODE) { runForkMode(); } else { //无限循环,在前面创建的socket接口上等待ActivityManagerService请求创建新的应用程序进程 runSelectLoopMode(); } closeServerSocket(); } catch (MethodAndArgsCaller caller) { caller.run(); } catch (RuntimeException ex) { Log.e(TAG, "Zygote died with exception", ex); closeServerSocket(); throw ex; } }
创建socket接口
private static void registerZygoteSocket() { if (sServerSocket == null) { int fileDesc; try { //获取socket描述符 String env = System.getenv(ANDROID_SOCKET_ENV); fileDesc = Integer.parseInt(env); } catch (RuntimeException ex) { throw new RuntimeException( ANDROID_SOCKET_ENV + " unset or invalid", ex); } try { //在Linux系统中,所有的系统资源都可以看成是文件,甚至包括内存和CPU,因此,像标准的磁盘文件或者网络Socket自然也被认为是文件,这就是为什么LocalServerSocket构造函数的参数是一个文件描述符。 sServerSocket = new LocalServerSocket( createFileDescriptor(fileDesc)); } catch (IOException ex) { throw new RuntimeException( "Error binding to local socket '" + fileDesc + "'", ex); } } }
Socket编程中有两种方式去触发Socket数据读操作。一种是使用listen()监听某个端口,然后调用read()去从这个端口上读数据,这种方式被称为阻塞式读操作,因为当端口没有数据时,read()函数将一直等待,直到数据准备好后才返回;另一种是使用select()函数将需要监测的文件描述符作为select()函数的参数,然后当该文件描述符上出现新的数据后,自动触发一个中断,然后在中断处理函数中再去读指定文件描述符上的数据,这种方式被称为非阻塞式读操作。LocalServerSocket中使用的正是后者,即非阻塞读操作。
预加载类和资源
在Android源码编译的时候,会最终把preload-classes文件打包到framework.jar中。ZygoteInit中通过调用preloadClasses()完成装载这些类。装载的方法很简单,就是读取preload-classes列表中的每一行,因为每一行代表了一个具体的类,然后调用Class.forName()装载目标类。preloadResources()函数中分别调用preloadDrawables()和preloadColorStateLists()加载两类资源。加载的原理很简单,就是把这些资源读出来放到一个全局变量中,只要该类对象不被销毁,这些全局变量就会一直保存。保存Drawable资源的全局变量是mResources,该变量的类型是Resources类,由于该类内部会保存一个Drawable资源列表,因此,实际上缓存这些Drawable资源是在Resources内部;保存Color资源的全局变量也是mResources,同样,Resources类内部也有一个Color资源的列表。
static void preload() { preloadClasses(); preloadResources(); }
循环等待服务
private static void runSelectLoopMode() throws MethodAndArgsCaller { //首先将sServerSocket加入到被监测的文件描述符列表中 ArrayList<FileDescriptor> fds = new ArrayList(); ArrayList<ZygoteConnection> peers = new ArrayList(); FileDescriptor[] fdArray = new FileDescriptor[4]; fds.add(sServerSocket.getFileDescriptor()); peers.add(null); int loopCount = GC_LOOP_COUNT; while (true) { int index; /* * Call gc() before we block in select(). * It's work that has to be done anyway, and it's better * to avoid making every child do it. It will also * madvise() any free memory as a side-effect. * * Don't call it every time, because walking the entire * heap is a lot of overhead to free a few hundred bytes. */ if (loopCount <= 0) { gc(); loopCount = GC_LOOP_COUNT; } else { loopCount--; } //selectReadable()函数的返回值有三种。一种是-1,代表着内部错误;第二种是0,代表着没有可处理的连接,因此会以Socket服务端口重新建立一个ZygoteConnection对象,并等待客户端的请求;第三种是大于0, 代表着还有没处理完的连接请求,因此需要先处理该请求,而暂时不需要建立新的连接等待。 try { fdArray = fds.toArray(fdArray); index = selectReadable(fdArray); } catch (IOException ex) { throw new RuntimeException("Error in select()", ex); } if (index < 0) { throw new RuntimeException("Error in select()"); } else if (index == 0) { //接受Android应用的socket命令 ZygoteConnection newPeer = acceptCommandPeer(); peers.add(newPeer); fds.add(newPeer.getFileDesciptor()); } else { boolean done; //基于zygote进程孵化出新的应用进程 done = peers.get(index).runOnce(); if (done) { peers.remove(index); fds.remove(index); } } } }
fork创建进程
fork是Linux系统的一个系统调用,其作用是复制当前进程,产生一个新的进程。新进程将拥有和原始进程完全相同的进程信息,除了进程id不同。进程信息包括该进程所打开的文件描述符列表、所分配的内存等。当新进程被创建后,两个进程将共享已经分配的内存空间,直到其中一个需要向内存中写入数据时,操作系统才负责复制一份目标地址空间,并将要写的数据写入到新的地址中,这就是所谓的copy-on-write机制,即“仅当写的时候才复制”,这种机制可以最大限度地在多个进程中共享物理内存。这就是我们会先预加载framework的类和资源的原因,这样子进程就可以与zygote共享framework资源了。在操作系统内部,启动新的进程包含三个过程。
第一个过程,内核创建一个进程数据结构,用于表示将要启动的进程。
第二个过程,内核调用程序装载器函数,从指定的程序文件读取程序代码,并将这些程序代码装载到预先设定的内存地址。
第三个过程,装载完毕后,内核将程序指针指向到目标程序地址的入口处开始执行指定的进程。当然,实际的过程会考虑更多的细节,不过大致思路就是这么简单。
由于fork()函数是Linux的系统调用,Android中的Java层仅仅是对该调用进行了JNI封装而已。ZygoteConnection类的runOnce()函数就是通过JNI调用fork()函数创建子进程的。
fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
1)在父进程中,fork返回新创建子进程的进程ID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值;
zygote已经创建了一个Socket服务端,而这个服务端是不应该被新进程使用的,否则系统中会有多个进程接收Socket客户端的命令。因此,新进程被创建好后,首先需要在新进程中关闭该Socket服务端,并调用新进程中指定的Class文件的main()函数作为新进程的入口点。而这些正是在调用forkAndSpecialize()函数后根据返回值pid完成的。