当前位置:   article > 正文

Android系统(244)---Zygote进程的启动流程_free proxy server android

free proxy server android

Android进程系列第二篇---Zygote进程的启动流程

 

内容预览.png

概述:

本文(基于Android O源码)主要讲解Zygote进程创建流程,线程容易创建,但进程的相关的东西都被系统很好的封装了,以至于进程的创建,很多人还是头一回。首先一张图来看看Zygote进程在系统中的地位。

Zygote的地位.png

Zygote进程又称受精卵进程,它由app_process启动,Zygote进程最大意义是作为一个Socket的Server端,接收着四面八方的进程创建请求,Android中所有的应用进程的创建都是一个应用进程通过Binder请求SystemServer进程,SystemServer进程发送socket消息给Zygote进程,统一由Zygote进程创建出来的。典型的C/S架构!!!。图中红色标注为Binder通信方式,蓝色标注为Socket通信方式。

话说为什么Android系统采用这种架构呢,为什么所有进程的创建都是由Zygote来做呢?原因有如下几点

  • Zygote进程在启动的时候会创建一个虚拟机实例,因此通过Zygote进程在父进程,创建的子进程都会继承这个虚拟机实例,App中的JAVA代码可以得到翻译执行。

  • 进程与进程之间需要跨进程通信,由Zygote进程作为父进程还可以获得一个Binder线程池,这样进程之间就可以使用Binder进行跨进程通信了。

  • 进程的“血液”可以理解成Message,启动四大组件靠的都是Looper消息机制,看过老罗的书,说由Zygote进程作为父进程,子进程可以获得一个消息循环。这句话我表示不理解,因为各个应用进程的Looper循环是自己在ActivityThread中创建的,SystemServer进程的消息循环也是自己创建的,那为什么说子进程可以继承Zygote进程的消息循环呢?这点我觉得不严谨,欢迎讨论。

所以这可以理解成Zygote进程作为所有应用进程的父进程的原因。

1、进入JAVA的世界,ZygoteInit的main方法

SystemServer进程的创建.png

 

这张序列图就是Zygote进程的创建流程,结合代码看一看。

  1. frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
  2. public static void main(String argv[]) {
  3. //1、创建ZygoteServer
  4. ZygoteServer zygoteServer = new ZygoteServer();
  5. .......
  6. try {
  7. .......
  8. boolean startSystemServer = false;
  9. String socketName = "zygote";
  10. String abiList = null;
  11. boolean enableLazyPreload = false;
  12. // 2、解析app_main.cpp传来的参数
  13. for (int i = 1; i < argv.length; i++) {
  14. if ("start-system-server".equals(argv[i])) {
  15. startSystemServer = true;
  16. } else if ("--enable-lazy-preload".equals(argv[i])) {
  17. enableLazyPreload = true;
  18. } else if (argv[i].startsWith(ABI_LIST_ARG)) {
  19. abiList = argv[i].substring(ABI_LIST_ARG.length());
  20. } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
  21. socketName = argv[i].substring(SOCKET_NAME_ARG.length());
  22. } else {
  23. throw new RuntimeException("Unknown command line argument: " + argv[i]);
  24. }
  25. }
  26. if (abiList == null) {
  27. throw new RuntimeException("No ABI list supplied.");
  28. }
  29. //3、创建一个Server端的Socket
  30. zygoteServer.registerServerSocket(socketName);
  31. // In some configurations, we avoid preloading resources and classes eagerly.
  32. // In such cases, we will preload things prior to our first fork.
  33. if (!enableLazyPreload) {
  34. bootTimingsTraceLog.traceBegin("ZygotePreload");
  35. EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
  36. SystemClock.uptimeMillis());
  37. //4、加载进程的资源和类
  38. preload(bootTimingsTraceLog);
  39. EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
  40. SystemClock.uptimeMillis());
  41. bootTimingsTraceLog.traceEnd(); // ZygotePreload
  42. } else {
  43. Zygote.resetNicePriority();
  44. }
  45. ........
  46. if (startSystemServer) {
  47. //5、开启SystemServer进程,这是受精卵进程的第一次分裂
  48. startSystemServer(abiList, socketName, zygoteServer);
  49. }
  50. Log.i(TAG, "Accepting command socket connections");
  51. //6、启动一个死循环监听来自Client端的消息
  52. zygoteServer.runSelectLoop(abiList);
  53. //7、关闭SystemServer的Socket
  54. zygoteServer.closeServerSocket();
  55. } catch (Zygote.MethodAndArgsCaller caller) {
  56. //8、这里捕获这个异常调用MethodAndArgsCaller的run方法。
  57. caller.run();
  58. } catch (Throwable ex) {
  59. Log.e(TAG, "System zygote died with exception", ex);
  60. zygoteServer.closeServerSocket();
  61. throw ex;
  62. }
  63. }

ZygoteInit的main方法大概就做了上面六件事情,一,创建ZygoteServer,在Android O上把与Socket的操作都封装到了ZygoteServer类中;二,解析app_main.cpp传来的参数。三、创建一个Server端的Socket,作用是当Zygote进程将SystemServer进程启动后,就会在这个Socket上来等待ActivityManagerService请求,即请求创建我们自己APP应用程序进程;四,预加载类和资源,包括颜色啊,R文件,drawable、类等;五,启动system_server进程,这是上层framework的运行载体,ActivityManagerService就是运行在这个进程里面的;六,开启一个循环,等待着接收ActivityManagerService的请求,随时待命,当接收到创建新进程的请求时立即唤醒并执行相应工作;

我觉得这段代码的主线是,ZygoteInit进程启动后,会注册一个Socket,在runSelectLoop方法中开启一个while死循环等待ActivityManagerService创建新进程的请求,其次,ZygoteInit启动了SystemServer进程,执行SystemServer的main方法。

