概述:
本文(基于Android O源碼)主要講解Zygote進程創建流程,線程容易創建,但進程的相關的東西都被系統很好的封裝了,以至于進程的創建,很多人還是頭一回。首先一張圖來看看Zygote進程在系統中的地位。
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方法
這張序列圖就是Zygote進程的創建流程,結合代碼看一看。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
//1、創建ZygoteServer
ZygoteServer zygoteServer = new ZygoteServer();
.......
try {
.......
boolean startSystemServer = false;
String socketName = "zygote";
String abiList = null;
boolean enableLazyPreload = false;
// 2、解析app_main.cpp傳來的參數
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
} 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)) {
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.");
}
//3、創建一個Server端的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());
//4、加載進程的資源和類
preload(bootTimingsTraceLog);
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
bootTimingsTraceLog.traceEnd(); // ZygotePreload
} else {
Zygote.resetNicePriority();
}
........
if (startSystemServer) {
//5、開啟SystemServer進程,這是受精卵進程的第一次分裂
startSystemServer(abiList, socketName, zygoteServer);
}
Log.i(TAG, "Accepting command socket connections");
//6、啟動一個死循環監聽來自Client端的消息
zygoteServer.runSelectLoop(abiList);
//7、關閉SystemServer的Socket
zygoteServer.closeServerSocket();
} catch (Zygote.MethodAndArgsCaller caller) {
//8、這里捕獲這個異常調用MethodAndArgsCaller的run方法。
caller.run();
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
zygoteServer.closeServerSocket();
throw ex;
}
}
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的寫法是不是如出一轍呢?
frameworks/base/core/java/com/android/internal/os/WebViewZygoteInit.java
public static void main(String argv[]) {
sServer = new WebViewZygoteServer();
// 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 {
sServer.registerServerSocket("webview_zygote");
sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS));
sServer.closeServerSocket();
} catch (Zygote.MethodAndArgsCaller caller) {
caller.run();
} catch (RuntimeException e) {
Log.e(TAG, "Fatal exception:", e);
}
System.exit(0);
}
如果要理解的更深一點,就需要再思考幾個問題了。
- 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函數。
/frameworks/base/cmds/app_process/app_main.cpp
192int main(int argc, char* const argv[])
193{
......
282 // Parse runtime arguments. Stop at first unrecognized option.
283 bool zygote = false;
284 bool startSystemServer = false;
285 bool application = false;
286 String8 niceName;
287 String8 className;
288
289 ++i; // Skip unused "parent dir" argument.
//init.rc中會配置一些參數,這里進行比較設置一些變量走進不同的分支
290 while (i < argc) {
291 const char* arg = argv[i++];
292 if (strcmp(arg, "--zygote") == 0) {
//啟動的是Zygote進程
293 zygote = true;
294 niceName = ZYGOTE_NICE_NAME;
295 } else if (strcmp(arg, "--start-system-server") == 0) {
//啟動的是system-server進程
296 startSystemServer = true;
297 } else if (strcmp(arg, "--application") == 0) {
298 application = true;
299 } else if (strncmp(arg, "--nice-name=", 12) == 0) {
300 niceName.setTo(arg + 12);
301 } else if (strncmp(arg, "--", 2) != 0) {
302 className.setTo(arg);
303 break;
304 } else {
305 --i;
306 break;
307 }
308 }
309
.......
//設置一個“好聽的名字” zygote,之前的名稱是app_process
357 if (!niceName.isEmpty()) {
358 runtime.setArgv0(niceName.string(), true /* setProcName */);
359 }
360
361 if (zygote) {
//通過runtime啟動zygote
364 runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
365 } else if (className) {
366 runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
367 } else {
368 fprintf(stderr, "Error: no class name or --zygote supplied.\n");
369 app_usage();
370 LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
371 }
372}
Zygote本身是一個Native的應用程序,剛開始的名字為“app_process”,運行過程中,通過調用setArgv0將名字改為Zygote,真正啟動的地方是runtime的start方法,簡單看一下runtime的start方法。
/*
987 * Start the Android runtime. This involves starting the virtual machine
988 * and calling the "static void main(String[] args)" method in the class
989 * named by "className".
990 *
991 * Passes the main function two arguments, the class name and the specified
992 * options string.
993 */
994void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
995{
996 ALOGD(">>>>>> START %s uid %d <<<<<<\n",
997 className != NULL ? className : "(unknown)", getuid());
998
1026 /* start the virtual machine */
1027 JniInvocation jni_invocation;
1028 jni_invocation.Init(NULL);
1029 JNIEnv* env;
1030 if (startVm(&mJavaVM, &env, zygote) != 0) {
1031 return;
1032 }
1033 onVmCreated(env);
1034
1035 /*
1036 * Register android functions.
1037 */
1038 if (startReg(env) < 0) {
1039 ALOGE("Unable to register all android natives\n");
1040 return;
1041 }
1042
1043 /*
1044 * We want to call main() with a String array with arguments in it.
1045 * At present we have two arguments, the class name and an option string.
1046 * Create an array to hold them.
1047 */
1048 jclass stringClass;
1049 jobjectArray strArray;
1050 jstring classNameStr;
1051
1052 stringClass = env->FindClass("java/lang/String");
1053 assert(stringClass != NULL);
1054 strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
1055 assert(strArray != NULL);
1056 classNameStr = env->NewStringUTF(className);
1057 assert(classNameStr != NULL);
1058 env->SetObjectArrayElement(strArray, 0, classNameStr);
1059
1060 for (size_t i = 0; i < options.size(); ++i) {
1061 jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
1062 assert(optionsStr != NULL);
1063 env->SetObjectArrayElement(strArray, i + 1, optionsStr);
1064 }
1065
1066 /*
1067 * Start VM. This thread becomes the main thread of the VM, and will
1068 * not return until the VM exits.
1069 */
1070 char* slashClassName = toSlashClassName(className);
1071 jclass startClass = env->FindClass(slashClassName);
1072 if (startClass == NULL) {
1073 ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
1074 /* keep going */
1075 } else {
1076 jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
1077 "([Ljava/lang/String;)V");
1078 if (startMeth == NULL) {
1079 ALOGE("JavaVM unable to find main() in '%s'\n", className);
1080 /* keep going */
1081 } else {
1082 env->CallStaticVoidMethod(startClass, startMeth, strArray);
1083
1084#if 0
1085 if (env->ExceptionCheck())
1086 threadExitUncaughtException(env);
1087#endif
1088 }
1089 }
1090 free(slashClassName);
1091 //這行Log比較常見,因為其他應用進程也是由zygote 進程fork 出來的,所有其他進程也包含這段代碼,如果其他進程在java 層crash,那么也會走到這里
1092 ALOGD("Shutting down VM\n");
1093 if (mJavaVM->DetachCurrentThread() != JNI_OK)
1094 ALOGW("Warning: unable to detach main thread\n");
1095 if (mJavaVM->DestroyJavaVM() != 0)
1096 ALOGW("Warning: VM did not shut down cleanly\n");
1097}
1098
代碼很簡單,主要做了三件事情,一調用startVm開啟虛擬機,二調用startReg注冊JNI方法,三就是使用JNI把Zygote進程啟動起來。
996 ALOGD(">>>>>> START %s uid %d <<<<<<\n",
997 className != NULL ? className : "(unknown)", getuid());
這個是進入Zygote進程的重要依據,開機的時候一般都會打印這一行Log。如
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了嗎,不要覺得這里很容易,其實徹底弄明白還是需要一些思考的。
frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
private LocalServerSocket mServerSocket;
/**
* Registers a server socket for zygote command connections
*
* @throws RuntimeException when open fails
*/
void registerServerSocket(String socketName) {
//看起來是用了一個單例
if (mServerSocket == null) {
int fileDesc;
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try {
//從環境變量中獲取名為ANDROID_SOCKET_zygote的fd
String env = System.getenv(fullSocketName);
fileDesc = Integer.parseInt(env);
} catch (RuntimeException ex) {
throw new RuntimeException(fullSocketName + " unset or invalid", ex);
}
try {
//構建JAVA中的FD對象
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc);
//用上面的FD創建LocalServerSocket
mServerSocket = new LocalServerSocket(fd);
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
}
其中 參數 socketName = "zygote";注冊的過程實際上就是生成一個mServerSocket對象,用來接收Client端的請求,這里又有兩個小問題了。
- Socket FD是怎么生成的,在哪里創建?
- Android中socket整體的通信框架是怎樣的。
我們先看第二個問題,看看下面這張圖。
LocalSocket就是作為客戶端建立于服務端的連接,發送數據。LocalServerSocket作為服務端使用,建立服務端的socket監聽客戶端請求。典型的C/S架構!!!LocalServerSocket構造函數看到有兩種方式:
第一種
frameworks/base/core/java/android/net/LocalServerSocket.java
/**
* Creates a new server socket listening at specified name.
* On the Android platform, the name is created in the Linux
* abstract namespace (instead of on the filesystem).
*
* @param name address for socket
* @throws IOException
*/
public LocalServerSocket(String name) throws IOException
{
//1、創建服務端socket對象
impl = new LocalSocketImpl();
impl.create(LocalSocket.SOCKET_STREAM);
//2、設置地址
localAddress = new LocalSocketAddress(name);
//3、綁定地址
impl.bind(localAddress);
//4、監聽
impl.listen(LISTEN_BACKLOG);
}
第二種
/**
* Create a LocalServerSocket from a file descriptor that's already
* been created and bound. listen() will be called immediately on it.
* Used for cases where file descriptors are passed in via environment
* variables
*
* @param fd bound file descriptor
* @throws IOException
*/
public LocalServerSocket(FileDescriptor fd) throws IOException
{
impl = new LocalSocketImpl(fd);
impl.listen(LISTEN_BACKLOG);
localAddress = impl.getSockAddress();
}
從上面看到在Zygote中創建服務端的socket,使用的就是第二種。對于這種C/S架構,一般性的用法是這樣子的。通常服務端會有個死循環不斷響應客戶端發送來的請求
//創建socket并綁定監聽 新創建的
LocalServerSocket server = new LocalServerSocket(SOCKET_ADDRESS);
while (true) {
//等待建立連接
LocalSocket receiver = server.accept();
//接收獲取數據流
InputStream input = receiver.getInputStream();
……
}
然后,客戶端就可以用下面代碼向服務端發送請求
String message;
//創建socket
LocalSocket sender = new LocalSocket();
//建立對應地址連接
sender.connect(new LocalSocketAddress(SOCKET_ADDRESS));
//發送寫入數據
sender.getOutputStream().write(message.getBytes());
//關閉socket
sender.getOutputStream().close();
在系統創建進程的時候,為了更好的管理LocalSocket的輸入流和輸出流對象,將這個過程的返回結果封裝成了ZygoteState,都在ZygoteProcess的connect方法中。
frameworks/base/core/java/android/os/ZygoteProcess.java
public static ZygoteState connect(String socketAddress) throws IOException {
DataInputStream zygoteInputStream = null;
BufferedWriter zygoteWriter = null;
final LocalSocket zygoteSocket = new LocalSocket();
try {
zygoteSocket.connect(new LocalSocketAddress(socketAddress,
LocalSocketAddress.Namespace.RESERVED));
zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
zygoteWriter = new BufferedWriter(new OutputStreamWriter(
zygoteSocket.getOutputStream()), 256);
} catch (IOException ex) {
try {
zygoteSocket.close();
} catch (IOException ignore) {
}
throw ex;
}
String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
Log.i("Zygote", "Process: zygote socket " + socketAddress + " opened, supported ABIS: "
+ abiListString);
return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
Arrays.asList(abiListString.split(",")));
}
好,上面基本就是Android中socket通信框架。主要把握LocalSocket與LocalServerSocket的用法。現在繼續研究第一個問題,FD是在哪里創建的呢,為什么可以從環境變量中獲取呢。Socket的監聽方式為使用Linux系統調用select()函數監聽Socket文件描述符,當該文件描述符上有數據時,自動觸發中斷,在中斷處理函數中去讀取文件描述符中的數據。
在關機狀態下,我們可以看到dev/socket的文件有
android:/dev/socket # ls -la
total 0
drwxr-xr-x 2 root root 160 1970-12-23 09:50 .
drwxr-xr-x 14 root root 3440 1970-12-23 09:49 ..
srw-rw---- 1 system system 0 1970-12-23 09:50 adbd
srw-rw-rw- 1 root root 0 1970-12-23 09:49 property_service
srw-rw---- 1 system system 0 1970-12-23 09:49 thermal-recv-client
srw-rw-rw- 1 system system 0 1970-12-23 09:49 thermal-recv-passive-client
srw-rw-rw- 1 system system 0 1970-12-23 09:49 thermal-send-client
srw-rw---- 1 system system 0 1970-12-23 09:49 thermal-send-rule
開機狀態下呢
jason:/dev/socket # ls -la
total 0
drwxr-xr-x 7 root root 840 2018-07-09 19:24 .
drwxr-xr-x 14 root root 3660 1970-12-23 06:29 ..
srw-rw---- 1 system system 0 2018-07-09 19:24 adbd
srw-rw-rw- 1 system system 0 1970-12-23 06:29 audio_hw_socket
srw-rw---- 1 root mount 0 1970-12-23 06:28 cryptd
srw-rw---- 1 root inet 0 2018-07-09 16:05 dnsproxyd
srw-rw---- 1 root system 0 2018-07-09 16:05 dpmd
srw-rw---- 1 root inet 0 2018-07-09 16:05 dpmwrapper
srw-rw---- 1 root inet 0 2018-07-09 16:05 fwmarkd
srw-rw---- 1 system radio 0 2018-07-09 16:05 ims_datad
srw-rw---- 1 system radio 0 1970-12-23 06:29 ims_qmid
srw-rw---- 1 radio radio 0 2018-07-09 16:05 ipacm_log_file
srw-rw---- 1 system system 0 1970-12-23 06:29 lmkd
srw-rw-rw- 1 logd logd 0 1970-12-23 06:28 logd
srw-rw-rw- 1 logd logd 0 1970-12-23 06:28 logdr
s-w--w--w- 1 logd logd 0 1970-12-23 06:28 logdw
srw-rw---- 1 root system 0 2018-07-09 16:05 mdns
srw-rw-rw- 1 gps gps 0 2018-07-09 16:05 mlid
srw-rw---- 1 root system 0 2018-07-09 16:05 netd
drwxr-x--- 2 radio radio 60 2018-07-09 16:05 netmgr
srw-rw---- 1 system system 0 1970-12-23 06:29 pps
srw-rw-rw- 1 root root 0 1970-12-23 06:28 property_service
drwxrws--- 2 media audio 40 1970-12-23 06:29 qmux_audio
drwxrws--- 2 bluetooth bluetooth 40 1970-12-23 06:29 qmux_bluetooth
drwxrws--- 2 gps gps 40 1970-12-23 06:29 qmux_gps
drwxrws--- 2 radio radio 120 2018-07-09 16:05 qmux_radio
srw-rw---- 1 radio system 0 2018-07-09 16:05 rild-debug2
srw-rw---- 1 root radio 0 2018-07-09 16:05 rild2
srw-rw-rw- 1 system system 0 2018-07-09 16:05 seempdw
srw-rw---- 1 root inet 0 2018-07-09 16:05 tcm
srw-rw---- 1 system system 0 1970-12-23 06:29 thermal-recv-client
srw-rw-rw- 1 system system 0 1970-12-23 06:29 thermal-recv-passive-client
srw-rw-rw- 1 system system 0 1970-12-23 06:29 thermal-send-client
srw-rw---- 1 system system 0 1970-12-23 06:29 thermal-send-rule
srw-rw-rw- 1 system system 0 2018-07-09 16:05 tombstoned_crash
srw-rw-rw- 1 system system 0 2018-07-09 16:05 tombstoned_intercept
srw-rw-rw- 1 system system 0 2018-07-09 16:05 tombstoned_java_trace
srw-rw---- 1 root mount 0 1970-12-23 06:28 vold
srw-rw---- 1 webview_zygote system 0 2018-07-09 16:05 webview_zygote
srw-rw---- 1 wifi wifi 0 2018-07-09 16:05 wpa_wlan0
srw-rw---- 1 root system 0 1970-12-23 06:29 zygote
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。
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
其中
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為輔。
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
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.
system/core/init/descriptors.cpp
45#define ANDROID_SOCKET_ENV_PREFIX "ANDROID_SOCKET_"
46#define ANDROID_SOCKET_DIR "/dev/socket"
50void DescriptorInfo::CreateAndPublish(const std::string& globalContext) const {
51 // Create
52 const std::string& contextStr = context_.empty() ? globalContext : context_;
53 int fd = Create(contextStr);
54 if (fd < 0) return;
55
56 // Publish
57 std::string publishedName = key() + name_;
58 std::for_each(publishedName.begin(), publishedName.end(),
59 [] (char& c) { c = isalnum(c) ? c : '_'; });
60
61 std::string val = android::base::StringPrintf("%d", fd);
//將創建的socket 的fd 放入 環境變量:ANDROID_SOCKET_zygote 中,以便在zygote進程中,獲取此socket的fd
62 add_environment(publishedName.c_str(), val.c_str());
63
64 // make sure we don't close on exec
65 fcntl(fd, F_SETFD, 0);
66}
80int SocketInfo::Create(const std::string& context) const {
81 int flags = ((type() == "stream" ? SOCK_STREAM :
82 (type() == "dgram" ? SOCK_DGRAM :
83 SOCK_SEQPACKET)));
//創建名為zygote 的socket
84 return create_socket(name().c_str(), flags, perm(), uid(), gid(), context.c_str());
85}
86
87const std::string SocketInfo::key() const {
88 return ANDROID_SOCKET_ENV_PREFIX;
89}
123const std::string FileInfo::key() const {
124 return ANDROID_FILE_ENV_PREFIX;
125}
現在應該明白了registerServerSocket的來龍去脈了吧,尤其是這個socket的fd是怎么來的
frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
private LocalServerSocket mServerSocket;
/**
* Registers a server socket for zygote command connections
*
* @throws RuntimeException when open fails
*/
void registerServerSocket(String socketName) {
//看起來是用了一個單例
if (mServerSocket == null) {
int fileDesc;
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try {
//從環境變量中獲取名為ANDROID_SOCKET_zygote的fd
String env = System.getenv(fullSocketName);
fileDesc = Integer.parseInt(env);
} catch (RuntimeException ex) {
throw new RuntimeException(fullSocketName + " unset or invalid", ex);
}
try {
//構建JAVA中的FD對象
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc);
//用上面的FD創建LocalServerSocket
mServerSocket = new LocalServerSocket(fd);
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
}
總結:init進程中add環境變量,Zygote進程中get環境變量。
1.3、Zygote進程預加載資源
- 何為預加載?
android系統資源加載分兩種方式,預加載和使用進程中加載。 預加載是指在zygote進程啟動的時候就加載,這樣系統只在zygote執行一次加載操作,所有APP用到該資源不需要再重新加載,減少資源加載時間,加快了應用啟動速度,一般情況下,系統中App共享的資源會被列為預加載資源。
- 預加載是什么原理?
預加載的原理很簡單,就是在zygote進程啟動后將資源讀取出來,保存到Resources一個全局靜態變量中,下次讀取系統資源的時候優先從靜態變量中查找。主要代碼在zygoteInit.java類中方法preloadResources(),主要代碼如下: - 系統哪些資源被預加載了?
/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
124 static void preload(BootTimingsTraceLog bootTimingsTraceLog) {
125 Log.d(TAG, "begin preload");
126 bootTimingsTraceLog.traceBegin("BeginIcuCachePinning");
127 beginIcuCachePinning();
128 bootTimingsTraceLog.traceEnd(); // BeginIcuCachePinning
129 bootTimingsTraceLog.traceBegin("PreloadClasses");
130 preloadClasses();
131 bootTimingsTraceLog.traceEnd(); // PreloadClasses
132 bootTimingsTraceLog.traceBegin("PreloadResources");
133 preloadResources();
134 bootTimingsTraceLog.traceEnd(); // PreloadResources
135 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
136 preloadOpenGL();
137 Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
138 preloadSharedLibraries();
139 preloadTextResources();
140 // Ask the WebViewFactory to do any initialization that must run in the zygote process,
141 // for memory sharing purposes.
142 WebViewFactory.prepareWebViewInZygote();
143 endIcuCachePinning();
144 warmUpJcaProviders();
145 Log.d(TAG, "end preload");
146
147 sPreloadComplete = true;
148 }
系統哪些資源被預加載了?加載的有類,資源,共享庫,
1.3.1 加載類----preloadClasses
preload方法中會調用會preloadClasses預加載一些類,這些類記錄在[frameworks/base/preloaded-classes](http://androidxref.com/8.0.0_r4/xref/frameworks/base/preloaded-classes)文本文件里。大概有四千多個,下面列舉一下。
32[Landroid.accounts.Account;
33[Landroid.animation.Animator;
34[Landroid.animation.Keyframe$FloatKeyframe;
35[Landroid.animation.Keyframe$IntKeyframe;
36[Landroid.animation.PropertyValuesHolder;
37[Landroid.app.LoaderManagerImpl;
38[Landroid.app.Notification$Action;
39[Landroid.app.NotificationChannel;
40[Landroid.app.RemoteInput;
41[Landroid.app.job.JobInfo$TriggerContentUri;
42[Landroid.bluetooth.BluetoothDevice;
43[Landroid.content.ContentProviderResult;
44[Landroid.content.ContentValues;
45[Landroid.content.Intent;
46[Landroid.content.UndoOwner;
47[Landroid.content.pm.ActivityInfo;
48[Landroid.content.pm.ConfigurationInfo;
49[Landroid.content.pm.FeatureGroupInfo;
50[Landroid.content.pm.FeatureInfo;
51[Landroid.content.pm.InstrumentationInfo;
52[Landroid.content.pm.PathPermission;
53[Landroid.content.pm.PermissionInfo;
54[Landroid.content.pm.ProviderInfo;
55[Landroid.content.pm.ServiceInfo;
56[Landroid.content.pm.Signature;
57[Landroid.content.res.Configuration;
58[Landroid.content.res.StringBlock;
59[Landroid.content.res.XmlBlock;
60[Landroid.database.CursorWindow;
61[Landroid.database.sqlite.SQLiteConnection$Operation;
62[Landroid.database.sqlite.SQLiteConnectionPool$AcquiredConnectionStatus;
63[Landroid.graphics.Bitmap$CompressFormat;
64[Landroid.graphics.Bitmap$Config;
65[Landroid.graphics.Bitmap;
66[Landroid.graphics.Canvas$EdgeType;
67[Landroid.graphics.ColorSpace$Model;
68[Landroid.graphics.ColorSpace$Named;
69[Landroid.graphics.ColorSpace;
70[Landroid.graphics.FontFamily;
71[Landroid.graphics.Interpolator$Result;
上面文件中列舉的四千多個類都要通過Class.forName加載到系統中,生成字節碼。
229 /**
230 * Performs Zygote process initialization. Loads and initializes
231 * commonly used classes.
232 *
233 * Most classes only cause a few hundred bytes to be allocated, but
234 * a few will allocate a dozen Kbytes (in one case, 500+K).
235 */
236 private static void preloadClasses() {
237 final VMRuntime runtime = VMRuntime.getRuntime();
238
239 InputStream is;
240 try {
241 is = new FileInputStream(PRELOADED_CLASSES);
242 } catch (FileNotFoundException e) {
243 Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
244 return;
266 droppedPriviliges = true;
267 }
........
274 try {
275 BufferedReader br = new BufferedReader(new InputStreamReader(is), 256);
278 int count = 0;
279 String line;
280 while ((line = br.readLine()) != null) {
281 // Skip comments and blank lines.
282 line = line.trim();
//跳過文件中注釋的部分
283 if (line.startsWith("#") || line.equals("")) {
284 continue;
285 }
286
287 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line);
288 try {
289 if (false) {
290 Log.v(TAG, "Preloading " + line + "...");
291 }
292 // Load and explicitly initialize the given class. Use
293 // Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups
294 // (to derive the caller's class-loader). Use true to force initialization, and
295 // null for the boot classpath class-loader (could as well cache the
296 // class-loader of this class in a variable).
297 Class.forName(line, true, null);
298 count++;
299 } catch (ClassNotFoundException e) {
300 Log.w(TAG, "Class not found for preloading: " + line);
301 } catch (UnsatisfiedLinkError e) {
302 Log.w(TAG, "Problem preloading " + line + ": " + e);
303 } catch (Throwable t) {
304 ........
314 }
315
316 Log.i(TAG, "...preloaded " + count + " classes in "
317 + (SystemClock.uptimeMillis()-startTime) + "ms.");
318 } catch (IOException e) {
319 Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
320 } finally {
.....
339 }
340 }
1.3.2 加載資源----preloadResources
系統中有大量的資源可以直接被App所使用,比如一個顏色,一個drawble,這些都是通過preloadResources加載的。
/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
342 /**
343 * Load in commonly used resources, so they can be shared across
344 * processes.
345 *
346 * These tend to be a few Kbytes, but are frequently in the 20-40K
347 * range, and occasionally even larger.
348 */
349 private static void preloadResources() {
350 final VMRuntime runtime = VMRuntime.getRuntime();
351
352 try {
353 mResources = Resources.getSystem();
354 mResources.startPreloading();
355 if (PRELOAD_RESOURCES) {
356 Log.i(TAG, "Preloading resources...");
357
358 long startTime = SystemClock.uptimeMillis();
//1、加載preloaded_drawables中定義的資源,這里面定義的基本是圖片
359 TypedArray ar = mResources.obtainTypedArray(
360 com.android.internal.R.array.preloaded_drawables);
361 int N = preloadDrawables(ar);
362 ar.recycle();
363 Log.i(TAG, "...preloaded " + N + " resources in "
364 + (SystemClock.uptimeMillis()-startTime) + "ms.");
365
366 startTime = SystemClock.uptimeMillis();
//2、加載preloaded_color_state_lists中定義的資源,這里面定義的基本是顏色
367 ar = mResources.obtainTypedArray(
368 com.android.internal.R.array.preloaded_color_state_lists);
//如果是顏色資源,需要調用preloadColorStateLists加載,見下面的preloadColorStateLists代碼的解釋
369 N = preloadColorStateLists(ar);
370 ar.recycle();
371 Log.i(TAG, "...preloaded " + N + " resources in "
372 + (SystemClock.uptimeMillis()-startTime) + "ms.");
373
//3、加載config_freeformWindowManagement中定義的資源,這里面定義的基本是圖片
374 if (mResources.getBoolean(
375 com.android.internal.R.bool.config_freeformWindowManagement)) {
376 startTime = SystemClock.uptimeMillis();
377 ar = mResources.obtainTypedArray(
378 com.android.internal.R.array.preloaded_freeform_multi_window_drawables);
379 N = preloadDrawables(ar);
380 ar.recycle();
381 Log.i(TAG, "...preloaded " + N + " resource in "
382 + (SystemClock.uptimeMillis() - startTime) + "ms.");
383 }
384 }
385 mResources.finishPreloading();
386 } catch (RuntimeException e) {
387 Log.w(TAG, "Failure preloading resources", e);
388 }
389 }
preloaded_drawables、preloaded_color_state_lists、preloaded_freeform_multi_window_drawables都是在/frameworks/base/core/res/res/values/arrays.xml中定義的
/frameworks/base/core/res/res/values/arrays.xml
20<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
21
22 <!-- Do not translate. These are all of the drawable resources that should be preloaded by
23 the zygote process before it starts forking application processes. -->
24 <array name="preloaded_drawables">
25 <item>@drawable/ab_share_pack_material</item>
26 <item>@drawable/ab_solid_shadow_material</item>
27 <item>@drawable/action_bar_item_background_material</item>
28 <item>@drawable/activated_background_material</item>
......
138 <item>@drawable/toast_frame</item>
139 </array>
141 <!-- Do not translate. These are all of the color state list resources that should be
142 preloaded by the zygote process before it starts forking application processes. -->
143 <array name="preloaded_color_state_lists">
144 <item>@color/primary_text_dark</item>
145 <item>@color/primary_text_dark_disable_only</item>
146 <item>@color/primary_text_dark_nodisable</item>
.......
186 <item>@color/search_url_text_material_light</item>
187 </array>
189 <array name="preloaded_freeform_multi_window_drawables">
190 <item>@drawable/decor_maximize_button_dark</item>
191 <item>@drawable/decor_maximize_button_light</item>
192 </array>
對于顏色資源,需要調用preloadColorStateLists來加載
/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
391 private static int preloadColorStateLists(TypedArray ar) {
392 int N = ar.length();
393 for (int i=0; i<N; i++) {
394 int id = ar.getResourceId(i, 0);
395 if (false) {
396 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
397 }
398 if (id != 0) {
399 if (mResources.getColorStateList(id, null) == null) {
400 throw new IllegalArgumentException(
401 "Unable to find preloaded color resource #0x"
402 + Integer.toHexString(id)
403 + " (" + ar.getString(i) + ")");
404 }
405 }
406 }
407 return N;
408 }
對于preloadOpenGL、preloadSharedLibraries和preloadTextResources在此不一一分析了,原來ZygoteInit中的preload方法加載了這么多資源,這個也就是為什么開機慢而打開一個應用快的原因之一。我們能不能把加載資源的這些耗時操作放到子線程中做呢?
671 public static void main(String argv[]) {
672 ZygoteServer zygoteServer = new ZygoteServer();
673
674 // Mark zygote start. This ensures that thread creation will throw
675 // an error.
676 ZygoteHooks.startZygoteNoThreadCreation();
677
.......
756 ZygoteHooks.stopZygoteNoThreadCreation();
757 }
看到為防止多線程帶來的同步問題,google這個地方禁止開啟多線程。故網上有很多說法,嘗試把這些代碼放到子線程中去做應該是不對的(Android O)。
下一篇將在此基礎上梳理SystemServer進程的創建流程