接上篇: Android系統啓動流程(一)init進程的啓動流程java
Zygote在英語中是受精卵的意思,從這個名字能夠看出,zygote進程是用來孵化其餘進程的,SystemServer和其餘應用程序進程都是由Zygote進程所建立的。Zygote是以服務的形式存在於Android系統中的,是Android系統的一個重要的守護進程,下面咱們經過源碼來分析Zygote進程的啓動流程。linux
在init進程啓動時,會解析Zygote服務進程的啓動腳本並開啓Zygote進程,針對不一樣位數的操做系統,Zygote也分別對應不一樣的啓動腳本,在Android8.0系統的源碼中共有4個啓動腳本,分別是init.zygote32.rc(支持32位系統)、init.zygote64.rc(支持64位系統)、init.zygote32_64.rc(同時支持32位和64位,但以32位爲主)、init.zygote64_32.rc(同時支持32位和64位,但以64位爲主),咱們以init.zygote32.rc爲例來看一下Zygote服務的腳本源碼:android
目錄位置:\system\core\rootdir\init.zygote32.rcios
service zygote /system/bin/app_process -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
複製代碼
從上面的代碼能夠看出,該服務的名稱是zygote,路徑爲/system/bin/app_process,參數包含-Xzygote /system/bin --zygote --start-system-server,class的名稱爲main。c++
init進程在解析完上面的代碼後,會對zygote服務進行啓動,啓動部分的腳本代碼以下:數組
源碼位置:\system\core\rootdir\init.rcmarkdown
on zygote-start && property:ro.crypto.state=unencrypted //在.rc文件中,on表示一個觸發器,zygote-start是觸發器的名稱
//當該觸發器被觸發後,便會執行下面的命令
exec_start update_verifier_nonencrypted
start netd
start zygote //啓動zygote服務
start zygote_secondary
複製代碼
上面的代碼是定義在init.rc中的一個觸發器,當該觸發器被觸發後,便會執行start zygote這行命令,從而啓動zygote服務,start命令對應的函數爲do_start,源碼以下:app
源碼路徑:\system\core\init\builtins.cppsocket
static int do_start(const std::vector<std::string>& args) {
// 1.根據service的名稱找到該服務
Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
if (!svc) {
LOG(ERROR) << "do_start: Service " << args[1] << " not found";
return -1;
}
if (!svc->Start()) //2.調用Start方法開啓該服務
return -1;
return 0;
}
複製代碼
在註釋1處經過service的名稱來找到zygote這個服務的實例,而後再註釋2出調用Service的Start方法來開啓這個服務,咱們來看一下Start方法的源碼:函數
源碼路徑:\system\core\init\service.cpp
bool Service::Start() {
...
pid_t pid = -1;
if (namespace_flags_) {
pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
} else {
pid = fork(); // 1.經過fork函數建立zygote子進程
}
if (pid == 0) { //pid爲0,說明當前在子進程中
...
if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) { // 2.調用execve執行子進程的代碼
PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
}
_exit(127);
}
...
return true;
}
複製代碼
在註釋1處經過fork函數建立了一個子線程,因爲fork函數是對父進程的自我複製,因此fork函數會同時在父進程和子進程中返回,並在父進程中返回子進程的id,在子進程中返回0。
在註釋2處,經過調用execve函數來執行子進程的代碼,從zygote的啓動腳本中能夠看到,該服務的執行代碼位於 /system/bin/app_process中,對應的文件爲app_main.cpp,這樣程序即進入了app_main的main方法中。
咱們先來看一下app_main.cpp的main方法的源碼:
源碼路徑:\frameworks\base\cmds\app_process\app_main.cpp
int main(int argc, char* const argv[]) {
...
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));// 1.建立AppRuntime實例
...
while (i < argc) { // 2.循環遍歷參數
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) { //當參數爲「--zygote」時
zygote = true; //將zygote標記變爲true
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) { //當參數爲「--start-system-server」時
startSystemServer = true; //將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) {
// 3.若是zygote標誌爲true,則執行runtime的start方法
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
}
}
複製代碼
在註釋1處建立了一個AppRuntime實例。
在註釋2處對參數進行循環遍歷,若是參數中含有 "--zygote",則將zygote置爲true,若是參數中含有"--start-system-server",則將startSystemServer置爲true。
在註釋3處經過調用runtime的start方法來執行ZygoteInit文件中的代碼,並將ZygoteInit的文件路徑做爲參數傳入了start方法,這是一個java文件。start函數的源碼位於AppRuntime的父類AndroidRuntime中,源碼以下:
源碼路徑:\frameworks\base\core\jni\AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote) {
...
JniInvocation jni_invocation;
jni_invocation.Init(NULL);//初始化jni
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) != 0) { // 1.啓動java虛擬機
return;
}
onVmCreated(env);
//主要用於註冊jni函數
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
...
/* 2.經過jni的方式執行ZygoteInit的main方法*/
char* slashClassName = toSlashClassName(className); //將classname中的"."替換爲「/」
jclass startClass = env->FindClass(slashClassName);//經過jni的方式加載ZygoteInit的java類
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V"); //找到ZygoteInit的main方法
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);//經過jni的方式調用ZygoteInit的mian方法
...
}
...
}
複製代碼
因爲ZygoteInit文件是由java代碼編寫的,所以咱們須要用jni的方法來執行ZygoteInit的main方法。
在註釋1處,經過startVm方法建立了java虛擬機。
在註釋2處,經過執行一系列的jni方法,最終調用了ZygoteInit的main方法。
咱們來看一下Zygote的main方法的源碼:
源碼路徑:\frameworks\base\core\java\com\android\internal\os\ZygoteInit.java
public static void main(String argv[]) {
ZygoteServer zygoteServer = new ZygoteServer(); //建立ZygoteServer實例
...
try {
...
zygoteServer.registerServerSocket(socketName); // 1.註冊ServerSocket
...
if (startSystemServer) {
startSystemServer(abiList, socketName, zygoteServer); // 2.啓動SystemServer
}
Log.i(TAG, "Accepting command socket connections");
zygoteServer.runSelectLoop(abiList); // 3.開啓事件循環,不斷監聽新的請求
zygoteServer.closeServerSocket();
} catch (Zygote.MethodAndArgsCaller caller) {
caller.run();
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
zygoteServer.closeServerSocket();
throw ex;
}
}
複製代碼
在main方法中,首先建立了一個ZygoteServer實例,而後在註釋1處,經過調用zygoteServer的registerServerSocket方法對ServerSocket進行了註冊。
在註釋2處調用startSystemServer方法開啓了SystemServer進程。
在註釋3處經過調用zygoteServer的runSelectLoop方法開啓了事件循環,這樣zygote進程就能夠持續監聽新的應用進程建立請求。
咱們先來看一下registerServerSocket方法的源碼:
源碼路徑:\frameworks\base\core\java\com\android\internal\os\ZygoteServer.java
private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
private LocalServerSocket mServerSocket;
void registerServerSocket(String socketName) {
if (mServerSocket == null) {
int fileDesc;
//經過拼接字符串獲得最終的Socket名稱,最後的結果爲「ANDROID_SOCKET_zygote」
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try {
String env = System.getenv(fullSocketName);
fileDesc = Integer.parseInt(env);
} catch (RuntimeException ex) {
throw new RuntimeException(fullSocketName + " unset or invalid", ex);
}
try {
FileDescriptor fd = new FileDescriptor();//建立文件描述符對象
fd.setInt$(fileDesc);
mServerSocket = new LocalServerSocket(fd);//建立ServerSocket對象
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
}
複製代碼
在registerServerSocket方法中,首先經過字符串拼接的方式得到了Socket的名稱,而後根據這個名稱建立了一個文件描述符對象。基於linux一切都是文件的思想,socket也被看做是一個文件,該文件描述符即用來表示該socket。
而後經過這個文件描述符建立了一個LocalServerSocket對象,經過名稱咱們即可以看出,這是一個運行於服務端的socket,它的做用即是用來監聽新的應用進程建立請求。
咱們再來看一下runSelectLoop方法:
void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
fds.add(mServerSocket.getFileDescriptor());
peers.add(null);
/* 1.開始無限循環,不斷監聽新的請求 */
while (true) {
/* 將fds中的數據轉移到pollFds數組中 */
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
}
try {
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
for (int i = pollFds.length - 1; i >= 0; --i) {
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {// 2.當數組中沒有未執行的任務時
ZygoteConnection newPeer = acceptCommandPeer(abiList);//不斷進行監聽,當有新請求時便會返回
//將這個鏈接請求放入數組中
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
boolean done = peers.get(i).runOnce(this);// 3.從peers中取出鏈接請求並執行
if (done) {
//執行完成後從數組中移除
peers.remove(i);
fds.remove(i);
}
}
}
}
}
複製代碼
在runSelectLoop方法中,首先建立了一個FileDescriptor數組和一個ZygoteConnection數組,他們用來存儲新接收到的請求。在註釋1處開啓了一個無限循環來不斷監聽新的請求,所以zygote進程在android系統的運行過程當中會一直存在,直到系統關閉。
在這個無限循環中,先將fds數組的數據轉移到了pollFds數組中,而後對pollFds數組進行了遍歷,當i==0時,說明數組中全部請求任務都已經執行完了,那麼調用acceptCommandPeer方法來獲取新的請求,acceptCommandPeer方法是一個阻塞方法,若是沒有新的鏈接請求,acceptCommandPeer會一直阻塞,直到有新的鏈接請求到來時,acceptCommandPeer纔會將這個新的請求返回。獲取到新的請求後便將這個請求放入peers和fds數組中。
當i不爲0時,說明數組中還存在未執行的請求,則將請求取出並調用runOnce方法來執行這個請求。
咱們先來看一下acceptCommandPeer方法的源碼:
private ZygoteConnection acceptCommandPeer(String abiList) {
try {
return createNewConnection(mServerSocket.accept(), abiList);//調用accept方法等待新的請求
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
}
}
protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList) throws IOException {
return new ZygoteConnection(socket, abiList); //建立ZygoteConnection實例
}
複製代碼
acceptCommandPeer的源碼很是簡單,就是就是調用accept方法等待新的請求,該方法會一直阻塞當前線程,直到有新的請求到來。
咱們再來看一下ZygoteConnection的runOnce方法:
源碼路徑:\frameworks\base\core\java\com\android\internal\os\ZygoteConnection.java
boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {
...
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
parsedArgs.appDataDir);
...
}
複製代碼
runOnce方法的代碼很長,咱們只看最關鍵的一句代碼,即調用forkAndSpecialize方法建立新的進程,該方法最終會調用native方法來fork新的應用程序進程。
以前咱們講過,zygote進程在啓動的時候會建立一個java虛擬機,而咱們的應用程序進程都是由zygote進程fork得來的,而fork的本質是對父進程的自我複製,所以全部的應用程序子進程也會得到一個複製而來的java虛擬機副本,這樣便無需在應用程序進程中單獨啓動java虛擬機了。