这种模式其实可以理解成一个模板格式的代码,不信你在看看WebViewZygoteInit中的写法和ZygoteInit的写法是不是如出一辙呢?

  1. frameworks/base/core/java/com/android/internal/os/WebViewZygoteInit.java
  2. public static void main(String argv[]) {
  3. sServer = new WebViewZygoteServer();
  4. // Zygote goes into its own process group.
  5. try {
  6. Os.setpgid(0, 0);
  7. } catch (ErrnoException ex) {
  8. throw new RuntimeException("Failed to setpgid(0,0)", ex);
  9. }
  10. try {
  11. sServer.registerServerSocket("webview_zygote");
  12. sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS));
  13. sServer.closeServerSocket();
  14. } catch (Zygote.MethodAndArgsCaller caller) {
  15. caller.run();
  16. } catch (RuntimeException e) {
  17. Log.e(TAG, "Fatal exception:", e);
  18. }
  19. System.exit(0);
  20. }

如果要理解的更深一点,就需要再思考几个问题了。

  • 1、ZygoteInit方法是怎么调用的?
  • 2、Socket是怎么注册的?Socket通信的原理是否还记得?
  • 3、SystemServer进程资源如何加载?
  • 4、为什么要抛出MethodAndArgsCaller这个异常?作用是什么?

好,现在解答这些问题,这些问题弄懂,Zygote进程创建流程也就OK了。

1.1、ZygoteInit方法是怎么调用的

从上面的流程图中看到ZygoteInit的main是从app_main.cpp来的。app_main是Zygote进程对应的主文件,Zygote进程被Init启动的时候,就会调用这个app_main.cpp的main函数。

  1. /frameworks/base/cmds/app_process/app_main.cpp
  2. 192int main(int argc, char* const argv[])
  3. 193{
  4. ......
  5. 282 // Parse runtime arguments. Stop at first unrecognized option.
  6. 283 bool zygote = false;
  7. 284 bool startSystemServer = false;
  8. 285 bool application = false;
  9. 286 String8 niceName;
  10. 287 String8 className;
  11. 288
  12. 289 ++i; // Skip unused "parent dir" argument.
  13. //init.rc中会配置一些参数,这里进行比较设置一些变量走进不同的分支
  14. 290 while (i < argc) {
  15. 291 const char* arg = argv[i++];
  16. 292 if (strcmp(arg, "--zygote") == 0) {
  17. //启动的是Zygote进程
  18. 293 zygote = true;
  19. 294 niceName = ZYGOTE_NICE_NAME;
  20. 295 } else if (strcmp(arg, "--start-system-server") == 0) {
  21. //启动的是system-server进程
  22. 296 startSystemServer = true;
  23. 297 } else if (strcmp(arg, "--application") == 0) {
  24. 298 application = true;
  25. 299 } else if (strncmp(arg, "--nice-name=", 12) == 0) {
  26. 300 niceName.setTo(arg + 12);
  27. 301 } else if (strncmp(arg, "--", 2) != 0) {
  28. 302 className.setTo(arg);
  29. 303 break;
  30. 304 } else {
  31. 305 --i;
  32. 306 break;
  33. 307 }
  34. 308 }
  35. 309
  36. .......
  37. //设置一个“好听的名字” zygote,之前的名称是app_process
  38. 357 if (!niceName.isEmpty()) {
  39. 358 runtime.setArgv0(niceName.string(), true /* setProcName */);
  40. 359 }
  41. 360
  42. 361 if (zygote) {
  43. //通过runtime启动zygote
  44. 364 runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
  45. 365 } else if (className) {
  46. 366 runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
  47. 367 } else {
  48. 368 fprintf(stderr, "Error: no class name or --zygote supplied.\n");
  49. 369 app_usage();
  50. 370 LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
  51. 371 }
  52. 372}

