概述
我们都知道,Android系统存在着两个完全不同的世界:
1. Java世界,Google提供的SDK编写出来的程序大部分都是针对这个世界的。在这个世界中运行的程序都是基于Dalvik虚拟机的java程序。
2. Native世界,也就是用Native语言C或者C++开发的程序,例如采用NDK开发的程序。
于是便有了这个疑问:
Android系统是基于linux内核构建的,那么最早肯定是native世界,可java世界是什么时候开始被创建出来的呢?
带着这个疑问,我们今天就来剖析下zygote进程的实现原理。
zygote分析
zygote本身是一个Native的应用程序,在前面对init进程的分析中,我们知道,zygote是系统在刚开始初始化时,init进程根据init.rc配置文件fork出来的子进程,映像路径为/system/bin/app_process。所以,zygote最初的名字叫“app_process”,在程序运行过程中,app_process通过Linux下的prctl系统调用将自己的名字改成了“zygote”,所以我们通过ps命令看到的进程名是“zygote”。
Zygote的原型app_process所对应的源文件为frameworks\base\cmds\app_process\App_main.cpp。相应main函数如下:
int main(int argc, char* const argv[]) { ….. /* Init.rc中的zygote启动参数: -Xzygote /system/bin --zygote --start-system-server */ mArgC = argc; mArgV = argv; mArgLen = 0; for (int i=0; i<argc; i++) { mArgLen += strlen(argv[i]) + 1; } mArgLen--; // 定义一个AppRuntime的对象runtime AppRuntime runtime; const char* argv0 = argv[0]; argc--; argv++; // 添加vm启动参数 int i = runtime.addVmArguments(argc, argv); // 开始解析运行时参数 bool zygote = false; bool startSystemServer = false; bool application = false; const char* parentDir = NULL; const char* niceName = NULL; const char* className = NULL; while (i < argc) { const char* arg = argv[i++]; if (!parentDir) { parentDir = arg; // parentDir = /system/bin } else if (strcmp(arg, "--zygote") == 0) { zygote = true; // zygote确实也为true niceName = "zygote"; } else if (strcmp(arg, "--start-system-server") == 0) { startSystemServer = true; // 为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); // 通过prctl系统调用把自己的名字改成zygote set_process_name(niceName); } runtime.mParentDir = parentDir; if (zygote) { /* 调用runtime的start函数,startSystemServer为true 参数1:“com.android.internal.os.ZygoteInit” 参数2:“start-system-server” */ runtime.start("com.android.internal.os.ZygoteInit", startSystemServer ? "start-system-server" : ""); } else if (className) { …… } …… }
zygote的main函数主要做了两件事情:
1. app_process通过prctl系统调用把自己的名字改成zygote
2. AppRuntime通过start函数接着启动system_server
AppRuntime分析
接着来看看AppRuntime的实现。
class AppRuntime : public AndroidRuntime { …… }
AppRuntime是继承自AndroidRuntime的,主要重载了onVmCreated、onStarted、onZygoteInit和onExit函数。前面的代码runtime.start(…),这个start函数使用的是基类AndroidRuntime的start,我们来分析一下它。
// 参数1:“com.android.internal.os.ZygoteInit” // 参数2:“start-system-server” void AndroidRuntime::start(const char* className, const char* options) { …… // 设置环境变量ANDROID_ROOT为"/system" const char* rootDir = getenv("ANDROID_ROOT"); if (rootDir == NULL) { rootDir = "/system"; if (!hasDir("/system")) { LOG_FATAL("No root directory specified, and /android does not exist."); return; } setenv("ANDROID_ROOT", rootDir, 1); } /* start the virtual machine */ JNIEnv* env; if (startVm(&mJavaVM, &env) != 0) { return; } …… /* Register android functions. */ if (startReg(env) < 0) { ALOGE("Unable to register all android natives\n"); return; } /* * We want to call main() with a String array with arguments in it. * At present we have two arguments, the class name and an option string. * Create an array to hold them. */ 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. */ /* className本身为com.android.internal.os.ZygoteInit toSlashClassName函数将className的.转换成为/,于是 转换之后为:com/android/internal/os/ZygoteInit */ char* slashClassName = toSlashClassName(className); 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函数,此时将转到java世界开始执行 所以zygote进程相当于开创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"); }
上面的代码逻辑不难,但是要从native层进入到java层开始执行需要煞费一番苦心,我们一一来分析。
创建虚拟机 - startVm
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) { …… property_get("dalvik.vm.checkjni", propBuf, ""); if (strcmp(propBuf, "true") == 0) { checkJni = true; } else if (strcmp(propBuf, "false") != 0) { /* property is neither true nor false; fall back on kernel parameter */ property_get("ro.kernel.android.checkjni", propBuf, ""); if (propBuf[0] == '1') { checkJni = true; } } ….. /* * The default starting and maximum size of the heap. Larger * values should be specified in a product property override. */ strcpy(heapstartsizeOptsBuf, "-Xms"); property_get("dalvik.vm.heapstartsize", heapstartsizeOptsBuf+4, "4m"); opt.optionString = heapstartsizeOptsBuf; mOptions.add(opt); strcpy(heapsizeOptsBuf, "-Xmx"); property_get("dalvik.vm.heapsize", heapsizeOptsBuf+4, "16m"); opt.optionString = heapsizeOptsBuf; mOptions.add(opt); // Increase the main thread's interpreter stack size for bug 6315322. opt.optionString = "-XX:mainThreadStackSize=24K"; mOptions.add(opt); …… /* * Initialize the VM. * * The JavaVM* is essentially per-process, and the JNIEnv* is per-thread. * If this call succeeds, the VM is ready, and we can start issuing * JNI calls. */ if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) { ALOGE("JNI_CreateJavaVM failed\n"); goto bail; } result = 0; bail: free(stackTraceFile); return result; }
这个函数主要是根据init进程初始化的时候解析的属性配置文件得到的数据来进行初始化VM虚拟机,包括设置jni check、heap size等等属性。最后,通过JNI_CreateJavaVM函数创建虚拟机。代码注释中写的也很清楚了,返回值pJavaVM一般是针对整个进程的,而pEnv是针对每个线程的。
注册JNI函数– startReg
前面已经调用startVm函数来创建虚拟机,下一步则需要给整个虚拟机注册一些常用的JNI函数。正是因为后续java世界用到的一些函数是采用native方式实现的,所以才需要提前注册这些函数。
/* * Register android native functions with the VM. */ /*static*/ int AndroidRuntime::startReg(JNIEnv* env) { /* * 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.) */ 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); if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) { env->PopLocalFrame(NULL); return -1; } env->PopLocalFrame(NULL); 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) { return -1; } } return 0; }
上面的代码通过register_jni_procs函数注册JNI函数,其中gRegJNI是一个数组,如下:
static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_debug_JNITest), REG_JNI(register_com_android_internal_os_RuntimeInit), REG_JNI(register_android_os_SystemClock), REG_JNI(register_android_util_EventLog), REG_JNI(register_android_util_Log), REG_JNI(register_android_util_FloatMath), REG_JNI(register_android_text_format_Time), REG_JNI(register_android_content_AssetManager), REG_JNI(register_android_content_StringBlock), …… }
看第一个例子吧:
int register_android_debug_JNITest(JNIEnv* env) { return jniRegisterNativeMethods(env, "android/debug/JNITest", gMethods, NELEM(gMethods)); }
其实就是调用jniRegisterNativeMethods函数来注册android.debug.JNITest类的JNI函数。
进入Java世界
虚拟机创建完毕,JNI函数也注册好了,可以说是万事俱备只欠东风了。那么我们来看看,JAVA世界的入口到底在哪里呢?根据前面的分析可知,CallStaticVoidMethod函数最终会调用com.android.internal.os.ZygoteInit类的main函数,下面就来看看这个入口函数吧。
public static void main(String argv[]) { try { // Start profiling the zygote initialization. SamplingProfilerIntegration.start(); // 注册zygote用的socket registerZygoteSocket(); // 预加载资源和类 preload(); // Finish profiling the zygote initialization. SamplingProfilerIntegration.writeZygoteSnapshot(); // Do an initial gc to clean up after startup gc(); // Disable tracing so that forked processes do not inherit stale tracing tags from // Zygote. Trace.setTracingEnabled(false); …… if (argv[1].equals("start-system-server")) { startSystemServer(); } else if (!argv[1].equals("")) { throw new RuntimeException(argv[0] + USAGE_STRING); } runSelectLoop(); // 等待事件发生 closeServerSocket(); } catch (MethodAndArgsCaller caller) { caller.run(); // 这个很重要,后面会分析到 } catch (RuntimeException ex) { Log.e(TAG, "Zygote died with exception", ex); closeServerSocket(); throw ex; } }
建立IPC通信服务端– registerZygoteSocket
private static void registerZygoteSocket() { if (sServerSocket == null) { int fileDesc; try { /* private static final String ANDROID_SOCKET_ENV = "ANDROID_SOCKET_zygote"; */ 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 { sServerSocket = new LocalServerSocket( createFileDescriptor(fileDesc)); } catch (IOException ex) { throw new RuntimeException( "Error binding to local socket '" + fileDesc + "'", ex); } }
从环境变量中获得名为“ANDROID_SOCKET_zygote”的值作为socket的fd,那么这个ANDROID_SOCKET_zygote环境变量又是什么时候设置的呢?
还记得在init进程初始化的时候会去解析init.rc的service服务,并且最后会调用service_start去启动服务。那么service_start函数其中有如下一段代码,用来创建通信用的socket fd:
for (si = svc->sockets; si; si = si->next) { int socket_type = ( !strcmp(si->type, "stream") ? SOCK_STREAM : (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET)); /* create_socket其实就是往ANDROID_SOCKET_DIR中创建了一个文件,zygote进程为 /dev/socket/zygote,并返回文件fd */ int s = create_socket(si->name, socket_type, si->perm, si->uid, si->gid); if (s >= 0) { // zygote服务的si->name即socket名也为zygote publish_socket(si->name, s); } }
其中publish_socket函数就是设置ANDROID_SOCKET_zygote环境变量的地方:
static void publish_socket(const char *name, int fd) { /* #define ANDROID_SOCKET_ENV_PREFIX "ANDROID_SOCKET_" #define ANDROID_SOCKET_DIR "/dev/socket" */ char key[64] = ANDROID_SOCKET_ENV_PREFIX; char val[64]; // 拼接socket的名字,即ANDROID_SOCKET_zygote strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1, name, sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX)); snprintf(val, sizeof(val), "%d", fd); // 添加socket的fd值到环境变量 add_environment(key, val); /* make sure we don't close-on-exec */ fcntl(fd, F_SETFD, 0); }
知道了ANDROID_SOCKET_ENV的来龙去脉,那么我们接着回过头来看registerZygoteSocket函数,这个函数其实很简单,就是创建了一个服务端的socket,并赋值给全局变量sServerSocket。那么这个服务端的socket具体要接受什么消息,之后又需要处理什么事件呢?我们后面再来看。
预加载类、资源等
static void preload() { preloadClasses(); preloadResources(); preloadOpenGL(); }
先来看看类加载:
private static void preloadClasses() { final VMRuntime runtime = VMRuntime.getRuntime(); // private static final String PRELOADED_CLASSES = "preloaded-classes"; // 预加载类的信息存储在PRELOADED_CLASSES文件中 InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream( PRELOADED_CLASSES); if (is == null) { Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + "."); } else { …… 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; } try { …… // 通过Java反射来加载类,line为类名 Class.forName(line); …… count++; } catch (ClassNotFoundException e) { Log.w(TAG, "Class not found for preloading: " + line); } catch (Throwable t) { …… } } } catch (IOException e) { Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e); } finally { …… } } }
函数比较简单,就是读取preloaded-classes文件的内容,以行为单位。preloaded-classes文件的内容如下:
# Classes which are preloaded by com.android.internal.os.ZygoteInit. # Automatically generated by frameworks/base/tools/preload/WritePreloadedClassFile.java. # MIN_LOAD_TIME_MICROS=1250 # MIN_PROCESSES=10 android.R$styleable android.accounts.Account android.accounts.Account$1 android.accounts.AccountManager android.accounts.AccountManager$12 android.accounts.AccountManager$13 android.accounts.AccountManager$6 android.accounts.AccountManager$AmsTask android.accounts.AccountManager$AmsTask$1 android.accounts.AccountManager$AmsTask$Response android.accounts.AccountManagerFuture android.accounts.IaccountManager …… // 省略N行
preloaded-classes这个文件主要由frameworks\base\tools\preload工具自动生成的,工具会判断每个类加载的时间是否大于1250微妙,超过这个时间就会被写到preloaded-classes文件中,最后由zygote来预加载。它总共有1000多行的类,可见Zygote进程初始化的时候任务还是很繁重的,这也是导致Android系统启动慢的原因之一。
preloadResources和preloadOpenGL我也就不多做解释了,大体处理都差不多。
启动system_server
/** * Prepare the arguments and fork for the system server process. */ private static boolean startSystemServer() throws MethodAndArgsCaller, RuntimeException { long capabilities = posixCapabilitiesAsBits( 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_RESOURCE, OsConstants.CAP_SYS_TIME, OsConstants.CAP_SYS_TTY_CONFIG ); /* Hardcoded command line to start the system server */ String args[] = { "--setuid=1000", // #define AID_SYSTEM 1000 "--setgid=1000", // 1000 是系统属性 "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007", "--capabilities=" + capabilities + "," + capabilities, "--runtime-init", "--nice-name=system_server", // 进程名为system_server "com.android.server.SystemServer", // 类名 }; ZygoteConnection.Arguments parsedArgs = null; int pid; try { parsedArgs = new ZygoteConnection.Arguments(args); ZygoteConnection.applyDebuggerSystemProperty(parsedArgs); ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs); /* 进程启动参数都已经在上面硬编码了 最后调用fork启动system_server进程 */ 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) { // 子进程处理消息 handleSystemServerProcess(parsedArgs); } return true; }
系统初始化到这里之后,系统中已经有3个进程了,最开始的init,zygote和zygote分裂出来的system_server。system_server部门我们以后再看,接着看zygote的执行。
zygote等待请求– runSelectLoop
private static void runSelectLoop() throws MethodAndArgsCaller { ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>(); ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>(); FileDescriptor[] fdArray = new FileDescriptor[4]; // sServerSocket是之前在registerZygoteSocket创建的服务端socket 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--; } try { fdArray = fds.toArray(fdArray); /* selectReadable是一个native函数,内部调用select等待客户端 的连接,客户端连接上之后就会返回。 返回值: <0: 内部发生错误 =0: 该客户端第一次连接到服务端 >0: 客户端与服务端已经建立连接,并开始发送数据 */ 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) { /* 返回0,表明该客户端第一次请求服务端,服务端调用accept与客户 端建立连接。客户端在zygote中以ZygoteConnection对象表示。 */ ZygoteConnection newPeer = acceptCommandPeer(); peers.add(newPeer); fds.add(newPeer.getFileDesciptor()); } else { boolean done; /* 返回>0,表明发送数据的客户端的index,peers.get(index)取得 发送数据客户端的ZygoteConnection对象,之后调用runOnce 函数处理具体的请求。 */ done = peers.get(index).runOnce(); // 请求处理完成之后,移除与该客户端的连接 if (done) { peers.remove(index); fds.remove(index); } } } }
runSelectLoop函数的逻辑比较简单,主要有两点
1、 处理客户端的连接和请求。其中客户端在zygote进程中使用ZygoteConnection对象表示。
2、 客户的请求有ZygoteConnection的runOnce来处理。
Zygote总结
Zygote是在android系统中创建java世界的盘古,它创建了第一个java虚拟机。同时,它又是女娲,它成功的繁殖了framework的核心system_server进程。主要步骤如下:
1、 创建AppRuntime对象,并调用其start函数。之后zygote的核心初始化都由AppRuntime中。
2、 调用startVm创建java虚拟机,然后调用startReg来注册JNI函数。
3、 通过JNI调用com.android.internal.os.ZygoteInit类的main函数,从此进入了java世界。
4、 调用registerZygoteSocket创建可以响应子孙后代请求的socket。同时,zygote调用preload函数预加载了常用的类、资源等,为java世界添砖加瓦。
5、 调用startSystemServer函数分裂了一个子进程system_server来为java世界服务。
6、 Zygote完成了java世界的初创工作,便调用runSelectLoop来让自己沉沉的睡去。之后,如果收到子孙后代的请求,它便会醒来为他们工作。