Android 8.0 系統啟動流程之zygote程序(八)
前言
在上一篇中我們講到,init程序會解析.rc檔案,並介紹了.rc檔案中的語法規則。其中不乏有許多service啟動,這些service通常不是普通的服務,文件裡面的稱呼是daemon(守護程序).
所謂守護程序就是這些服務程序會在系統初始化時啟動,並一直運行於後臺,直到系統關閉時終止. 我們本篇講的zygote程序就是其中之一,zygote程序主要負責建立Java虛擬機器,載入系統資源,啟動SystemServer程序,以及在後續執行過程中啟動普通的應用程式。
1、zygote觸發過程
1.1 init.zygoteXX.rc
定義在platform/system/core/rootdir/init.zygoteXX.rc
我們知道service是定義在.rc檔案中的,那麼zygote定義在哪兒呢?在init.rc中有這樣一句
import /init.${ro.zygote}.rc
上節中講到 ${ro.zygote} 會被替換成 ro.zyogte 的屬性值,這個是由不同的硬體廠商自己定製的,
有四個值,zygote32、zygote64、zygote32_64、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會啟動兩個程序,且存在主次之分. 我們以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
1.2 start zygote
定義在 platform/system/core/rootdir/init.rc
在上一篇中分析到,在init程序的最後,會加入 late-init 的trigger
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
當late-init 觸發時,會觸發zygote-start
on late-init
...
trigger zygote-start
當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 zygote
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
由此分析,zygote的觸發是在init程序最後,接下來,我們看看start zygote是如何繼續執行的.
1.3 app_processXX
上一篇中我們知道 start 命令有一個對應的執行函式 do_start ,定義在platform/system/core/init/builtins.cpp中
static const Map builtin_functions = {
...
{"start", {1, 1, do_start}},
...
};
do_start首先是通過FindServiceByName去service陣列中遍歷,根據名字匹配出對應的service,然後呼叫service的Start函式,Start函式我們在上一篇結尾有分析,主要是fork出一個新程序然後執行service對應的二進位制檔案,並將引數傳遞進去.
static int do_start(const std::vector<std::string>& args) {
Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]); //找出對應service
if (!svc) {
LOG(ERROR) << "do_start: Service " << args[1] << " not found";
return -1;
}
if (!svc->Start())//啟動服務
return -1;
return 0;
}
zygote對應的二進位制檔案是 /system/bin/app_process64 (以此為例),我們看一下對應的mk檔案,
對應的目錄在platform/frameworks/base/cmds/app_process/Android.mk,其實不管是app_process、app_process32還是app_process64,對應的原始檔都是app_main.cpp.
...
app_process_src_files := \
app_main.cpp \
LOCAL_SRC_FILES:= $(app_process_src_files)
...
LOCAL_MODULE:= app_process
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := app_process32
LOCAL_MODULE_STEM_64 := app_process64
...
接下來,我們分析app_main.cpp.
2、zygote引數解析
platform/frameworks/base/cmds/app_process/app_main.cpp
在app_main.cpp的main函式中,主要做的事情就是引數解析. 這個函式有兩種啟動模式:
- 一種是zygote模式,也就是初始化zygote程序,傳遞的引數有–start-system-server –socket-name=zygote,前者表示啟動SystemServer,後者指定socket的名稱
- 一種是application模式,也就是啟動普通應用程式,傳遞的引數有class名字以及class帶的引數
兩者最終都是呼叫AppRuntime物件的start函式,載入ZygoteInit或RuntimeInit兩個Java類,並將之前整理的引數傳入進去
由於本篇講的是zygote程序啟動流程,因此接下來我只講解ZygoteInit的載入.
int main(int argc, char* const argv[])
{
//將引數argv放到argv_String字串中,然後打印出來
//之前start zygote傳入的引數是 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
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 runtime(argv[0], computeArgBlockSize(argc, argv));//構建AppRuntime物件,並將引數傳入
// Process command line arguments
// ignore argv[0]
argc--;
argv++;
// Everything up to '--' or first non '-' arg goes to the vm.
//
// The first argument after the VM args is the "parent dir", which
// is currently unused.
//
// After the parent dir, we expect one or more the following internal
// arguments :
//
// --zygote : Start in zygote mode
// --start-system-server : Start the system server.
// --application : Start in application (stand alone, non zygote) mode.
// --nice-name : The nice name for this process.
//
// For non zygote starts, these arguments will be followed by
// the main class name. All remaining arguments are passed to
// the main method of this class.
//
// For zygote starts, all remaining arguments are passed to the zygote.
// main function.
//
// Note that we must copy argument string values since we will rewrite the
// entire argument block when we apply the nice name to argv0.
//
// As an exception to the above rule, anything in "spaced commands"
// goes to the vm even though it has a space in it.
//上面這段英文大概講的是,所有在 "--" 後面的非 "-"開頭的引數都將傳入vm, 但是有個例外是spaced commands陣列中的引數
const char* spaced_commands[] = { "-cp", "-classpath" };//這兩個引數是Java程式需要依賴的Jar包,相當於import
// 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++) {
if (known_command == true) { //將spaced_commands中的引數額外加入VM
runtime.addOption(strdup(argv[i]));
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) {
if (strcmp(argv[i], spaced_commands[j]) == 0) {//比較引數是否是spaced_commands中的引數
known_command = true;
ALOGV("app_process main found known command '%s'", argv[i]);
}
}
if (argv[i][0] != '-') { //如果引數第一個字元是'-',直接跳出迴圈,之前傳入的第一個引數是 -Xzygote,所以執行到這兒就跳出了,i=0
break;
}
if (argv[i][1] == '-' && argv[i][2] == 0) {
++i; // Skip --.
break;
}
runtime.addOption(strdup(argv[i]));
ALOGV("app_process main add option '%s'", argv[i]);
}
// 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.
//跳過一個引數,之前跳過了-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) {//需要啟動SystemServer
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {//表示是application啟動模式,也就是普通應用程式
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {//程序別名
niceName.setTo(arg + 12);
} else if (strncmp(arg, "--", 2) != 0) {//application啟動的class
className.setTo(arg);
break;
} else {
--i;
break;
}
}
Vector<String8> args;
if (!className.isEmpty()) {//className不為空,說明是application啟動模式
args.add(application ? String8("application") : String8("tool"));
runtime.setClassNameAndArgs(className, argc - i, argv + i);//將className和引數設定給runtime
if (!LOG_NDEBUG) {//列印class帶的引數
String8 restOfArgs;
char* const* argv_new = argv + i;
int argc_new = argc - i;
for (int k = 0; k < argc_new; ++k) {
restOfArgs.append("\"");
restOfArgs.append(argv_new[k]);
restOfArgs.append("\" ");
}
ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string());
}
} else { //zygote啟動模式
// We're in zygote mode.
maybeCreateDalvikCache(); //新建Dalvik的快取目錄
if (startSystemServer) {//加入start-system-server引數
args.add(String8("start-system-server"));
}
char prop[PROP_VALUE_MAX];
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=");
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
args.add(String8(argv[i]));
}
}
if (!niceName.isEmpty()) {//設定程序別名
runtime.setArgv0(niceName.string(), true /* setProcName */);
}
if (zygote) { //如果是zygote啟動模式,則載入ZygoteInit
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {//如果是application啟動模式,則載入RuntimeInit
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.");
}
}
我們看到,在最後呼叫的是runtime.start函式,這個就是要啟動虛擬機器了,接下來我們分析start函式
3、建立虛擬機器
這部分我將分兩步講解,一是虛擬機器的建立,二是呼叫ZygoteInit類的main函式
3.1 建立虛擬機器、註冊JNI函式
platform/frameworks/base/core/jni/AndroidRuntime.cpp
前半部分主要是初始化JNI,然後建立虛擬機器,註冊一些JNI函式,我將分開一個個單獨講
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;
if (startVm(&mJavaVM, &env, zygote) != 0) {//建立虛擬機器
return;
}
onVmCreated(env);//表示虛擬建立完成,但是裡面是空實現
/*
* Register android functions.
*/
if (startReg(env) < 0) {//註冊JNI函式
ALOGE("Unable to register all android natives\n");
return;
}
...
//JNI方式呼叫ZygoteInit類的main函式
}
3.1.1 JniInvocation.Init
定義在platform/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
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;
}
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;
}
3.1.2 startVm
定義在platform/frameworks/base/core/jni/AndroidRuntime.cpp
這個函式特別長,但是裡面做的事情很單一,其實就是從各種系統屬性中讀取一些引數,然後通過addOption設定到AndroidRuntime的mOptions陣列中存起來,另外就是呼叫之前從libart.so中找到JNI_CreateJavaVM函式,並將這些引數傳入,由於本篇主要講zygote啟動流程,因此關於虛擬機器的實現就不深入探究了
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{
JavaVMInitArgs initArgs;
...
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;
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {//呼叫libart.so的JNI_CreateJavaVM函式
ALOGE("JNI_CreateJavaVM failed\n");
return -1;
}
return 0;
}
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
return JniInvocation::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args);
}
jint JniInvocation::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
return JNI_CreateJavaVM_(p_vm, p_env, vm_args);//呼叫之前初始化的JNI_CreateJavaVM_
}
3.1.3 startReg
定義在platform/frameworks/base/core/jni/AndroidRuntime.cpp
startReg首先是設定了Android建立執行緒的處理函式,然後建立了一個200容量的區域性引用作用域,用於確保不會出現OutOfMemoryException,最後就是呼叫register_jni_procs進行JNI註冊
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.)
*/
androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
//設定Android建立執行緒的函式javaCreateThreadEtc,這個函式內部是通過Linux的clone來建立執行緒的
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;
}
3.1.4 register_jni_procs
定義在platform/frameworks/base/core/jni/AndroidRuntime.cpp
它的處理是交給RegJNIRec的mProc,RegJNIRec是個很簡單的結構體,mProc是個函式指標
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;
}
struct RegJNIRec {
int (*mProc)(JNIEnv*);
};
我們看看register_jni_procs傳入的RegJNIRec陣列gRegJNI,裡面就是一堆的函式指標
static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_os_RuntimeInit),
REG_JNI(register_com_android_internal_os_ZygoteInit),
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
REG_JNI(register_android_util_Log),
REG_JNI(register_android_util_MemoryIntArray)
...
}
我們隨便看一個register_com_android_internal_os_ZygoteInit,這實際上是自定義JNI函式並進行動態註冊的標準寫法,內部是呼叫JNI的RegisterNatives,這樣註冊後,Java類ZygoteInit的native方法nativeZygoteInit就會呼叫com_android_internal_os_ZygoteInit_nativeZygoteInit函式
int register_com_android_internal_os_ZygoteInit(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));
}
以上便是第一部分的內容,主要工作是從libart.so提取出JNI初始函式JNI_CreateJavaVM,然後讀取一些系統屬性作為引數呼叫JNI_CreateJavaVM建立虛擬機器,在虛擬機器建立完成後,動態註冊一些native函式,接下來我們講第二部分,反射呼叫ZygoteInit類的main函式
3.2 反射呼叫ZygoteInit類的main函式
虛擬機器建立完成後,我們就可以用JNI反射呼叫Java了,其實接下來的語法用過JNI的都應該比較熟悉了,直接是CallStaticVoidMethod反射呼叫ZygoteInit的main函式
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
//接下來的這些語法大家應該比較熟悉了,都是JNI裡的語法,主要作用就是呼叫ZygoteInit類的main函式
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
for (size_t i = 0; i < options.size(); ++i) {
jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
assert(optionsStr != NULL);
env->SetObjectArrayElement(strArray, i + 1, optionsStr);
}
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
char* slashClassName = toSlashClassName(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");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);//呼叫main函式
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
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");
}
4、小結
本篇主要講zygote程序的觸發過程,zygote是如何解析傳進來的引數,然後講了Java虛擬機器的建立. 有了虛擬機器,就可以執行Java程式碼了,