Zygote本身是一个Native的应用程序,刚开始的名字为“app_process”,运行过程中,通过调用setArgv0将名字改为Zygote,真正启动的地方是runtime的start方法,简单看一下runtime的start方法。

  1. /*
  2. 987 * Start the Android runtime. This involves starting the virtual machine
  3. 988 * and calling the "static void main(String[] args)" method in the class
  4. 989 * named by "className".
  5. 990 *
  6. 991 * Passes the main function two arguments, the class name and the specified
  7. 992 * options string.
  8. 993 */
  9. 994void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
  10. 995{
  11. 996 ALOGD(">>>>>> START %s uid %d <<<<<<\n",
  12. 997 className != NULL ? className : "(unknown)", getuid());
  13. 998
  14. 1026 /* start the virtual machine */
  15. 1027 JniInvocation jni_invocation;
  16. 1028 jni_invocation.Init(NULL);
  17. 1029 JNIEnv* env;
  18. 1030 if (startVm(&mJavaVM, &env, zygote) != 0) {
  19. 1031 return;
  20. 1032 }
  21. 1033 onVmCreated(env);
  22. 1034
  23. 1035 /*
  24. 1036 * Register android functions.
  25. 1037 */
  26. 1038 if (startReg(env) < 0) {
  27. 1039 ALOGE("Unable to register all android natives\n");
  28. 1040 return;
  29. 1041 }
  30. 1042
  31. 1043 /*
  32. 1044 * We want to call main() with a String array with arguments in it.
  33. 1045 * At present we have two arguments, the class name and an option string.
  34. 1046 * Create an array to hold them.
  35. 1047 */
  36. 1048 jclass stringClass;
  37. 1049 jobjectArray strArray;
  38. 1050 jstring classNameStr;
  39. 1051
  40. 1052 stringClass = env->FindClass("java/lang/String");
  41. 1053 assert(stringClass != NULL);
  42. 1054 strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
  43. 1055 assert(strArray != NULL);
  44. 1056 classNameStr = env->NewStringUTF(className);
  45. 1057 assert(classNameStr != NULL);
  46. 1058 env->SetObjectArrayElement(strArray, 0, classNameStr);
  47. 1059
  48. 1060 for (size_t i = 0; i < options.size(); ++i) {
  49. 1061 jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
  50. 1062 assert(optionsStr != NULL);
  51. 1063 env->SetObjectArrayElement(strArray, i + 1, optionsStr);
  52. 1064 }
  53. 1065
  54. 1066 /*
  55. 1067 * Start VM. This thread becomes the main thread of the VM, and will
  56. 1068 * not return until the VM exits.
  57. 1069 */
  58. 1070 char* slashClassName = toSlashClassName(className);
  59. 1071 jclass startClass = env->FindClass(slashClassName);
  60. 1072 if (startClass == NULL) {
  61. 1073 ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
  62. 1074 /* keep going */
  63. 1075 } else {
  64. 1076 jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
  65. 1077 "([Ljava/lang/String;)V");
  66. 1078 if (startMeth == NULL) {
  67. 1079 ALOGE("JavaVM unable to find main() in '%s'\n", className);
  68. 1080 /* keep going */
  69. 1081 } else {
  70. 1082 env->CallStaticVoidMethod(startClass, startMeth, strArray);
  71. 1083
  72. 1084#if 0
  73. 1085 if (env->ExceptionCheck())
  74. 1086 threadExitUncaughtException(env);
  75. 1087#endif
  76. 1088 }
  77. 1089 }
  78. 1090 free(slashClassName);
  79. 1091 //这行Log比较常见,因为其他应用进程也是由zygote 进程fork 出来的,所有其他进程也包含这段代码,如果其他进程在java 层crash,那么也会走到这里
  80. 1092 ALOGD("Shutting down VM\n");
  81. 1093 if (mJavaVM->DetachCurrentThread() != JNI_OK)
  82. 1094 ALOGW("Warning: unable to detach main thread\n");
  83. 1095 if (mJavaVM->DestroyJavaVM() != 0)
  84. 1096 ALOGW("Warning: VM did not shut down cleanly\n");
  85. 1097}
  86. 1098

代码很简单,主要做了三件事情,一调用startVm开启虚拟机,二调用startReg注册JNI方法,三就是使用JNI把Zygote进程启动起来。

  1. 996 ALOGD(">>>>>> START %s uid %d <<<<<<\n",
  2. 997 className != NULL ? className : "(unknown)", getuid());

这个是进入Zygote进程的重要依据,开机的时候一般都会打印这一行Log。如

  1. 07-09 14:40:37.788 16504 16504 D AndroidRuntime: >>>>>> START com.android.internal.os.ZygoteInit uid 0 <<<<<<

如果遇到不能开机的情况,这行Log没有打开,极有可能不是上层的问题。

1.2、Socket是怎么注册的?

这个问题还用说嘛,看一下ZygoteServer类的registerServerSocket不就OK了吗,不要觉得这里很容易,其实彻底弄明白还是需要一些思考的。

  1. frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
  2. private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
  3. private LocalServerSocket mServerSocket;
  4. /**
  5. * Registers a server socket for zygote command connections
  6. *
  7. * @throws RuntimeException when open fails
  8. */
  9. void registerServerSocket(String socketName) {
  10. //看起来是用了一个单例
  11. if (mServerSocket == null) {
  12. int fileDesc;
  13. final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
  14. try {
  15. //从环境变量中获取名为ANDROID_SOCKET_zygote的fd
  16. String env = System.getenv(fullSocketName);
  17. fileDesc = Integer.parseInt(env);
  18. } catch (RuntimeException ex) {
  19. throw new RuntimeException(fullSocketName + " unset or invalid", ex);
  20. }
  21. try {
  22. //构建JAVA中的FD对象
  23. FileDescriptor fd = new FileDescriptor();
  24. fd.setInt$(fileDesc);
  25. //用上面的FD创建LocalServerSocket
  26. mServerSocket = new LocalServerSocket(fd);
  27. } catch (IOException ex) {
  28. throw new RuntimeException(
  29. "Error binding to local socket '" + fileDesc + "'", ex);
  30. }
  31. }
  32. }

其中 参数 socketName = "zygote";注册的过程实际上就是生成一个mServerSocket对象,用来接收Client端的请求,这里又有两个小问题了。

  • Socket FD是怎么生成的,在哪里创建?
  • Android中socket整体的通信框架是怎样的。

我们先看第二个问题,看看下面这张图。

Android Socket通信框架.png

LocalSocket就是作为客户端建立于服务端的连接,发送数据。LocalServerSocket作为服务端使用,建立服务端的socket监听客户端请求。典型的C/S架构!!!LocalServerSocket构造函数看到有两种方式:
第一种

  1. frameworks/base/core/java/android/net/LocalServerSocket.java
  2. /**
  3. * Creates a new server socket listening at specified name.
  4. * On the Android platform, the name is created in the Linux
  5. * abstract namespace (instead of on the filesystem).
  6. *
  7. * @param name address for socket
  8. * @throws IOException
  9. */
  10. public LocalServerSocket(String name) throws IOException
  11. {
  12. //1、创建服务端socket对象
  13. impl = new LocalSocketImpl();
  14. impl.create(LocalSocket.SOCKET_STREAM);
  15. //2、设置地址
  16. localAddress = new LocalSocketAddress(name);
  17. //3、绑定地址
  18. impl.bind(localAddress);
  19. //4、监听
  20. impl.listen(LISTEN_BACKLOG);
  21. }

