前言 在Android中,zygote是整個系統創建新進程的核心進程。zygote進程在內部會先啟動Dalvik虛擬機,繼而載入一些必要的系統資源和系統類,最後進入一種監聽狀態。在之後的運作中,當其他系統模塊(比如 AMS)希望創建新進程時,只需向zygote進程發出請求,zygote進程監聽到該請 ...
前言
在Android中,zygote是整個系統創建新進程的核心進程。zygote進程在內部會先啟動Dalvik虛擬機,繼而載入一些必要的系統資源和系統類,最後進入一種監聽狀態。在之後的運作中,當其他系統模塊(比如 AMS)希望創建新進程時,只需向zygote進程發出請求,zygote進程監聽到該請求後,會相應地fork出新的進程,於是這個新進程在初生之時,就先天具有了自己的Dalvik虛擬機以及系統資源。
開篇
核心源碼
關鍵類 | 路徑 |
---|---|
init.rc | system/core/rootdir/init.rc |
init.cpp | system/core/init/init.cpp |
init.zygote64.rc | system/core/rootdir/init.zygote64.rc |
builtins.cpp | system/core/init/builtins.cpp |
service.cpp | system/core/init/service.cpp |
app_main.cpp | frameworks/base/cmds/app_process/app_main.cpp |
AndroidRuntime.cpp | frameworks/base/core/jni/AndroidRuntime.cpp |
JniInvocation.cpp | libnativehelper/JniInvocation.cpp |
ZygoteInit.java | frameworks/base/core/java/com/android/internal/os/ZygoteInit.java |
ZygoteServer.java | frameworks/base/core/java/com/android/internal/os/ZygoteServer.java |
Zygote簡介
在Android系統中,JavaVM(Java虛擬機)、應用程式進程以及運行系統的關鍵服務的SystemServer進程都是由Zygote進程來創建的,我們也將它稱為孵化器。它通過fock(複製進程)的形式來創建應用程式進程和SystemServer進程,由於Zygote進程在啟動時會創建JavaVM,因此通過fock而創建的應用程式進程和SystemServer進程可以在內部獲取一個JavaVM的實例拷貝。
Read The Fucking Code
Zygote觸發
在分析init進程時,我們知道init進程啟動後,會解析init.rc文件,然後創建和載入service欄位指定的進程。zygote進程就是以這種方式,被init進程載入的。
在system/core/rootdir/init.rc的開始部分,可以看到:
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc // ${ro.zygote}由廠商定義,與平臺相關
on early-init
# Set init and its forked children's oom_adj.
write /proc/1/oom_score_adj -1000
init.zygoteXX.rc
從之前分析的init篇中我們知道,在不同的平臺(32、64及64_32)上,init.rc將包含不同的zygote.rc文件。在system/core/rootdir目錄下,有init.zygote32_64.rc、init.zyote64.rc、 init.zyote32.rc、init.zygote64_32.rc。
✨ 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
為什麼要定義這麼多種情況呢?直接定義一個不就好了,這主要是因為Android 5.0以後開始支持64位程式,為了相容32位和64位才這樣定義。不同的zygote.rc內容大致相同,主要區別體現在啟動的是32位,還是64位的進程。init.zygote32_64.rc和init.zygote64_32.rc會啟動兩個進程,且存在主次之分。
這裡拿64位處理器為例,init.zygote64_32.rc的代碼如下所示:
// 進程名稱是zygote,運行的二進位文件在/system/bin/app_process64
// 啟動參數是 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
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 // 創建一個socket,名字叫zygote,以tcp形式
onrestart write /sys/android_power/request_state wake // onrestart 指當進程重啟時執行後面的命令
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 // 創建子進程時,向 /dev/cpuset/foreground/tasks 寫入pid
// 另一個service ,名字 zygote_secondary
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
start zygote
定義了service,肯定有地方調用 start zygote。在之前init解析的博客中,我們分析過init進程的啟動。init進程啟動的最後,會產生”late-init”事件。
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
對應於init.rc配置文件中,我們找到如下代碼:
# Mount filesystems and start core system services.
on late-init
trigger early-fs
... ...
# Now we can start zygote for devices with file based encryption
trigger zygote-start // 觸發了zygote-start事件後,就會啟動zygote進程
... ...
對應於init.rc配置文件中,我們找到如下代碼:
# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
on zygote-start && property:ro.crypto.state=unencrypted
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start netd // start對應的映射關係定義於system/core/init/builtins.cpp中
start zygote // 調用start對應的處理函數,啟動名為zygote的服務(傳入前文init.zygote.rc中定義的參數)
start zygote_secondary
on zygote-start && property:ro.crypto.state=unsupported
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
start命令有一個對應的執行函數do_start,定義在platform/system/core/init/builtins.cpp中
const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
static const Map builtin_functions = {
... ...
{"start", {1, 1, do_start}},
... ...
};
// clang-format on
return builtin_functions;
}
我們來看下do_start():
static int do_start(const std::vector<std::string>& args) {
Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]); // 找到zygote service對應信息
if (!svc) {
LOG(ERROR) << "do_start: Service " << args[1] << " not found";
return -1;
}
if (!svc->Start()) // 啟動對應的進程
return -1;
return 0;
}
do_start首先是通過FindServiceByName去service數組中遍歷,根據名字匹配出對應的service,然後調用service的Start函數。
最後,我們來看看service.cpp中定義Start函數:
bool Service::Start() {
... ...
pid_t pid = -1;
if (namespace_flags_) {
pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
} else {
pid = fork(); // 從init進程中,fork出zygote進程
}
... ...
}
Start函數主要是fork出一個新進程,然後執行service對應的二進位文件,並將參數傳遞進去。那麼下麵我們以init.zygote64.rc為例進行分析。
app_process
從上面我們分析的init.zygote64.rc可以看出,zygote64啟動文件的地址為app_process64。app_process64對應的代碼定義在frameworks/base/cmds/app_process中,
我們來看看對應的Android.mk: frameworks/base/cmds/app_process
LOCAL_PATH:= $(call my-dir)
... ...
app_process_src_files := \
app_main.cpp \
... ...
LOCAL_MODULE:= app_process
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := app_process32
LOCAL_MODULE_STEM_64 := app_process64
其實不管是app_process、app_process32還是app_process64,對應的源文件都是app_main.cpp。
接下來我們就看看app_process對應的main函數,該函數定義於app_main.cpp中。
在app_main.cpp的main函數中,主要做的事情就是參數解析. 這個函數有兩種啟動模式:
✨ 一種是zygote模式,也就是初始化zygote進程,傳遞的參數有--start-system-server --socket-name=zygote,前者表示啟動SystemServer,後者指定socket的名稱(Zygote64_32)。
✨ 一種是application模式,也就是啟動普通應用程式,傳遞的參數有class名字以及class帶的參數。
兩者最終都是調用AppRuntime對象的start函數,載入ZygoteInit或RuntimeInit兩個Java類,並將之前整理的參數傳入進去。
我們這裡暫時只講解ZygoteInit的載入流程。
int main(int argc, char* const argv[])
{
// 將參數argv放到argv_String字元串中,然後列印出來
// 之前start zygote傳入的參數是 -Xzygote /system/bin --zygote --start-system-server
if (!LOG_NDEBUG) {
String8 argv_String;
for (int i = 0; i < argc; ++i) {
argv_String.append("\"");
argv_String.append(argv[i]);
argv_String.append("\" ");
}
ALOGV("app_process main with argv: %s", argv_String.string());
}
// AppRuntime定義於app_main.cpp中,繼承自AndroidRuntime
// 就是對Android運行環境的一種抽象,類似於java虛擬機對Java程式的作用
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// Process command line arguments
// ignore argv[0]
argc--;
argv++;
// 這兩個參數是Java程式需要依賴的Jar包,相當於import
const char* spaced_commands[] = { "-cp", "-classpath" };
// Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).
bool known_command = false;
int i;
// 找到解析參數的起點
for (i = 0; i < argc; i++) {
// 將spaced_commands中的參數額外加入VM
if (known_command == true) {
runtime.addOption(strdup(argv[i]));
// The static analyzer gets upset that we don't ever free the above
// string. Since the allocation is from main, leaking it doesn't seem
// problematic. NOLINTNEXTLINE
ALOGV("app_process main add known option '%s'", argv[i]);
known_command = false;
continue;
}
for (int j = 0;
j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));
++j) {
// 比較參數是否是spaced_commands中的參數
if (strcmp(argv[i], spaced_commands[j]) == 0) {
known_command = true;
ALOGV("app_process main found known command '%s'", argv[i]);
}
}
// 如果參數第一個字元是'-',直接跳出迴圈,之前傳入的第一個參數是 -Xzygote,所以執行到這兒就跳出了
if (argv[i][0] != '-') {
break;
}
if (argv[i][1] == '-' && argv[i][2] == 0) {
++i; // Skip --.
break;
}
runtime.addOption(strdup(argv[i]));
// The static analyzer gets upset that we don't ever free the above
// string. Since the allocation is from main, leaking it doesn't seem
// problematic. NOLINTNEXTLINE
ALOGV("app_process main add option '%s'", argv[i]);
}
// Parse runtime arguments. Stop at first unrecognized option.
// 從這裡其實可以看出,通過app_main可以啟動zygote、system-server及普通apk進程
// 這個可以通過init.rc來配置
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName; // app_process的名稱改為zygote
String8 className; // 啟動apk進程時,對應的類名
++i; // Skip unused "parent dir" argument.
// 跳過一個參數,之前跳過了-Xzygote,這裡繼續跳過 /system/bin ,也就是所謂的 "parent dir"
while (i < argc) { // 開始解析輸入參數
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) { // 表示是zygote啟動模式
zygote = true;
niceName = ZYGOTE_NICE_NAME; // 這個值根據平臺可能是zygote64或zygote
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true; // init.zygote.rc中定義了該欄位,啟動zygote後會啟動system-server
} else if (strcmp(arg, "--application") == 0) {
application = true; // 表示是application啟動模式,也就是普通應用程式
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName.setTo(arg + 12); // 進程別名,可以自己指定進程名
} else if (strncmp(arg, "--", 2) != 0) {
className.setTo(arg); // 與--application配置,啟動指定的類,application啟動的class
break;
} else {
--i;
break;
}
}
// 準備參數
Vector<String8> args;
if (!className.isEmpty()) { // className不為空,說明是application啟動模式
// We're not in zygote mode, the only argument we need to pass
// to RuntimeInit is the application argument.
//
// The Remainder of args get passed to startup class main(). Make
// copies of them before we overwrite them with the process name.
args.add(application ? String8("application") : String8("tool"));
runtime.setClassNameAndArgs(className, argc - i, argv + i); // 將className和參數設置給runtime
... ...
} else { // zygote啟動模式
// We're in zygote mode.
maybeCreateDalvikCache(); // 創建Dalvik的緩存目錄並定義許可權
if (startSystemServer) { // 增加start-system-server參數,預設啟動zygote後,就會啟動system_server
args.add(String8("start-system-server"));
}
char prop[PROP_VALUE_MAX]; // 獲取平臺對應的abi信息
if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
ABI_LIST_PROPERTY);
return 11;
}
String8 abiFlag("--abi-list="); // 參數需要制定abi
abiFlag.append(prop);
args.add(abiFlag); // 加入--abi-list=參數
// In zygote mode, pass all remaining arguments to the zygote
// main() method.
for (; i < argc; ++i) {
args.add(String8(argv[i])); // 將剩下的參數加入args
}
}
if (!niceName.isEmpty()) { // 將app_process的進程名,替換為niceName
runtime.setArgv0(niceName.string(), true /* setProcName */);
}
if (zygote) { // 調用Runtime的start函數, 啟動ZygoteInit
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) { // 啟動zygote沒有進入這個分支
// 但這個分支說明,通過配置init.rc文件,其實是可以不通過zygote來啟動一個進程
// 如果是application啟動模式,則載入RuntimeInit
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
// error情況
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
}
}
AndroidRuntime
由於AppRuntime繼承自AndroidRuntime,且沒有重寫start方法,因此zygote的流程進入到了AndroidRuntime.cpp。
接下來,我們來看看AndroidRuntime的start函數的流程。
創建Java虛擬機
/*
* Start the Android runtime. This involves starting the virtual machine
* and calling the "static void main(String[] args)" method in the class
* named by "className".
*
* Passes the main function two arguments, the class name and the specified
* options string.
*/
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
... ... // 列印一些日誌,獲取ANDROID_ROOT環境變數
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL); // 初始化JNI,載入libart.so
JNIEnv* env;
// 創建虛擬機,其中大多數參數由系統屬性決定
// 最終,startVm利用JNI_CreateJavaVM創建出虛擬機
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
// 回調AppRuntime的onVmCreated函數
// 對於zygote進程的啟動流程而言,無實際操作,表示虛擬創建完成,但是裡面是空實現
onVmCreated(env);
... ...
}
這邊我們跟一下jni_invocation.Init():libnativehelper/JniInvocation.cpp
Init函數主要作用是初始化JNI,具體工作是首先通過dlopen載入libart.so獲得其句柄,然後調用dlsym從libart.so中找到JNI_GetDefaultJavaVMInitArgs、JNI_CreateJavaVM、JNI_GetCreatedJavaVMs三個函數地址,賦值給對應成員屬性,這三個函數會在後續虛擬機創建中調用。
bool JniInvocation::Init(const char* library) {
#ifdef __ANDROID__
char buffer[PROP_VALUE_MAX];
#else
char* buffer = NULL;
#endif
library = GetLibrary(library, buffer); // 預設返回 libart.so
// Load with RTLD_NODELETE in order to ensure that libart.so is not unmapped when it is closed.
// This is due to the fact that it is possible that some threads might have yet to finish
// exiting even after JNI_DeleteJavaVM returns, which can lead to segfaults if the library is
// unloaded.
const int kDlopenFlags = RTLD_NOW | RTLD_NODELETE;
/*
* 1.dlopen功能是以指定模式打開指定的動態鏈接庫文件,並返回一個句柄
* 2.RTLD_NOW表示需要在dlopen返回前,解析出所有未定義符號,如果解析不出來,在dlopen會返回NULL
* 3.RTLD_NODELETE表示在dlclose()期間不卸載庫,並且在以後使用dlopen()重新載入庫時不初始化庫中的靜態變數
*/
handle_ = dlopen(library, kDlopenFlags); // 獲取libart.so的句柄
if (handle_ == NULL) { // 獲取失敗列印錯誤日誌並嘗試再次打開libart.so
if (strcmp(library, kLibraryFallback) == 0) {
// Nothing else to try.
ALOGE("Failed to dlopen %s: %s", library, dlerror());
return false;
}
// Note that this is enough to get something like the zygote
// running, we can't property_set here to fix this for the future
// because we are root and not the system user. See
// RuntimeInit.commonInit for where we fix up the property to
// avoid future fallbacks. http://b/11463182
ALOGW("Falling back from %s to %s after dlopen error: %s",
library, kLibraryFallback, dlerror());
library = kLibraryFallback;
handle_ = dlopen(library, kDlopenFlags);
if (handle_ == NULL) {
ALOGE("Failed to dlopen %s: %s", library, dlerror());
return false;
}
}
/*
* 1.FindSymbol函數內部實際調用的是dlsym
* 2.dlsym作用是根據 動態鏈接庫 操作句柄(handle)與符號(symbol),返回符號對應的地址
* 3.這裡實際就是從libart.so中將JNI_GetDefaultJavaVMInitArgs等對應的地址存入&JNI_GetDefaultJavaVMInitArgs_中
*/
if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_),
"JNI_GetDefaultJavaVMInitArgs")) {
return false;
}
if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),
"JNI_CreateJavaVM")) {
return false;
}
if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_),
"JNI_GetCreatedJavaVMs")) {
return false;
}
return true;
}
其次,我們再跟一下startVm():
這個函數特別長,但是裡面做的事情很單一,其實就是從各種系統屬性中讀取一些參數,然後通過addOption設置到AndroidRuntime的mOptions數組中存起來,另外就是調用之前從libart.so中找到JNI_CreateJavaVM函數,並將這些參數傳入,由於本篇主要講zygote啟動流程,因此關於虛擬機的實現就不深入探究了。
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{
JavaVMInitArgs initArgs;
char propBuf[PROPERTY_VALUE_MAX];
char stackTraceFileBuf[sizeof("-Xstacktracefile:")-1 + PROPERTY_VALUE_MAX];
char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX];
... ...
/* route exit() to our handler */
addOption("exit", (void*) runtime_exit); // 將參數放入mOptions數組中
... ...
initArgs.version = JNI_VERSION_1_4;
initArgs.options = mOptions.editArray(); // 將mOptions賦值給initArgs
initArgs.nOptions = mOptions.size();
initArgs.ignoreUnrecognized = JNI_FALSE;
/*
* 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) { // 調用libart.so的JNI_CreateJavaVM函數
ALOGE("JNI_CreateJavaVM failed\n");
return -1;
}
return 0;
}
註冊JNI函數
我們回到AndroidRuntime的start函數。初始化JVM後,接下來就會調用startReg函數。
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
... ...
/* 01. 創建Java虛擬機*/
/*
* Register android functions.
*/
if (startReg(env) < 0) { // 註冊JNI函數
ALOGE("Unable to register all android natives\n");
return;
}
... ...
}
startReg首先是設置了Android創建線程的處理函數,然後創建了一個200容量的局部引用作用域,用於確保不會出現OutOfMemoryException,最後就是調用register_jni_procs進行JNI註冊。
我們跟進startReg():
/*
* Register android native functions with the VM.
*/
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
ATRACE_NAME("RegisterAndroidNatives");
/*
* 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.)
*/
// 定義Android創建線程的func:javaCreateThreadEtc,這個函數內部是通過Linux的clone來創建線程的
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); // 創建一個200容量的局部引用作用域,這個局部引用其實就是局部變數
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) { // 註冊JNI函數
env->PopLocalFrame(NULL);
return -1;
}
env->PopLocalFrame(NULL); // 釋放局部引用作用域
//createJavaThread("fubar", quickTest, (void*) "hello");
return 0;
}
從上述代碼可以看出,startReg函數中主要是通過register_jni_procs來註冊JNI函數。其中,gRegJNI是一個全局數組,該數組的定義如下:
static const RegJNIRec gRegJNI[] = { // 裡面就是一堆的函數指針
REG_JNI(register_com_android_internal_os_RuntimeInit),
REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
REG_JNI(register_android_util_Log),
... ...
};
我們挑一個register_com_android_internal_os_ZygoteInit_nativeZygoteInit,這實際上是自定義JNI函數併進行動態註冊的標準寫法,
內部是調用JNI的RegisterNatives,這樣註冊後,Java類ZygoteInit的native方法nativeZygoteInit就會調用com_android_internal_os_ZygoteInit_nativeZygoteInit函數。
int register_com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env)
{
const JNINativeMethod methods[] = {
{ "nativeZygoteInit", "()V",
(void*) com_android_internal_os_ZygoteInit_nativeZygoteInit },
};
return jniRegisterNativeMethods(env, "com/android/internal/os/ZygoteInit",
methods, NELEM(methods));
}
REG_JNI對應的巨集定義及RegJNIRec結構體的定義為:
#ifdef NDEBUG
#define REG_JNI(name) { name }
struct RegJNIRec {
int (*mProc)(JNIEnv*);
};
#else
#define REG_JNI(name) { name, #name }
struct RegJNIRec {
int (*mProc)(JNIEnv*);
const char* mName;
};
#endif
根據巨集定義可以看出,巨集REG_JNI將得到函數名;定義RegJNIRec數組時,函數名被賦值給RegJNIRec結構體,於是每個函數名被強行轉換為函數指針。
因此,register_jni_procs的參數就是一個函數指針數組,數組的大小和JNIEnv。
我們來跟進一下register_jni_procs函數:
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) { // 調用mProc
#ifndef NDEBUG
ALOGD("----------!!! %s failed to load\n", array[i].mName);
#endif
return -1;
}
}
return 0;
}
結合前面的分析,容易知道register_jni_procs函數,實際上就是調用函數指針(mProc)對應的函數,以進行實際的JNI函數註冊。
反射啟動ZygoteInit
繼續分析AndroidRuntime.cpp的start函數:
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
... ...
/* 01. 創建Java虛擬機*/
/* 02. 註冊JNI函數 */
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
// 替換string為實際路徑
// 例如:將 "com.android.internal.os.ZygoteInit" 替換為 "com/android/internal/os/ZygoteInit"
char* slashClassName = toSlashClassName(className != NULL ? className : "");
jclass startClass = env->FindClass(slashClassName); // 找到class文件
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} 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);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray); // 調用ZygoteInit的main函數
... ...
}
}
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");
}
可以看到,在AndroidRuntime的最後,將通過反射調用ZygoteInit的main函數。至此,zygote進程進入了java世界。
其實我們仔細想一想,就會覺得zygote的整個流程實際上是非常符合實際情況的。
✨✨ 在Android中,每個進程都運行在對應的虛擬機上,因此zygote首先就負責創建出虛擬機。
✨✨ 然後,為了反射調用java代碼,必須有對應的JNI函數,於是zygote進行了JNI函數的註冊。
✨✨ 當一切準備妥當後,zygote進程才進入到了java世界。
ZygoteInit
現在我們跟進ZygoteInit.java的main函數。
public static void main(String argv[]) {
//創建ZygoteServer對象
ZygoteServer zygoteServer = new ZygoteServer();
// Mark zygote start. This ensures that thread creation will throw
// an error.
// 調用native函數,確保當前沒有其它線程在運行
// 主要還是處於安全的考慮
ZygoteHooks.startZygoteNoThreadCreation();
// Zygote goes into its own process group.
try {
Os.setpgid(0, 0);
} catch (ErrnoException ex) {
throw new RuntimeException("Failed to setpgid(0,0)", ex);
}
final Runnable caller;
try {
... ...
RuntimeInit.enableDdms();
boolean startSystemServer = false;
String socketName = "zygote";
String abiList = null;
boolean enableLazyPreload = false;
// 解析參數,得到上述變數的值
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.");
}
zygoteServer.registerServerSocket(socketName); // 註冊server socket
// 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); // 預設情況,預載入信息
... ...
} else {
// 如註釋,延遲預載入
// 變更Zygote進程優先順序為NORMAL級別
// 第一次fork時才會preload
Zygote.resetNicePriority();
}
// Do an initial gc to clean up after startup
bootTimingsTraceLog.traceBegin("PostZygoteInitGC");
gcAndFinalize(); // 如果預載入了,很有必要GC一波
bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC
... ...
// Zygote process unmounts root storage spaces.
Zygote.nativeUnmountStorageOnInit();
// Set seccomp policy
// 載入seccomp的過濾規則
// 所有 Android 軟體都使用系統調用(簡稱為 syscall)與 Linux 內核進行通信
// 內核提供許多特定於設備和SOC的系統調用,讓用戶空間進程(包括應用)可以直接與內核進行交互
// 不過,其中許多系統調用Android未予使用或未予正式支持
// 通過seccomp,Android可使應用軟體無法訪問未使用的內核系統調用
// 由於應用無法訪問這些系統調用,因此,它們不會被潛在的有害應用利用
// 該過濾器安裝到zygote進程中,由於所有Android應用均衍生自該進程
// 因而會影響到所有應用
Seccomp.setPolicy();
/// M: Added for BOOTPROF
addBootEvent("Zygote:Preload End");
/// @}
ZygoteHooks.stopZygoteNoThreadCreation(); // 允許有其它線程了
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, socketName, zygoteServer); // fork出system server
// {@code r == null} in the parent (zygote) process, and {@code r != null} in the
// child (system_server) process.
if (r != null) {
r.run();
return;
}
}
Log.i(TAG, "Accepting command socket connections");
// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
caller = zygoteServer.runSelectLoop(abiList); // zygote進程進入無限迴圈,處理請求
} catch (Throwable ex) {
... ...
} finally {
zygoteServer.closeServerSocket();
}
// We're in the child process and have exited the select loop. Proceed to execute the
// command.
if (caller != null) {
caller.run();
}
}
上面是ZygoteInit的main函數的主幹部分,除了安全相關的內容外,最主要的工作就是註冊server socket、預載入、啟動system server及進入無限迴圈處理請求消息。
接下來我們分四部分分別討論!
創建server socket
Android O將server socket相關的工作抽象到ZygoteServer.java中了。我們來看看其中的registerZygoteSocket函數:
/**
* Registers a server socket for zygote command connections
*
* @throws RuntimeException when open fails
*/
void registerServerSocket(String socketName) {
if (mServerSocket == null) {
int fileDesc;
// ANDROID_SOCKET_PREFIX為"ANDROID_SOCKET_"
// 此處的socket name,就是zygote
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try {
// 還記得麽?在init.zygote.rc被載入時,指定了名為zygote的socket
// 在進程被創建時,就會創建對應的文件描述符,並加入到環境變數中
// 因此,此時可以取出對應的環境變數
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); // 獲取zygote socket的文件描述符
mServerSocket = new LocalServerSocket(fd); // 將socket包裝成一個server socket
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
}
我們跟蹤LocalServerSocket():
public LocalServerSocket(String name) throws IOException
{
impl = new LocalSocketImpl();
impl.create(LocalSocket.SOCKET_STREAM); // 創建SOCKET_STREAM類型的AF_UNIX socket
localAddress = new LocalSocketAddress(name);
impl.bind(localAddress); // 綁定到指定地址
impl.listen(LISTEN_BACKLOG); // 開始監聽
}
預載入
我們看看預載入的內容:
static void preload(TimingsTraceLog bootTimingsTraceLog) {
... ...
beginIcuCachePinning(); // Pin ICU Data, 獲取字元集轉換資源等
... ...
preloadClasses(); // 讀取文件system/etc/preloaded-classes,然後通過反射載入對應的類
// 一般由廠商來定義,有時需要載入數千個類,啟動慢的原因之一
... ...
preloadResources(); // 負責載入一些常用的系統資源
... ...
nativePreloadAppProcessHALs();
... ...
preloadOpenGL(); // 圖形相關
... ...
preloadSharedLibraries(); // 一些必要庫
preloadTextResources(); // 語言相關的字元信息
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
// for memory sharing purposes.
WebViewFactory.prepareWebViewInZygote();
endIcuCachePinning();
warmUpJcaProviders(); // 安全相關的
Log.d(TAG, "end preload");
sPreloadComplete = true;
}
為了讓系統實際運行時更加流暢,在zygote啟動時候,調用preload函數進行了一些預載入操作。Android 通過zygote fork的方式創建子進程。zygote進程預載入這些類和資源,在fork子進程時,僅需要做一個複製即可。
這樣可以節約子進程的啟動時間。同時,根據fork的copy-on-write機制可知,有些類如果不做改變,甚至都不用複製,子進程可以和父進程共用這部分數據,從而省去不少記憶體的占用。
啟動SystemServer進程
再來看看啟動System Server的流程:
/**
* Prepare the arguments and forks for the system server process.
*
* Returns an {@code Runnable} that provides an entrypoint into system_server code in the
* child process, and {@code null} in the parent.
*/
private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {
long capabilities = posixCapabilitiesAsBits(
OsConstants.CAP_IPC_LOCK,
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_PTRACE,
OsConstants.CAP_SYS_TIME,
OsConstants.CAP_SYS_TTY_CONFIG,
OsConstants.CAP_WAKE_ALARM
);
/* Containers run without this capability, so avoid setting it in that case */
if (!SystemProperties.getBoolean(PROPERTY_RUNNING_IN_CONTAINER, false)) {
capabilities |= posixCapabilitiesAsBits(OsConstants.CAP_BLOCK_SUSPEND);
}
/* Hardcoded command line to start the system server */
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1032,3001,3002,3003,3006,3007,3009,3010",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"com.android.server.SystemServer",
};
ZygoteConnection.Arguments parsedArgs = null;
int pid;
try {
parsedArgs = new ZygoteConnection.Arguments(args); // 將上面準備的參數,按照ZygoteConnection的風格進行封裝
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
/* Request to fork the system server process */
pid = Zygote.forkSystemServer( // 通過fork"分裂"出system_server
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) {
// 處理32_64和64_32的情況
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
// fork時會copy socket,system server需要主動關閉
zygoteServer.closeServerSocket();
// system server進程處理自己的工作
return handleSystemServerProcess(parsedArgs);
}
return null;
}
處理請求信息
創建出SystemServer進程後,zygote進程調用ZygoteServer中的函數runSelectLoop,處理server socket收到的命令。
/**
* Runs the zygote process's select loop. Accepts new connections as
* they happen, and reads commands from connections one spawn-request's
* worth at a time.
*/
Runnable runSelectLoop(String abiList) {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
// 首先將server socket加入到fds
fds.add(mServerSocket.getFileDescriptor());
peers.add(null);
while (true) {
// 每次迴圈,都重新創建需要監聽的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;
}
// server socket最先加入fds, 因此這裡是server socket收到數據
if (i == 0) {
// 收到新的建立通信的請求,建立通信連接
ZygoteConnection newPeer = acceptCommandPeer(abiList);
// 加入到peers和fds, 即下一次也開始監聽
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
//其它通信連接收到數據
... ...
}
}
}
}
從上面代碼可知,初始時fds中僅有server socket,因此當有數據到來時,將執行i等於0的分支。此時,顯然是需要創建新的通信連接,因此acceptCommandPeer將被調用。
我們看看acceptCommandPeer函數:
/**
* Waits for and accepts a single command connection. Throws
* RuntimeException on failure.
*/
private ZygoteConnection acceptCommandPeer(String abiList) {
try {
// socket編程中,accept()調用主要用在基於連接的套接字類型,比如SOCK_STREAM和SOCK_SEQPACKET
// 它提取出所監聽套接字的等待連接隊列中第一個連接請求,創建一個新的套接字,並返回指向該套接字的文件描述符
// 新建立的套接字不在監聽狀態,原來所監聽的套接字的狀態也不受accept()調用的影響
return createNewConnection(mServerSocket.accept(), abiList);
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
}
}
protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
throws IOException {
return new ZygoteConnection(socket, abiList);
}
從上面的代碼,可以看出acceptCommandPeer調用了server socket的accpet函數。於是當新的連接建立時,zygote將會創建出一個新的socket與其通信,並將該socket加入到fds中。因此,一旦通信連接建立後,fds中將會包含有多個socket。
當poll監聽到這一組sockets上有數據到來時,就會從阻塞中恢復。於是,我們需要判斷到底是哪個socket收到了數據。
在runSelectLoop中採用倒序的方式輪詢。由於server socket第一個被加入到fds,因此最後輪詢到的socket才需要處理新建連接的操作;其它socket收到數據時,僅需要調用zygoteConnection的runonce函數執行數據對應的操作。若一個連接處理完所有對應消息後,該連接對應的socket和連接等將被移除。
完結
Zygote啟動流程到此結束,Zygote進程共做瞭如下幾件事: