Zygote是做什么的
Zygote簡(jiǎn)單地說(shuō)主要作用是兩方面:
- 啟動(dòng)SystemServer
- 孵化應(yīng)用進(jìn)程
Android中大多數(shù)應(yīng)用進(jìn)程和系統(tǒng)進(jìn)程都是通過(guò)Zygote進(jìn)程來(lái)生成。Zygote為孵化的應(yīng)用程序提供了幾個(gè)基礎(chǔ)資源:
- 常用類(lèi) :Android的Java 類(lèi)庫(kù), 大部分來(lái)自于 Apache Hamony, 開(kāi)源的Java API 實(shí)現(xiàn),如 java.lang, java.util, java.net
- JNI函數(shù)
- 主題資源 : 比如theme圖片, 占用10M+的內(nèi)存空間,這些內(nèi)存是系統(tǒng)共享的
- 共享庫(kù):
Zygote進(jìn)程啟動(dòng)
Zygote由init進(jìn)程解析init.rc腳本啟動(dòng)的?,F(xiàn)在機(jī)器分為32位和64位,Zygote的啟動(dòng)也要區(qū)分對(duì)待:
/system/core/rootdir/init.rc
import /init.${ro.zygote}.rc
根據(jù)系統(tǒng)屬性ro.zygote的具體值,加載不同的描述Zygote的rc腳本。譬如firely rk3399包含的文件:
init.zygote32.rc
init.zygote32_64.rc
init.zygote64.rc
init.zygote64_32.rc
其中zygote32和zygote64分別對(duì)應(yīng)32位和64位機(jī)器。zygote32_64和zygote64_32則Primary Arch 和 Secondary Arch的組合,以init.zygote64.rc為例,相關(guān)腳本如下:
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
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
從路徑來(lái)看,Zygote所在的應(yīng)用程序名稱叫"app_process64",
- zygote 就是service的名稱
- /system/bin/app_process64 應(yīng)用程序路徑,即Zygote所在的應(yīng)用進(jìn)程
- -Xzygote /system/bin --zygote --start-system-server 為傳遞給app_process的參數(shù),后面的分析會(huì)看到參數(shù)--zygote被用來(lái) app_process 啟動(dòng)Zygote的選項(xiàng), --start-system-server 會(huì)被作為參數(shù)傳遞給Zygote的ZygoteInit.
可以簡(jiǎn)單地用下面這副圖描述Zygote的啟動(dòng)