第二种

  1. /**
  2. * Create a LocalServerSocket from a file descriptor that's already
  3. * been created and bound. listen() will be called immediately on it.
  4. * Used for cases where file descriptors are passed in via environment
  5. * variables
  6. *
  7. * @param fd bound file descriptor
  8. * @throws IOException
  9. */
  10. public LocalServerSocket(FileDescriptor fd) throws IOException
  11. {
  12. impl = new LocalSocketImpl(fd);
  13. impl.listen(LISTEN_BACKLOG);
  14. localAddress = impl.getSockAddress();
  15. }

从上面看到在Zygote中创建服务端的socket,使用的就是第二种。对于这种C/S架构,一般性的用法是这样子的。通常服务端会有个死循环不断响应客户端发送来的请求

  1. //创建socket并绑定监听 新创建的
  2. LocalServerSocket server = new LocalServerSocket(SOCKET_ADDRESS);
  3. while (true) {
  4.   //等待建立连接
  5.   LocalSocket receiver = server.accept();
  6.   //接收获取数据流
  7.   InputStream input = receiver.getInputStream();
  8.   
  9.   ……
  10. }

然后,客户端就可以用下面代码向服务端发送请求

  1. String message;
  2. //创建socket
  3. LocalSocket sender = new LocalSocket();
  4. //建立对应地址连接
  5. sender.connect(new LocalSocketAddress(SOCKET_ADDRESS));
  6. //发送写入数据
  7. sender.getOutputStream().write(message.getBytes());
  8. //关闭socket
  9. sender.getOutputStream().close();

在系统创建进程的时候,为了更好的管理LocalSocket的输入流和输出流对象,将这个过程的返回结果封装成了ZygoteState,都在ZygoteProcess的connect方法中。

  1. frameworks/base/core/java/android/os/ZygoteProcess.java
  2. public static ZygoteState connect(String socketAddress) throws IOException {
  3. DataInputStream zygoteInputStream = null;
  4. BufferedWriter zygoteWriter = null;
  5. final LocalSocket zygoteSocket = new LocalSocket();
  6. try {
  7. zygoteSocket.connect(new LocalSocketAddress(socketAddress,
  8. LocalSocketAddress.Namespace.RESERVED));
  9. zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
  10. zygoteWriter = new BufferedWriter(new OutputStreamWriter(
  11. zygoteSocket.getOutputStream()), 256);
  12. } catch (IOException ex) {
  13. try {
  14. zygoteSocket.close();
  15. } catch (IOException ignore) {
  16. }
  17. throw ex;
  18. }
  19. String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
  20. Log.i("Zygote", "Process: zygote socket " + socketAddress + " opened, supported ABIS: "
  21. + abiListString);
  22. return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
  23. Arrays.asList(abiListString.split(",")));
  24. }

好,上面基本就是Android中socket通信框架。主要把握LocalSocket与LocalServerSocket的用法。现在继续研究第一个问题,FD是在哪里创建的呢,为什么可以从环境变量中获取呢。Socket的监听方式为使用Linux系统调用select()函数监听Socket文件描述符,当该文件描述符上有数据时,自动触发中断,在中断处理函数中去读取文件描述符中的数据。

在关机状态下,我们可以看到dev/socket的文件有

  1. android:/dev/socket # ls -la
  2. total 0
  3. drwxr-xr-x 2 root root 160 1970-12-23 09:50 .
  4. drwxr-xr-x 14 root root 3440 1970-12-23 09:49 ..
  5. srw-rw---- 1 system system 0 1970-12-23 09:50 adbd
  6. srw-rw-rw- 1 root root 0 1970-12-23 09:49 property_service
  7. srw-rw---- 1 system system 0 1970-12-23 09:49 thermal-recv-client
  8. srw-rw-rw- 1 system system 0 1970-12-23 09:49 thermal-recv-passive-client
  9. srw-rw-rw- 1 system system 0 1970-12-23 09:49 thermal-send-client
  10. srw-rw---- 1 system system 0 1970-12-23 09:49 thermal-send-rule