Zygote啟動(dòng)時(shí)都做了什么
Zygote啟動(dòng)主要經(jīng)歷了兩部分:
- native世界
- java世界
native世界
從Zygote的rc腳本我們知道, Zygote是通過(guò)app_process啟動(dòng),入口就是app_process的main函數(shù)
frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// Parse runtime arguments. Stop at first unrecognized option.
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName;
String8 className;
++i; // Skip unused "parent dir" argument.
while (i < argc) {
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName.setTo(arg + 12);
} else if (strncmp(arg, "--", 2) != 0) {
className.setTo(arg);
break;
} else {
--i;
break;
}
}
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
}
這里有3點(diǎn),
- 創(chuàng)建了AndroidRuntime對(duì)象, 這里面主要的動(dòng)作就是啟動(dòng)虛擬機(jī)
- 解析傳進(jìn)來(lái)的參數(shù) . 這個(gè)場(chǎng)景中--zygote 指定了app_process接下來(lái)將啟動(dòng)"ZygoteInit", 并傳入-start-system-server
- 在虛擬機(jī)中運(yùn)行ZygoteInit , ZygoteInit是java寫(xiě)的,即這一步Zygote就從native世界進(jìn)入到了java世界
簡(jiǎn)單來(lái)說(shuō),Zygote在native世界做的主要是以下幾步:
- 啟動(dòng)Android虛擬機(jī)
- 注冊(cè)Android的JNI函數(shù)
- 進(jìn)入java世界
java世界
Zygote的java世界入口是ZygoteInit 的main函數(shù)
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
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) {
preload(bootTimingsTraceLog); //預(yù)加載各類(lèi)資源
} else {
Zygote.resetNicePriority();
}
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
// {@code r == null} in the parent (zygote) process, and {@code r != null} in the
// child (system_server) process.
if (r != null) {
r.run();
return;
}
}
caller = zygoteServer.runSelectLoop(abiList);
}
ZygoteInit的主函數(shù)主要完成幾件事情:
- 注冊(cè)一個(gè)socket
Zygote 作為孵化器,跟其他進(jìn)程間的通訊不是通過(guò)binder而是通過(guò)socket。一旦有新進(jìn)程需要運(yùn)行,系統(tǒng)會(huì)通過(guò)這個(gè)Socket(完整的名稱為ANDROID_SOCKET_zygote)跟Zygote通訊,由zygote完成進(jìn)程孵化過(guò)程 - 預(yù)加載各類(lèi)資源
函數(shù)preload用于加載虛擬機(jī)運(yùn)行時(shí)所需的各類(lèi)資源 - 啟動(dòng)System Server
- 進(jìn)入Loop循環(huán)
其他問(wèn)題點(diǎn)
為何用socket而不是binder
Zygote是通過(guò)fork來(lái)創(chuàng)建新進(jìn)程的,而binder是多線程的,有可能造成死鎖。
在 POSIX 標(biāo)準(zhǔn)中,fork 的行為是這樣的:復(fù)制整個(gè)用戶空間的數(shù)據(jù)(通常使用 copy-on-write 的策略,所以可以實(shí)現(xiàn)的速度很快)以及所有系統(tǒng)對(duì)象, 然后僅復(fù)制當(dāng)前線程到子進(jìn)程。這里:所有父進(jìn)程中別的線程,到了子進(jìn)程中都是突然蒸發(fā)掉的。
假如父進(jìn)程在獲取到鎖的情況下,fork了一個(gè)子進(jìn)程。子進(jìn)程的內(nèi)存中,這個(gè)鎖的狀態(tài)是上鎖狀態(tài)。子進(jìn)程僅運(yùn)行了fork所在的這個(gè)線程,其它線程沒(méi)有運(yùn)行,當(dāng)它嘗試獲取鎖時(shí),就發(fā)生了死鎖
為何要通過(guò)Zygote來(lái)孵化程序,而不是由其他進(jìn)程直接創(chuàng)建
主要有兩個(gè)好處:
- 縮短應(yīng)用的啟動(dòng)時(shí)間
無(wú)論哪個(gè)app,所有的虛擬機(jī)都必須按照某種一樣的而且是確定的方式初始化。Zygote預(yù)加載了各種資源,創(chuàng)建了虛擬機(jī)。由Zygote創(chuàng)建的進(jìn)程能夠繼承這些資源,不用再重新創(chuàng)建。加載app的類(lèi)只是最后一步而已。 - 優(yōu)化共享內(nèi)存
所有虛擬機(jī)都是從Zygote fork出來(lái)的,所以特么能夠享受到由內(nèi)核實(shí)現(xiàn)的內(nèi)存共享的優(yōu)勢(shì)。比如Zygote預(yù)加載的各類(lèi)資源,比如theme主題圖片,所有的進(jìn)程都是共享的,在物理內(nèi)存中只需要保存一份。
Zygote這一設(shè)計(jì)有什么缺點(diǎn)
所有應(yīng)用進(jìn)程都是從同一個(gè)進(jìn)程fork出來(lái)的,這會(huì)有效地破壞地址空間布局隨機(jī)化,這一技術(shù)是對(duì)抗代碼注入攻擊的重要安全手段
參考
網(wǎng)絡(luò)課程 --剖析Framework面試 沖擊Android高級(jí)職位
最強(qiáng)Android書(shū)架構(gòu)大剖析
深入理解Android內(nèi)核設(shè)計(jì)思想
https://blog.csdn.net/boygrass/article/details/27103383