开机状态下呢

  1. jason:/dev/socket # ls -la
  2. total 0
  3. drwxr-xr-x 7 root root 840 2018-07-09 19:24 .
  4. drwxr-xr-x 14 root root 3660 1970-12-23 06:29 ..
  5. srw-rw---- 1 system system 0 2018-07-09 19:24 adbd
  6. srw-rw-rw- 1 system system 0 1970-12-23 06:29 audio_hw_socket
  7. srw-rw---- 1 root mount 0 1970-12-23 06:28 cryptd
  8. srw-rw---- 1 root inet 0 2018-07-09 16:05 dnsproxyd
  9. srw-rw---- 1 root system 0 2018-07-09 16:05 dpmd
  10. srw-rw---- 1 root inet 0 2018-07-09 16:05 dpmwrapper
  11. srw-rw---- 1 root inet 0 2018-07-09 16:05 fwmarkd
  12. srw-rw---- 1 system radio 0 2018-07-09 16:05 ims_datad
  13. srw-rw---- 1 system radio 0 1970-12-23 06:29 ims_qmid
  14. srw-rw---- 1 radio radio 0 2018-07-09 16:05 ipacm_log_file
  15. srw-rw---- 1 system system 0 1970-12-23 06:29 lmkd
  16. srw-rw-rw- 1 logd logd 0 1970-12-23 06:28 logd
  17. srw-rw-rw- 1 logd logd 0 1970-12-23 06:28 logdr
  18. s-w--w--w- 1 logd logd 0 1970-12-23 06:28 logdw
  19. srw-rw---- 1 root system 0 2018-07-09 16:05 mdns
  20. srw-rw-rw- 1 gps gps 0 2018-07-09 16:05 mlid
  21. srw-rw---- 1 root system 0 2018-07-09 16:05 netd
  22. drwxr-x--- 2 radio radio 60 2018-07-09 16:05 netmgr
  23. srw-rw---- 1 system system 0 1970-12-23 06:29 pps
  24. srw-rw-rw- 1 root root 0 1970-12-23 06:28 property_service
  25. drwxrws--- 2 media audio 40 1970-12-23 06:29 qmux_audio
  26. drwxrws--- 2 bluetooth bluetooth 40 1970-12-23 06:29 qmux_bluetooth
  27. drwxrws--- 2 gps gps 40 1970-12-23 06:29 qmux_gps
  28. drwxrws--- 2 radio radio 120 2018-07-09 16:05 qmux_radio
  29. srw-rw---- 1 radio system 0 2018-07-09 16:05 rild-debug2
  30. srw-rw---- 1 root radio 0 2018-07-09 16:05 rild2
  31. srw-rw-rw- 1 system system 0 2018-07-09 16:05 seempdw
  32. srw-rw---- 1 root inet 0 2018-07-09 16:05 tcm
  33. srw-rw---- 1 system system 0 1970-12-23 06:29 thermal-recv-client
  34. srw-rw-rw- 1 system system 0 1970-12-23 06:29 thermal-recv-passive-client
  35. srw-rw-rw- 1 system system 0 1970-12-23 06:29 thermal-send-client
  36. srw-rw---- 1 system system 0 1970-12-23 06:29 thermal-send-rule
  37. srw-rw-rw- 1 system system 0 2018-07-09 16:05 tombstoned_crash
  38. srw-rw-rw- 1 system system 0 2018-07-09 16:05 tombstoned_intercept
  39. srw-rw-rw- 1 system system 0 2018-07-09 16:05 tombstoned_java_trace
  40. srw-rw---- 1 root mount 0 1970-12-23 06:28 vold
  41. srw-rw---- 1 webview_zygote system 0 2018-07-09 16:05 webview_zygote
  42. srw-rw---- 1 wifi wifi 0 2018-07-09 16:05 wpa_wlan0
  43. srw-rw---- 1 root system 0 1970-12-23 06:29 zygote
  44. srw-rw---- 1 root system 0 1970-12-23 06:29 zygote_secondary

最后两行是不是多了一个zygote和zygote_secondary呢?这两个是怎么来的呢,这个就得从init.rc文件了,内核启动完成之后会去读取init.rc文件,启动开机需要启动的进程。

在Android5.0中,Zygote的启动发生了一些变化,以前直接放在init.rc中的代码块放到了单独的文件中,在init.rc中通过import的方式引入文件,如下:

import /init.${ro.zygote}.rc

所以init.rc并不是直接引入某个固定的文件,而是根据属性“ro.zygote”的内容来引入system/core/init/目录下不同的文件,这个目录下目前有Init.zygote64.rc,Init.zygote32.rc,Init.zygote32_64.rc,Init.zygot64_32.rc。与之对应的属性 ro.zygote 的值可为:zygote32、zygote64、zygote32_64、zygote64_32。

init.zygote32.rc:zygote 进程对应的执行程序是 app_process (纯 32bit 模式)
init.zygote64.rc:zygote 进程对应的执行程序是 app_process64 (纯 64bit 模式)
init.zygote32_64.rc:启动两个 zygote 进程 (名为 zygote 和 zygote_secondary),对应的执行程序分别是 app_process32 (主模式)、app_process64。
init.zygote64_32.rc:启动两个 zygote 进程 (名为 zygote 和 zygote_secondary),对应的执行程序分别是 app_process64 (主模式)、app_process32。

什么意思呢?举个例子看zygot64_32.rc。

  1. service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
  2. class main
  3. priority -20
  4. user root
  5. group root readproc
  6. socket zygote stream 660 root system
  7. onrestart write /sys/android_power/request_state wake
  8. onrestart write /sys/power/state on
  9. onrestart restart audioserver
  10. onrestart restart cameraserver
  11. onrestart restart media
  12. onrestart restart netd
  13. onrestart restart wificond
  14. writepid /dev/cpuset/foreground/tasks
  15. service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
  16. class main
  17. priority -20
  18. user root
  19. group root readproc
  20. socket zygote_secondary stream 660 root system
  21. onrestart restart zygote
  22. writepid /dev/cpuset/foreground/tasks

其中

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote

这行表示zygote进程以服务的方式启动,对应的native应用程序是/system/bin/app_process64,给这个zygote进程传递了5个参数,分别是-Xzygote,/system/bin,--zygote,--start-system-server,--socket-name=zygote。可以看到zygot64_32.rc里面定义了两个Zygote服务:zygote和zygote_secondary。zygote为主,zygote_secondary为辅。

  1. onrestart restart cameraserver
  2. onrestart restart media
  3. onrestart restart netd
  4. onrestart restart wificond

onrestart后面跟的Zygote重启需要执行的命令,audioserver,cameraserver,media,netd,wificond,当Zygote进程重启了,这些进程都会重启。

  socket zygote stream 660 root system

这行表示Zygote进程在启动过程中,会在dev/socket目录下创建一个Socket,权限为660,表示所有的用户都可以对他进行读写,当init 解析到这样一条语句,它将做下面两件事:

  • 调用 create_socket() (system/core/init/util.c), 创建一个Socket fd, 将这个fd 与某个文件(/dev/socket/xxx, xxx 就是上面列到的名字,比如,zygote) 绑定(bind), 根据init.rc 里面定义来设定相关的用户,组和权限。最后返回这个fd。
  • 将socket 名字(带‘ANDROID_SOCKET_'前缀)(比如 zygote) 和 fd 注册到init 进程的环境变量里,这样所有的其他进程(所有进程都是init的子进程)都可以通过 getenv(name)获取到这个fd.
  1. system/core/init/descriptors.cpp
  2. 45#define ANDROID_SOCKET_ENV_PREFIX "ANDROID_SOCKET_"
  3. 46#define ANDROID_SOCKET_DIR "/dev/socket"
  4. 50void DescriptorInfo::CreateAndPublish(const std::string& globalContext) const {
  5. 51 // Create
  6. 52 const std::string& contextStr = context_.empty() ? globalContext : context_;
  7. 53 int fd = Create(contextStr);
  8. 54 if (fd < 0) return;
  9. 55
  10. 56 // Publish
  11. 57 std::string publishedName = key() + name_;
  12. 58 std::for_each(publishedName.begin(), publishedName.end(),
  13. 59 [] (char& c) { c = isalnum(c) ? c : '_'; });
  14. 60
  15. 61 std::string val = android::base::StringPrintf("%d", fd);
  16. //将创建的socket 的fd 放入 环境变量:ANDROID_SOCKET_zygote 中,以便在zygote进程中,获取此socket的fd
  17. 62 add_environment(publishedName.c_str(), val.c_str());
  18. 63
  19. 64 // make sure we don't close on exec
  20. 65 fcntl(fd, F_SETFD, 0);
  21. 66}
  1. 80int SocketInfo::Create(const std::string& context) const {
  2. 81 int flags = ((type() == "stream" ? SOCK_STREAM :
  3. 82 (type() == "dgram" ? SOCK_DGRAM :
  4. 83 SOCK_SEQPACKET)));
  5. //创建名为zygote 的socket
  6. 84 return create_socket(name().c_str(), flags, perm(), uid(), gid(), context.c_str());
  7. 85}
  8. 86
  9. 87const std::string SocketInfo::key() const {
  10. 88 return ANDROID_SOCKET_ENV_PREFIX;
  11. 89}
  1. 123const std::string FileInfo::key() const {
  2. 124 return ANDROID_FILE_ENV_PREFIX;
  3. 125}

现在应该明白了registerServerSocket的来龙去脉了吧,尤其是这个socket的fd是怎么来的

  1. frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
  2. private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
  3. private LocalServerSocket mServerSocket;
  4. /**
  5. * Registers a server socket for zygote command connections
  6. *
  7. * @throws RuntimeException when open fails
  8. */
  9. void registerServerSocket(String socketName) {
  10. //看起来是用了一个单例
  11. if (mServerSocket == null) {
  12. int fileDesc;
  13. final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
  14. try {
  15. //从环境变量中获取名为ANDROID_SOCKET_zygote的fd
  16. String env = System.getenv(fullSocketName);
  17. fileDesc = Integer.parseInt(env);
  18. } catch (RuntimeException ex) {
  19. throw new RuntimeException(fullSocketName + " unset or invalid", ex);
  20. }
  21. try {
  22. //构建JAVA中的FD对象
  23. FileDescriptor fd = new FileDescriptor();
  24. fd.setInt$(fileDesc);
  25. //用上面的FD创建LocalServerSocket
  26. mServerSocket = new LocalServerSocket(fd);
  27. } catch (IOException ex) {
  28. throw new RuntimeException(
  29. "Error binding to local socket '" + fileDesc + "'", ex);
  30. }
  31. }
  32. }

总结:init进程中add环境变量,Zygote进程中get环境变量。

1.3、Zygote进程预加载资源

  • 何为预加载?
    android系统资源加载分两种方式,预加载和使用进程中加载。 预加载是指在zygote进程启动的时候就加载,这样系统只在zygote执行一次加载操作,所有APP用到该资源不需要再重新加载,减少资源加载时间,加快了应用启动速度,一般情况下,系统中App共享的资源会被列为预加载资源。
  • 预加载是什么原理?
    预加载的原理很简单,就是在zygote进程启动后将资源读取出来,保存到Resources一个全局静态变量中,下次读取系统资源的时候优先从静态变量中查找。主要代码在zygoteInit.java类中方法preloadResources(),主要代码如下:
  • 系统哪些资源被预加载了?
  1. /frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
  2. 124 static void preload(BootTimingsTraceLog bootTimingsTraceLog) {
  3. 125 Log.d(TAG, "begin preload");
  4. 126 bootTimingsTraceLog.traceBegin("BeginIcuCachePinning");
  5. 127 beginIcuCachePinning();
  6. 128 bootTimingsTraceLog.traceEnd(); // BeginIcuCachePinning
  7. 129 bootTimingsTraceLog.traceBegin("PreloadClasses");
  8. 130 preloadClasses();
  9. 131 bootTimingsTraceLog.traceEnd(); // PreloadClasses
  10. 132 bootTimingsTraceLog.traceBegin("PreloadResources");
  11. 133 preloadResources();
  12. 134 bootTimingsTraceLog.traceEnd(); // PreloadResources
  13. 135 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
  14. 136 preloadOpenGL();
  15. 137 Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
  16. 138 preloadSharedLibraries();
  17. 139 preloadTextResources();
  18. 140 // Ask the WebViewFactory to do any initialization that must run in the zygote process,
  19. 141 // for memory sharing purposes.
  20. 142 WebViewFactory.prepareWebViewInZygote();
  21. 143 endIcuCachePinning();
  22. 144 warmUpJcaProviders();
  23. 145 Log.d(TAG, "end preload");
  24. 146
  25. 147 sPreloadComplete = true;
  26. 148 }

系统哪些资源被预加载了?加载的有类,资源,共享库,

1.3.1 加载类----preloadClasses

preload方法中会调用会preloadClasses预加载一些类,这些类记录在[frameworks/base/preloaded-classes](http://androidxref.com/8.0.0_r4/xref/frameworks/base/preloaded-classes)文本文件里。大概有四千多个,下面列举一下。

  1. 32[Landroid.accounts.Account;
  2. 33[Landroid.animation.Animator;
  3. 34[Landroid.animation.Keyframe$FloatKeyframe;
  4. 35[Landroid.animation.Keyframe$IntKeyframe;
  5. 36[Landroid.animation.PropertyValuesHolder;
  6. 37[Landroid.app.LoaderManagerImpl;
  7. 38[Landroid.app.Notification$Action;
  8. 39[Landroid.app.NotificationChannel;
  9. 40[Landroid.app.RemoteInput;
  10. 41[Landroid.app.job.JobInfo$TriggerContentUri;
  11. 42[Landroid.bluetooth.BluetoothDevice;
  12. 43[Landroid.content.ContentProviderResult;
  13. 44[Landroid.content.ContentValues;
  14. 45[Landroid.content.Intent;
  15. 46[Landroid.content.UndoOwner;
  16. 47[Landroid.content.pm.ActivityInfo;
  17. 48[Landroid.content.pm.ConfigurationInfo;
  18. 49[Landroid.content.pm.FeatureGroupInfo;
  19. 50[Landroid.content.pm.FeatureInfo;
  20. 51[Landroid.content.pm.InstrumentationInfo;
  21. 52[Landroid.content.pm.PathPermission;
  22. 53[Landroid.content.pm.PermissionInfo;
  23. 54[Landroid.content.pm.ProviderInfo;
  24. 55[Landroid.content.pm.ServiceInfo;
  25. 56[Landroid.content.pm.Signature;
  26. 57[Landroid.content.res.Configuration;
  27. 58[Landroid.content.res.StringBlock;
  28. 59[Landroid.content.res.XmlBlock;
  29. 60[Landroid.database.CursorWindow;
  30. 61[Landroid.database.sqlite.SQLiteConnection$Operation;
  31. 62[Landroid.database.sqlite.SQLiteConnectionPool$AcquiredConnectionStatus;
  32. 63[Landroid.graphics.Bitmap$CompressFormat;
  33. 64[Landroid.graphics.Bitmap$Config;
  34. 65[Landroid.graphics.Bitmap;
  35. 66[Landroid.graphics.Canvas$EdgeType;
  36. 67[Landroid.graphics.ColorSpace$Model;
  37. 68[Landroid.graphics.ColorSpace$Named;
  38. 69[Landroid.graphics.ColorSpace;
  39. 70[Landroid.graphics.FontFamily;
  40. 71[Landroid.graphics.Interpolator$Result;

上面文件中列举的四千多个类都要通过Class.forName加载到系统中,生成字节码。

  1. 229 /**
  2. 230 * Performs Zygote process initialization. Loads and initializes
  3. 231 * commonly used classes.
  4. 232 *
  5. 233 * Most classes only cause a few hundred bytes to be allocated, but
  6. 234 * a few will allocate a dozen Kbytes (in one case, 500+K).
  7. 235 */
  8. 236 private static void preloadClasses() {
  9. 237 final VMRuntime runtime = VMRuntime.getRuntime();
  10. 238
  11. 239 InputStream is;
  12. 240 try {
  13. 241 is = new FileInputStream(PRELOADED_CLASSES);
  14. 242 } catch (FileNotFoundException e) {
  15. 243 Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
  16. 244 return;
  17. 266 droppedPriviliges = true;
  18. 267 }
  19. ........
  20. 274 try {
  21. 275 BufferedReader br = new BufferedReader(new InputStreamReader(is), 256);
  22. 278 int count = 0;
  23. 279 String line;
  24. 280 while ((line = br.readLine()) != null) {
  25. 281 // Skip comments and blank lines.
  26. 282 line = line.trim();
  27. //跳过文件中注释的部分
  28. 283 if (line.startsWith("#") || line.equals("")) {
  29. 284 continue;
  30. 285 }
  31. 286
  32. 287 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line);
  33. 288 try {
  34. 289 if (false) {
  35. 290 Log.v(TAG, "Preloading " + line + "...");
  36. 291 }
  37. 292 // Load and explicitly initialize the given class. Use
  38. 293 // Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups
  39. 294 // (to derive the caller's class-loader). Use true to force initialization, and
  40. 295 // null for the boot classpath class-loader (could as well cache the
  41. 296 // class-loader of this class in a variable).
  42. 297 Class.forName(line, true, null);
  43. 298 count++;
  44. 299 } catch (ClassNotFoundException e) {
  45. 300 Log.w(TAG, "Class not found for preloading: " + line);
  46. 301 } catch (UnsatisfiedLinkError e) {
  47. 302 Log.w(TAG, "Problem preloading " + line + ": " + e);
  48. 303 } catch (Throwable t) {
  49. 304 ........
  50. 314 }
  51. 315
  52. 316 Log.i(TAG, "...preloaded " + count + " classes in "
  53. 317 + (SystemClock.uptimeMillis()-startTime) + "ms.");
  54. 318 } catch (IOException e) {
  55. 319 Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
  56. 320 } finally {
  57. .....
  58. 339 }
  59. 340 }

1.3.2 加载资源----preloadResources

系统中有大量的资源可以直接被App所使用,比如一个颜色,一个drawble,这些都是通过preloadResources加载的。

  1. /frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
  2. 342 /**
  3. 343 * Load in commonly used resources, so they can be shared across
  4. 344 * processes.
  5. 345 *
  6. 346 * These tend to be a few Kbytes, but are frequently in the 20-40K
  7. 347 * range, and occasionally even larger.
  8. 348 */
  9. 349 private static void preloadResources() {
  10. 350 final VMRuntime runtime = VMRuntime.getRuntime();
  11. 351
  12. 352 try {
  13. 353 mResources = Resources.getSystem();
  14. 354 mResources.startPreloading();
  15. 355 if (PRELOAD_RESOURCES) {
  16. 356 Log.i(TAG, "Preloading resources...");
  17. 357
  18. 358 long startTime = SystemClock.uptimeMillis();
  19. //1、加载preloaded_drawables中定义的资源,这里面定义的基本是图片
  20. 359 TypedArray ar = mResources.obtainTypedArray(
  21. 360 com.android.internal.R.array.preloaded_drawables);
  22. 361 int N = preloadDrawables(ar);
  23. 362 ar.recycle();
  24. 363 Log.i(TAG, "...preloaded " + N + " resources in "
  25. 364 + (SystemClock.uptimeMillis()-startTime) + "ms.");
  26. 365
  27. 366 startTime = SystemClock.uptimeMillis();
  28. //2、加载preloaded_color_state_lists中定义的资源,这里面定义的基本是颜色
  29. 367 ar = mResources.obtainTypedArray(
  30. 368 com.android.internal.R.array.preloaded_color_state_lists);
  31. //如果是颜色资源,需要调用preloadColorStateLists加载,见下面的preloadColorStateLists代码的解释
  32. 369 N = preloadColorStateLists(ar);
  33. 370 ar.recycle();
  34. 371 Log.i(TAG, "...preloaded " + N + " resources in "
  35. 372 + (SystemClock.uptimeMillis()-startTime) + "ms.");
  36. 373
  37. //3、加载config_freeformWindowManagement中定义的资源,这里面定义的基本是图片
  38. 374 if (mResources.getBoolean(
  39. 375 com.android.internal.R.bool.config_freeformWindowManagement)) {
  40. 376 startTime = SystemClock.uptimeMillis();
  41. 377 ar = mResources.obtainTypedArray(
  42. 378 com.android.internal.R.array.preloaded_freeform_multi_window_drawables);
  43. 379 N = preloadDrawables(ar);
  44. 380 ar.recycle();
  45. 381 Log.i(TAG, "...preloaded " + N + " resource in "
  46. 382 + (SystemClock.uptimeMillis() - startTime) + "ms.");
  47. 383 }
  48. 384 }
  49. 385 mResources.finishPreloading();
  50. 386 } catch (RuntimeException e) {
  51. 387 Log.w(TAG, "Failure preloading resources", e);
  52. 388 }
  53. 389 }

preloaded_drawables、preloaded_color_state_lists、preloaded_freeform_multi_window_drawables都是在/frameworks/base/core/res/res/values/arrays.xml中定义的

  1. /frameworks/base/core/res/res/values/arrays.xml
  2. 20<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
  3. 21
  4. 22 <!-- Do not translate. These are all of the drawable resources that should be preloaded by
  5. 23 the zygote process before it starts forking application processes. -->
  6. 24 <array name="preloaded_drawables">
  7. 25 <item>@drawable/ab_share_pack_material</item>
  8. 26 <item>@drawable/ab_solid_shadow_material</item>
  9. 27 <item>@drawable/action_bar_item_background_material</item>
  10. 28 <item>@drawable/activated_background_material</item>
  11. ......
  12. 138 <item>@drawable/toast_frame</item>
  13. 139 </array>
  14. 141 <!-- Do not translate. These are all of the color state list resources that should be
  15. 142 preloaded by the zygote process before it starts forking application processes. -->
  16. 143 <array name="preloaded_color_state_lists">
  17. 144 <item>@color/primary_text_dark</item>
  18. 145 <item>@color/primary_text_dark_disable_only</item>
  19. 146 <item>@color/primary_text_dark_nodisable</item>
  20. .......
  21. 186 <item>@color/search_url_text_material_light</item>
  22. 187 </array>
  23. 189 <array name="preloaded_freeform_multi_window_drawables">
  24. 190 <item>@drawable/decor_maximize_button_dark</item>
  25. 191 <item>@drawable/decor_maximize_button_light</item>
  26. 192 </array>

对于颜色资源,需要调用preloadColorStateLists来加载

  1. /frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
  2. 391 private static int preloadColorStateLists(TypedArray ar) {
  3. 392 int N = ar.length();
  4. 393 for (int i=0; i<N; i++) {
  5. 394 int id = ar.getResourceId(i, 0);
  6. 395 if (false) {
  7. 396 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
  8. 397 }
  9. 398 if (id != 0) {
  10. 399 if (mResources.getColorStateList(id, null) == null) {
  11. 400 throw new IllegalArgumentException(
  12. 401 "Unable to find preloaded color resource #0x"
  13. 402 + Integer.toHexString(id)
  14. 403 + " (" + ar.getString(i) + ")");
  15. 404 }
  16. 405 }
  17. 406 }
  18. 407 return N;
  19. 408 }

对于preloadOpenGL、preloadSharedLibraries和preloadTextResources在此不一一分析了,原来ZygoteInit中的preload方法加载了这么多资源,这个也就是为什么开机慢而打开一个应用快的原因之一。我们能不能把加载资源的这些耗时操作放到子线程中做呢?

  1. 671 public static void main(String argv[]) {
  2. 672 ZygoteServer zygoteServer = new ZygoteServer();
  3. 673
  4. 674 // Mark zygote start. This ensures that thread creation will throw
  5. 675 // an error.
  6. 676 ZygoteHooks.startZygoteNoThreadCreation();
  7. 677
  8. .......
  9. 756 ZygoteHooks.stopZygoteNoThreadCreation();
  10. 757 }

禁止使用子线程.png

看到为防止多线程带来的同步问题,google这个地方禁止开启多线程。故网上有很多说法,尝试把这些代码放到子线程中去做应该是不对的(Android O)。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/227894
推荐阅读
相关标签
  

闽ICP备14008679号