Android4.4的zygote進程
Android4.4的zygote進程
侯亮
1背景
前些天為了在科室做培訓,我基于Android 4.4重新整理了一份關于zygote的文檔。從技術的角度看,這幾年zygote并沒有出現什么大的變化,所以如果有人以前研究過zygote,應該不會對本文寫的內容感到陌生。
2zygote進程的描述
在Android中,zygote是整個系統創建新進程的核心裝置。從字面上看,zygote是受精卵的意思,它的主要工作就是進行細胞分裂。
zygote進程在內部會先啟動Dalvik虛擬機,繼而加載一些必要的系統資源和系統類,最后進入一種監聽狀態。在后續的運作中,當其他系統模塊(比如AMS)希望創建新進程時,只需向zygote進程發出請求,zygote進程監聽到該請求后,會相應地“分裂”出新的進程,于是這個新進程在初生之時,就先天具有了自己的Dalvik虛擬機以及系統資源。
系統啟動伊始,zygote進程就會被init進程啟動起來,init進程的詳情可參考我寫的《Android4.4的init進程》一文,此處不再贅述。我們直接來看init.rc腳本里的相關描述吧。在這個腳本中是這樣描述zygote的:
可以看到,zygote對應的可執行文件就是/system/bin/app_process,也就是說系統啟動時會執行到這個可執行文件的main()函數里。
3zygote進程的實現細節
zygote服務的main()函數位于frameworks\base\cmds\app_process\App_main.cpp文件中,其代碼截選如下:
int main(int argc, char* const argv[])
{
. . . . . .
AppRuntime runtime;
const char* argv0 = argv[0]; // -Xzygote
argc--;
argv++;
. . . . . .
int i = runtime.addVmArguments(argc, argv);
. . . . . .
while (i < argc) {
const char* arg = argv[i++]; // 應該是/system/bin目錄
if (!parentDir) {
parentDir = arg;
} else if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = "zygote";
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true;
}
. . . . . .
}
if (niceName && *niceName) {
setArgv0(argv0, niceName);
set_process_name(niceName); // 一般改名為“zygote”
}
runtime.mParentDir = parentDir;
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit",
startSystemServer ? "start-system-server" : "");
} else if (className) {
. . . . . .
} else {
. . . . . .
}
} 3.1AppRuntime的start()
main()函數里先構造了一個AppRuntime對象,即AppRuntime runtime;而后把進程名改成“zygote”,并利用runtime對象,把工作轉交給java層的ZygoteInit類處理。
這個AppRuntime類繼承于AndroidRuntime類,卻沒有重載其start(...)函數,所以main()函數中調用的runtime.start(...)其實走的是AndroidRuntime的start(...),而且傳入了類名參數,即字符串——“com.android.internal.os.ZygoteInit”。start()函數的主要代碼截選如下:
【frameworks/base/core/jni/AndroidRuntime.cpp】
void AndroidRuntime::start(const char* className, const char* options)
{
. . . . . .
const char* rootDir = getenv("ANDROID_ROOT");
. . . . . .
JniInvocation jni_invocation;
jni_invocation.Init(NULL); // 初始化JNI接口
JNIEnv* env;
if (startVm(&mJavaVM, &env) != 0) { // 啟動虛擬機
return;
}
onVmCreated(env);
if (startReg(env) < 0) { // 注冊系統需要的jni函數
ALOGE("Unable to register all android natives\n");
return;
}
. . . . . .
jclass startClass = env->FindClass(slashClassName);
. . . . . .
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
. . . . . .
env->CallStaticVoidMethod(startClass, startMeth, strArray);
. . . . . .
}代碼中會先初始化JNI接口,并啟動Dalvik虛擬機(startVm()),然后注冊一些系統需要的jni函數,接著將傳入的類名字符串參數輾轉傳給FindClass(),最后調用env->CallStaticVoidMethod()一句。
拋開Java層和C++層的概念,上面的流程說白了就是,Zygote進程的main()函數在啟動Dalvik虛擬機后,會調用另一個ZygoteInit類的main()靜態函數。調用示意圖如下:
3.1.1加載合適的虛擬機動態庫
一開始需要初始化JNI接口。
JniInvocation jni_invocation; jni_invocation.Init(NULL);這兩句是在Android 4.4上出現的,在Android 4.0上,還沒有它們呢。
jni_invocation的init()的代碼如下:
【libnativehelper/JniInvocation.cpp】
bool JniInvocation::Init(const char* library)
{
#ifdef HAVE_ANDROID_OS
char default_library[PROPERTY_VALUE_MAX];
property_get(kLibrarySystemProperty, default_library, kLibraryFallback);
#else
const char* default_library = kLibraryFallback;
#endif
if (library == NULL) {
library = default_library;
}
handle_ = dlopen(library, RTLD_NOW);
. . . . . .
. . . . . .
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;
} 因為我們使用的是Android系統,所以已經定義了HAVE_ANDROID_OS宏,而且library參數為NULL,于是在JniInvocation的Init()函數中,會走到
property_get(kLibrarySystemProperty, default_library, kLibraryFallback);其中,kLibrarySystemProperty的定義是“persist.sys.dalvik.vm.lib”,這是個系統屬性,它記錄著系統實際需要用到的是哪種虛擬機,是dalvik還是ART,它們分別對應libdvm.so或libart.so。接著,dlopen()嘗試加載libdvm.so或libart.so。這兩個so中都export出了JNI_GetDefaultJavaVMInitArgs,JNI_CreateJavaVM和JNI_GetCreatedJavaVMs這三個接口函數。
具體加載動態庫的函數是dlopen(),代碼如下:
【bionic/linker/Dlfcn.cpp】
void* dlopen(const char* filename, int flags) {
ScopedPthreadMutexLocker locker(&gDlMutex);
soinfo* result = do_dlopen(filename, flags);
if (result == NULL) {
__bionic_format_dlerror("dlopen failed", linker_get_error_buffer());
return NULL;
}
return result;
} 本文不需細究dlopen()的實現,大家只需知道,它是個強大的庫函數,可以打開某個動態庫,并將之裝入內存。調用dlopen()時傳入的第二個參數是RTLD_NOW,它表示加載器會立即計算庫的依賴性,從而在dlopen()返回之前,解析出每個未定義變量的地址。
3.1.2啟動Dalvik虛擬機,startVm()
初始化JNI環境后,就可以啟動Dalvik虛擬機了。下面是startVm()的代碼截選:
【frameworks/base/core/jni/AndroidRuntime.cpp】
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
{
. . . . . .
JavaVMInitArgs initArgs;
JavaVMOption opt;
. . . . . .
. . . . . .
opt.extraInfo = (void*) runtime_exit;
opt.optionString = "exit";
mOptions.add(opt);
. . . . . .
// Increase the main thread's interpreter stack size for bug 6315322.
opt.optionString = "-XX:mainThreadStackSize=24K";
mOptions.add(opt);
. . . . . .
. . . . . .
initArgs.version = JNI_VERSION_1_4;
initArgs.options = mOptions.editArray();
initArgs.nOptions = mOptions.size();
initArgs.ignoreUnrecognized = JNI_FALSE;
// 啟動dalvik虛擬機
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
. . . . . .
}
. . . . . .
} 因為本文闡述的重點不是Dalvik虛擬機,所以就不再深究JNI_CreateJavaVM()了。我們大概知道該函數會調用到剛剛jni_invocation的init()中,FindSymbol()得到的動態庫中相應的函數指針即可。
3.1.3注冊Android內部需要的函數,startReg()
當虛擬機成功啟動后,JNI環境也就建立好了,現在可以把JNIEnv*傳遞給startReg()來注冊一些重要的JNI接口了。startReg()的代碼截選如下:
【frameworks/base/core/jni/AndroidRuntime.cpp】
int AndroidRuntime::startReg(JNIEnv* env)
{
androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
. . . . . .
env->PushLocalFrame(200);
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
env->PopLocalFrame(NULL);
return -1;
}
env->PopLocalFrame(NULL);
return 0;
}
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) { // 回調每個RegJNIRec數組項的mProc
#ifndef NDEBUG
ALOGD("----------!!! %s failed to load\n", array[i].mName);
#endif
return -1;
}
}
return 0;
} 注冊jni函數的動作很簡單,只是在遍歷array數組,并嘗試回調每個數組項的mProc回調函數。具體數組項類型的定義如下,而且struct定義的上方還順帶定義了REG_JNI宏。:
#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 gRegJNI的定義截選如下:
static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_debug_JNITest),
REG_JNI(register_com_android_internal_os_RuntimeInit), // 舉例
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
REG_JNI(register_android_util_Log),
REG_JNI(register_android_util_FloatMath),
REG_JNI(register_android_text_format_Time),
REG_JNI(register_android_content_AssetManager),
. . . . . .
. . . . . . 這些注冊動作內部,基本上就是為自己關心的類注冊jni接口啦。比如上面的register_com_android_internal_os_RuntimeInit()函數,它的代碼如下:
【frameworks/base/core/jni/AndroidRuntime.cpp】
int register_com_android_internal_os_RuntimeInit(JNIEnv* env)
{
return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
gMethods, NELEM(gMethods));
}
static JNINativeMethod gMethods[] = {
{ "nativeFinishInit", "()V",
(void*) com_android_internal_os_RuntimeInit_nativeFinishInit },
{ "nativeZygoteInit", "()V",
(void*) com_android_internal_os_RuntimeInit_nativeZygoteInit },
{ "nativeSetExitWithoutCleanup", "(Z)V",
(void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup },
}; 其他“注冊動作”的格局大概也都是這樣,我們就不贅述了。
3.1.4加載ZygoteInit類
AppRuntime的start()最后會加載Java層次的ZygoteInit類,并利用JNI技術的CallStaticVoidMethod()調用其靜態的main()函數。
jclass startClass = env->FindClass(slashClassName);
. . . . . .
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
. . . . . .
env->CallStaticVoidMethod(startClass, startMeth, strArray); 這是很關鍵的一步,就在這一步,控制權就轉移到Java層次了。
3.2走入Java層——ZygoteInit.java
隨著控制權傳遞到Java層次,ZygoteInit要做一些和Android平臺緊密相關的重要動作,比如創建LocalServerSocket對象、預加載一些類以及資源、啟動“Android系統服務”、進入核心循環等等。我們先畫一張示意圖:

相應地,我們還可以把前文的調用關系也豐富一下,得到下圖:
3.2.1registerZygoteSocket()
我們先看ZygoteInit的main()函數調用的那個registerZygoteSocket()。這個函數內部其實會利用一個叫作“ANDROID_SOCKET_zygote”的環境變量。可是這個環境變量又是從哪里來的呢?為了解答這個問題,我們需要先看一下init進程service_start()函數。
3.2.1.1先看一下init進程的service_start()
前文我們已經列出了在init.rc腳本中,zygote服務是如何聲明的。現在我們只關心其中和socket相關的部分:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
. . . . . .
socket zygote stream 660 root system
. . . . . . 這個服務的socket選項表明,它需要一個名為“zygote”的“流型(stream)”socket。
當init進程真的啟動zygote服務時,會走到service_start()。我們現在只關心service_start()里和socket相關的動作。
【system/core/init/Init.c】
void service_start(struct service *svc, const char *dynamic_args)
{
. . . . . .
pid = fork(); // 先fork出新的service進程
if (pid == 0)
{
struct socketinfo *si;
struct svcenvinfo *ei;
. . . . . .
// 再為service進程創建必須的socket接口
for (si = svc->sockets; si; si = si->next)
{
int socket_type = (
!strcmp(si->type, "stream") ? SOCK_STREAM :
(!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
int s = create_socket(si->name, socket_type,
si->perm, si->uid, si->gid);
if (s >= 0) {
// 將socket接口記入ANDROID_SOCKET_zygote環境變量
publish_socket(si->name, s);
}
}
. . . . . .
if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
}
}
. . . . . .
} 每次為service創建新的子進程后,都會查看該service需要什么socket。比如zygote服務,明確提出需要一個“流型(stream)”的socket。
create_socket()會在/dev/socket目錄中創建一個Unix范疇的socket,而后,publish_socket()會把新建的socket的文件描述符記錄在以“ANDROID_SOCKET_”打頭的環境變量中。比如zygote對應的socket選項中的socket名為“zygote”,那么該socket對應的環境變量名就是“ANDROID_SOCKET_zygote”。
create_socket()的代碼截選如下:
【system/core/init/Util.c】
int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid)
{
struct sockaddr_un addr;
int fd, ret;
. . . . . .
fd = socket(PF_UNIX, type, 0);
. . . . . .
// "/dev/socket/zygote"
snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s", name);
ret = unlink(addr.sun_path);
. . . . . .
ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr)); // 給套接字命名
. . . . . .
chown(addr.sun_path, uid, gid);
chmod(addr.sun_path, perm);
. . . . . .
return fd;
. . . . . .
} 當用socket()函數創建套接字以后,使用bind()將“指定的地址”賦值給“用文件描述符代表的套接字”,一般來說,該操作被稱為“給套接字命名”。通常情況下,在一個SOCK_STREAM套接字接收連接之前,必須通過bind()函數用本地地址為套接字命名。對于zygote,其套接字地址應該是“/dev/socket/zygote”。在調用bind()函數之后,socket()函數創建的套接字已經和指定的地址關聯起來了,現在向這個地址發送的數據,就可以通過套接字讀取出來了。
接下來,service_start()還調用了個publish_socket()函數,該函數的代碼如下:
【system/core/init/Init.c】
static void publish_socket(const char *name, int fd)
{
char key[64] = ANDROID_SOCKET_ENV_PREFIX;
char val[64];
strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
name,
sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
snprintf(val, sizeof(val), "%d", fd); // 將文件描述符轉為字符串
add_environment(key, val);
/* make sure we don't close-on-exec */
fcntl(fd, F_SETFD, 0);
} 這么看來,所謂的“發布”(publish),主要是把socket的文件描述符記錄進環境變量。
上面代碼中的ANDROID_SOCKET_ENV_PREFIX的定義如下:
#define ANDROID_SOCKET_ENV_PREFIX "ANDROID_SOCKET_"
那么對于zygote而言,就是在環境變量“ANDROID_SOCKET_zygote”里記錄文件描述符對應的字符串。
add_environment()的代碼如下:
int add_environment(const char *key, const char *val)
{
int n;
for (n = 0; n < 31; n++) {
if (!ENV[n]) {
size_t len = strlen(key) + strlen(val) + 2;
char *entry = malloc(len);
snprintf(entry, len, "%s=%s", key, val);
ENV[n] = entry;
return 0;
}
}
return 1;
} 無非是把字符串記入一個靜態數組而已,在后續的代碼里,service_start()會調用execve(),并把ENV環境變量傳遞給execve()。
3.2.1.2registerZygoteSocket()里創建LocalServerSocket
OK,我們已經看到init進程在新fork出的zygote進程里,是如何記錄“ANDROID_SOCKET_zygote”環境變量的。現在我們可以回過頭來看zygote中的registerZygoteSocket()了,此處會切實地用到這個環境變量。
registerZygoteSocket()的代碼如下:
【frameworks/base/core/java/com/android/internal/os/ZygoteInit.java】
private static void registerZygoteSocket() {
if (sServerSocket == null) {
int fileDesc;
try {
String env = System.getenv(ANDROID_SOCKET_ENV);
fileDesc = Integer.parseInt(env); // 從環境變量的字符串中解析出文件描述符
} catch (RuntimeException ex) {
throw new RuntimeException(
ANDROID_SOCKET_ENV + " unset or invalid", ex);
}
try {
sServerSocket = new LocalServerSocket(createFileDescriptor(fileDesc));
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
} 先從環境變量里讀出socket的文件描述符,然后創建LocalServerSocket對象,并記入靜態變量sServerSocket中。以后zygote進程會循環監聽這個socket,一旦accept到連接請求,就創建命令連接(Command Connection)。監聽動作的細節是在runSelectLoop()中,我們會在后文闡述,這里先放下。
現在我們可以畫一張創建zygote socket接口的示意圖,如下:
請注意,圖中明確畫出了兩個進程,一個add環境變量,另一個get環境變量。
3.2.2預加載一些類——preloadClasses()
注冊完socket接口,ZygoteInit會預加載一些類,這些類記錄在frameworks/base/preloaded-classes文本文件里。下面是該文件的一部分截選:
# Classes which are preloaded by com.android.internal.os.ZygoteInit. # Automatically generated by frameworks/base/tools/preload/WritePreloadedClassFile.java. # MIN_LOAD_TIME_MICROS=1250 # MIN_PROCESSES=10 android.R$styleable android.accounts.Account android.accounts.Account$1 android.accounts.AccountManager android.accounts.AccountManager$12 android.accounts.AccountManager$13 android.accounts.AccountManager$6 android.accounts.AccountManager$AmsTask android.accounts.AccountManager$AmsTask$1 android.accounts.AccountManager$AmsTask$Response . . . . . . . . . . . .
在Android4.4上,這個腳本文件已經長達兩千七百多行了,它里面記錄著加載時間超過1250微秒的類,ZygoteInit嘗試在系統啟動時就把它們預加載進來,從而省去后續頻繁加載時帶來的系統開銷。
preloadClasses()的代碼截選如下:
private static void preloadClasses()
{
. . . . . .
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream(
PRELOADED_CLASSES); // 即"preloaded-classes"
. . . . . .
. . . . . .
try {
BufferedReader br = new BufferedReader(new InputStreamReader(is), 256);
. . . . . .
while ((line = br.readLine()) != null)
{
line = line.trim();
. . . . . .
Class.forName(line); // 使用加載當前類的類加載器來加載指定類
. . . . . .
count++;
. . . . . .
}
. . . . . .
} catch (IOException e) {
. . . . . .
} finally {
. . . . . .
runtime.preloadDexCaches();
. . . . . .
}
. . . . . .
} 在一個while循環里,每次讀取一行,然后調用Class.forName()方法來裝載類。這個工作量可不小,畢竟有兩千多行哩。
3.2.3預加載一些系統資源——preloadResources()
除了預加載一些類,zygote進程還要預加載一些系統資源。
private static void preloadResources()
{
. . . . . .
mResources = Resources.getSystem();
mResources.startPreloading();
if (PRELOAD_RESOURCES) {
. . . . . .
TypedArray ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_drawables);
int N = preloadDrawables(runtime, ar);
ar.recycle();
. . . . . .
ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_color_state_lists);
N = preloadColorStateLists(runtime, ar);
ar.recycle();
. . . . . .
}
mResources.finishPreloading();
. . . . . .
}
首先,從preloaded_drawables數組資源中讀取一個類型數組(TypedArray),具體的資源文件可參考frameworks/base/core/res/res/values/arrays.xml,截選如下:
基本上有兩大類資源:
1)一類和圖片有關(preloaed_drawables)
2)另一類和顏色有關(preloaded_color_state_lists)
加載第一類資源需要調用preloadDrawables(),逐個加載TypedArray里記錄的圖片資源:
private static int preloadDrawables(VMRuntime runtime, TypedArray ar)
{
int N = ar.length();
for (int i=0; i<N; i++) {
. . . . . .
int id = ar.getResourceId(i, 0); // 獲得i項對應的資源id
. . . . . .
if (id != 0) {
if (mResources.getDrawable(id) == null) {
throw new IllegalArgumentException(
"Unable to find preloaded drawable resource #0x"
+ Integer.toHexString(id)
+ " (" + ar.getString(i) + ")");
}
}
}
return N;
} 說起來之前mResources.obtainTypedArray()獲取TypedArray時,其內部用的是AssetManager。得到TypedArray之后,我們就可以通過調用ar.getResourceId(i, 0)來得到數組項對應的資源id了。
其中的mResources是ZygoteInit的私有靜態成員:
private static Resources mResources;
mResources的getDrawable()函數內部,會調用loadDrawable()。這樣,這些圖片資源就都加載到ZygoteInit的mResources里了。
另一些資源是顏色資源,是用preloadColorStateLists()加載的:
private static int preloadColorStateLists(VMRuntime runtime, TypedArray ar) {
int N = ar.length();
for (int i=0; i<N; i++) {
. . . . . .
int id = ar.getResourceId(i, 0);
. . . . . .
if (id != 0) {
if (mResources.getColorStateList(id) == null) {
throw new IllegalArgumentException(
"Unable to find preloaded color resource #0x"
+ Integer.toHexString(id)
+ " (" + ar.getString(i) + ")");
}
}
}
return N;
} 也是在一個for循環里逐個加載顏色集,比如arrays.xml里的
<item>@color/primary_text_dark</item>
這個顏色集的參考文件是frameworks/base/core/res/res/color/primary_text_dark.xml,
現在,我們畫一張加載系統資源的調用關系圖:
3.2.4啟動Android系統服務——startSystemServer()
接下來就是啟動Android的重頭戲了,此時ZygoteInit的main()函數會調用startSystemServer(),該函數用于啟動整個Android系統的系統服務。其大體做法是先fork一個子進程,然后在子進程中做一些初始化動作,繼而執行SystemServer類的main()靜態函數。需要注意的是,startSystemServer()并不是在函數體內直接調用Java類的main()函數的,而是通過拋異常的方式,在startSystemServer()之外加以處理的。
startSystemServer()的代碼如下:
private static boolean startSystemServer()
throws MethodAndArgsCaller, RuntimeException
{
. . . . . .
/* 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,1032,
3001,3002,3003,3006,3007",
"--capabilities=" + capabilities + "," + capabilities,
"--runtime-init",
"--nice-name=system_server",
"com.android.server.SystemServer",
};
ZygoteConnection.Arguments parsedArgs = null;
int pid;
try {
parsedArgs = new ZygoteConnection.Arguments(args);
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
// fork出系統服務對應的進程
pid = Zygote.forkSystemServer(parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids, parsedArgs.debugFlags, null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
// 對新fork出的系統進程,執行handleSystemServerProcess()
if (pid == 0) {
handleSystemServerProcess(parsedArgs);
}
return true;
} | args[]中的字符串 | 對應 |
| "--setuid=1000" | parsedArgs.uid |
| "--setgid=1000" | parsedArgs.gid |
|
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008, |
parsedArgs.gids |
| "--capabilities=" + capabilities + "," + capabilities | capabilitiesSpecified = true; permittedCapabilities = Long.decode(capStrings[0]); effectiveCapabilites = Long.decode(capString[1]); |
| "--runtime-init" | parsedArgs.runtimeInit設為true |
| "--nice-name=system_server" | parsedArgs.niceName |
| "com.android.server.SystemServer" | parsedArgs.remainingArgs |
3.2.4.1Zygote.forkSystemServer()
Zygote.forkSystemServer()的代碼如下:
【libcore/dalvik/src/main/java/dalvik/system/Zygote.java】
public static int forkSystemServer(int uid, int gid, int[] gids, int debugFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities)
{
preFork();
int pid = nativeForkSystemServer(uid, gid, gids, debugFlags, rlimits,
permittedCapabilities, effectiveCapabilities);
postFork();
return pid;
} 其中的nativeForkSystemServer()是個native成員函數,其對應的C++層函數為Dalvik_dalvik_system_Zygote_forkSystemServer()。
【dalvik/vm/native/dalvik_system_Zygote.cpp】
const DalvikNativeMethod dvm_dalvik_system_Zygote[] = {
{ "nativeFork", "()I",
Dalvik_dalvik_system_Zygote_fork },
{ "nativeForkAndSpecialize", "(II[II[[IILjava/lang/String;Ljava/lang/String;)I",
Dalvik_dalvik_system_Zygote_forkAndSpecialize },
{ "nativeForkSystemServer", "(II[II[[IJJ)I",
Dalvik_dalvik_system_Zygote_forkSystemServer },
{ NULL, NULL, NULL },
};
static void Dalvik_dalvik_system_Zygote_forkSystemServer(
const u4* args, JValue* pResult)
{
pid_t pid;
pid = forkAndSpecializeCommon(args, true);
if (pid > 0) {
int status;
ALOGI("System server process %d has been created", pid);
gDvm.systemServerPid = pid;
if (waitpid(pid, &status, WNOHANG) == pid) {
ALOGE("System server process %d has died. Restarting Zygote!", pid);
kill(getpid(), SIGKILL);
}
}
RETURN_INT(pid);
} forkAndSpecializeCommon()內部其實會調用fork(),而后設置gid、uid等信息。
3.2.4.2SystemServer的handleSystemServerProgress()函數
接著,startSystemServer()會在新fork出的子進程中調用handleSystemServerProgress(),讓這個新進程成為真正的系統進程(SystemServer進程)。
// 對新fork出的系統進程,執行handleSystemServerProcess()
if (pid == 0) {
handleSystemServerProcess(parsedArgs);
} 注意,調用handleSystemServerProcess()時,程序是運行在新fork出的進程中的。handleSystemServerProcess()的代碼如下:
【frameworks/base/core/java/com/android/internal/os/ZygoteInit.java】
private static void handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs)
throws ZygoteInit.MethodAndArgsCaller
{
closeServerSocket();
Libcore.os.umask(S_IRWXG | S_IRWXO);
if (parsedArgs.niceName != null) {
Process.setArgV0(parsedArgs.niceName); // niceName就是”system_server”
}
if (parsedArgs.invokeWith != null) {
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
null, parsedArgs.remainingArgs);
} else {
// 此時的remainingArgs就是”com.android.server.SystemServer”
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs);
}
}
3.2.4.2.1closeServerSocket()
因為當前已經不是運行在zygote進程里了,所以zygote里的那個監聽socket就應該關閉了。這就是closeServerSocket()的意義,其代碼如下:
static void closeServerSocket()
{
try {
if (sServerSocket != null) {
FileDescriptor fd = sServerSocket.getFileDescriptor();
sServerSocket.close();
if (fd != null) {
Libcore.os.close(fd);
}
}
} catch (IOException ex) {
Log.e(TAG, "Zygote: error closing sockets", ex);
} catch (libcore.io.ErrnoException ex) {
Log.e(TAG, "Zygote: error closing descriptor", ex);
}
sServerSocket = null;
}
3.2.4.2.2RuntimeInit.zygoteInit()
RuntimeInit.zygoteInit()的代碼如下:
【frameworks/base/core/java/com/android/internal/os/RuntimeInit.java】
public static final void zygoteInit(int targetSdkVersion, String[] argv)
throws ZygoteInit.MethodAndArgsCaller
{
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
redirectLogStreams();
commonInit();
nativeZygoteInit();
applicationInit(targetSdkVersion, argv);
} 3.2.4.2.2.1.調用redirectLogStreams()
首先,在新fork出的系統進程里,需要重新定向系統輸出流。
public static void redirectLogStreams()
{
System.out.close();
System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
System.err.close();
System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
}
3.2.4.2.2.2.調用commonInit()
private static final void commonInit()
{
. . . . . .
Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
TimezoneGetter.setInstance(new TimezoneGetter()
. . . . . .
. . . . . .
String trace = SystemProperties.get("ro.kernel.android.tracing");
. . . . . .
initialized = true;
}
3.2.4.2.2.3.調用nativeZygoteInit()
接下來調用的nativeZygoteInit()是個JNI函數,在AndroidRuntime.cpp文件中可以看到:
【frameworks/base/core/jni/AndroidRuntime.cpp】
static JNINativeMethod gMethods[] = {
{ "nativeFinishInit", "()V",
(void*) com_android_internal_os_RuntimeInit_nativeFinishInit },
{ "nativeZygoteInit", "()V",
(void*) com_android_internal_os_RuntimeInit_nativeZygoteInit },
{ "nativeSetExitWithoutCleanup", "(Z)V",
(void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup },
};
nativeZygoteInit()對應的本地函數為com_android_internal_os_RuntimeInit_nativeZygoteInit()。
static void com_android_internal_os_RuntimeInit_nativeZygoteInit(JNIEnv* env,
jobject clazz)
{
gCurRuntime->onZygoteInit();
}
gCurRuntime是C++層的AndroidRuntime類的靜態變量。在AndroidRuntime構造之時,
gCurRuntime = this。不過實際調用的onZygoteInit()應該是AndroidRuntime的子類AppRuntime的:
【frameworks/base/cmds/app_process/App_main.cpp】
class AppRuntime : public AndroidRuntime
{
. . . . . .
virtual void onZygoteInit()
{
// Re-enable tracing now that we're no longer in Zygote.
atrace_set_tracing_enabled(true);
sp<ProcessState> proc = ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}
ProcessState對象是典型的單例模式,它的self()函數如下:
sp<ProcessState> ProcessState::self()
{
Mutex::Autolock _l(gProcessMutex);
if (gProcess != NULL) {
return gProcess;
}
gProcess = new ProcessState;
return gProcess;
}
我們整理一下思路,畫一張startSystemServer()的調用關系圖:
接下來我們來講上圖中zygoteInit()調用的最后一行:applicationInit()。
3.2.4.2.2.4.調用applicationInit()
applicationInit()函數的代碼如下:
【frameworks/base/core/java/com/android/internal/os/RuntimeInit.java】
private static void applicationInit(int targetSdkVersion, String[] argv)
throws ZygoteInit.MethodAndArgsCaller
{
nativeSetExitWithoutCleanup(true);
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
final Arguments args;
try {
args = new Arguments(argv);
} catch (IllegalArgumentException ex) {
Slog.e(TAG, ex.getMessage());
return;
}
invokeStaticMain(args.startClass, args.startArgs);
}
其中的invokeStaticMain()一句最為關鍵,它承擔向外拋出“特殊異常”的作用。我們先畫一張startSystemServer()的調用關系圖:

看到了吧,最后一步拋出了異常。這相當于一個“特殊的goto語句”!上面的cl = Class.forName(className)一句,其實加載的就是SystemServer類。這個類名是從前文的parsedArgs.remainingArgs得來的,其值就是“com.android.server.SystemServer”。此處拋出的異常,會被本進程的catch語句接住,在那里才會執行SystemServer類的main()函數。示意圖如下:
如上圖所示,新fork出的SystemServer子進程直接跳過了中間那句runSelectLoop(),徑直跳轉到caller.run()一步了。
當然,父進程Zygote在fork動作后,會退出startSystemServer()函數,并走到runSelectLoop(),從而進入一種循環監聽狀態,每當Activity Manager Service向它發出“啟動新應用進程”的命令時,它又會fork一個子進程,并在子進程里拋出一個異常,這樣子進程還是會跳轉到catch一句。
我們可以把上面的示意圖再豐富一下:
還有一點需要說明一下,fork出的SystemServer進程在跳轉到catch語句后,會執行SystemServer類的main()函數,而其他情況下,fork出的應用進程在跳轉的catch語句后,則會執行ActivityThread類的main()函數。這個ActivityThread對于應用程序而言非常重要,但因為和本篇主題關系不大,我們就不在這里展開講了。
3.2.4.3 SystemServer的main()函數
前文我們已經看到了,startSystemServer()創建的新進程在執行完applicationInit()之后,會拋出一個異常,并由新fork出的SystemServer子進程的catch語句接住,繼而執行SystemServer類的main()函數。
那么SystemServer的main()函數又在做什么事情呢?其調用關系圖如下:

在Android4.4版本總,ServerThread已經不再繼承于Thread了,它現在只是個輔助類,其命名還殘留有舊代碼的味道。在以前的Android版本中,ServerThread的確繼承于Thread,而且在線程的run()成員函數里,做著類似addService、systemReady的工作。
因為本文主要是闡述zygote進程的,所以我們就不在這里繼續細說system server進程啦,有興趣的同學可以繼續研究。我們還是回過頭繼續說zygote里的動作吧。
3.2.5監聽zygote socket
3.2.5.1runSelectLoop()
ZygoteInit的main()函數在調用完startSystemServer()之后,會進一步走到runSelectLoop()。runSelectInit()的代碼如下:
【frameworks/base/core/java/com/android/internal/os/ZygoteInit.java】
private static void runSelectLoop() throws MethodAndArgsCaller
{
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
FileDescriptor[] fdArray = new FileDescriptor[4];
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
int loopCount = GC_LOOP_COUNT;
while (true) {
int index;
if (loopCount <= 0) {
gc();
loopCount = GC_LOOP_COUNT;
} else {
loopCount--;
}
try {
fdArray = fds.toArray(fdArray);
index = selectReadable(fdArray);
} catch (IOException ex) {
throw new RuntimeException("Error in select()", ex);
}
if (index < 0) {
throw new RuntimeException("Error in select()");
} else if (index == 0) {
ZygoteConnection newPeer = acceptCommandPeer();
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
boolean done;
done = peers.get(index).runOnce();
if (done) {
peers.remove(index);
fds.remove(index);
}
}
}
}
在一個while循環中,不斷調用selectReadable()。該函數是個native函數,對應C++層的com_android_internal_os_ZygoteInit_selectReadable()。
【frameworks/base/core/jni/com_android_internal_os_ZygoteInit.cpp】
static jint com_android_internal_os_ZygoteInit_selectReadable (JNIEnv *env, jobject clazz,
jobjectArray fds)
{
. . . . . .
int err;
do {
err = select (nfds, &fdset, NULL, NULL, NULL);
} while (err < 0 && errno == EINTR);
. . . . . .
for (jsize i = 0; i < length; i++) {
jobject fdObj = env->GetObjectArrayElement(fds, i);
. . . . . .
int fd = jniGetFDFromFileDescriptor(env, fdObj);
. . . . . .
if (FD_ISSET(fd, &fdset)) {
return (jint)i;
}
}
return -1;
}
err = select (nfds, &fdset, NULL, NULL, NULL);一句的最后三個參數都為NULL,表示該select()操作只打算監視文件描述符的“讀變化”,而且如果沒有可讀的文件,select()就維持阻塞狀態。
在被監視的文件描述符數組(fds)中,第一個文件描述符對應著“zygote接收其他進程連接申請的那個socket(及sServerSocket)”,一旦它發生了變化,我們就嘗試建立一個ZygoteConnection。
// (index == 0)的情況
ZygoteConnection newPeer = acceptCommandPeer();
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
如果select動作發現文件描述符數組(fds)的其他文件描述符有東西可讀了,說明有其他進程通過某個已建立好的ZygoteConnection發來了命令,此時我們需要調用runOnce()。
// (index > 0)的情況
boolean done;
done = peers.get(index).runOnce();
if (done) {
peers.remove(index);
fds.remove(index);
}
建立ZygoteConnection的acceptCommandPeer()的代碼如下:
private static ZygoteConnection acceptCommandPeer() {
try {
return new ZygoteConnection(sServerSocket.accept());
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
}
}
3.2.5.1.1ZygoteConnection的runOnce()
ZygoteConnection的runOnce()代碼截選如下:
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
. . . . . .
args = readArgumentList();
descriptors = mSocket.getAncillaryFileDescriptors();
. . . . . .
int pid = -1;
FileDescriptor childPipeFd = null;
FileDescriptor serverPipeFd = null;
try {
parsedArgs = new Arguments(args);
. . . . . .
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal,
parsedArgs.seInfo, parsedArgs.niceName);
}
. . . . . .
if (pid == 0) {
// in child
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
return true;
} else {
// in parent...pid of < 0 means failure
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
}
. . . . . .
}
3.2.5.1.2readArgumentList()
runOnce()中從socket中讀取參數數據的動作是由readArgumentList()完成的,該函數的代碼如下:
private String[] readArgumentList()
throws IOException
{
int argc;
. . . . . .
String s = mSocketReader.readLine();
. . . . . .
argc = Integer.parseInt(s);
. . . . . .
String[] result = new String[argc];
for (int i = 0; i < argc; i++) {
result[i] = mSocketReader.readLine();
if (result[i] == null) {
// We got an unexpected EOF.
throw new IOException("truncated request");
}
}
return result;
}
我們知道,當AMS需要啟動一個新進程時,會調用類似下面的句子:
Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, null);
3.2.5.1.3handleChildProc()
runOnce()在讀完參數之后,會進一步調用到handleChildProc()。正如前文所說,該函數會間接拋出特殊的MethodAndArgsCaller異常,只不過此時拋出的異常攜帶的類名為ActivityThread。
private void handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller
{
closeSocket();
ZygoteInit.closeServerSocket();
. . . . . .
if (parsedArgs.niceName != null) {
Process.setArgV0(parsedArgs.niceName);
}
if (parsedArgs.runtimeInit) {
if (parsedArgs.invokeWith != null) {
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
pipeFd, parsedArgs.remainingArgs);
} else {
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs);
}
} else {
String className;
. . . . . .
className = parsedArgs.remainingArgs[0];
. . . . . .
String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1];
System.arraycopy(parsedArgs.remainingArgs, 1,
mainArgs, 0, mainArgs.length);
if (parsedArgs.invokeWith != null) {
WrapperInit.execStandalone(parsedArgs.invokeWith,
parsedArgs.classpath, className, mainArgs);
} else {
ClassLoader cloader;
if (parsedArgs.classpath != null) {
cloader = new PathClassLoader(parsedArgs.classpath,
ClassLoader.getSystemClassLoader());
} else {
cloader = ClassLoader.getSystemClassLoader();
}
try {
ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
} catch (RuntimeException ex) {
logAndPrintError(newStderr, "Error starting.", ex);
}
}
}
} 4小結
至此,zygote進程就闡述完畢了。作為一個最原始的“受精卵”,它必須在合適的時機進行必要的細胞分裂。分裂動作也沒什么大的花樣,不過就是fork()新進程而已。如果fork()出的新進程是system server,那么其最終執行的就是SystemServer類的main()函數,而如果fork()出的新進程是普通的用戶進程的話,那么其最終執行的就是ActivityThread類的main()函數。有關ActivityThread的細節,我們有時間再深入探討,這里就不細說了。
本篇文章和我的上一篇文章《Android4.4的init進程》可以算是姊妹篇啦。讀完這兩篇文章,我相信大家對Android的啟動流程能有一些大面上的認識了。
來自:http://my.oschina.net/youranhongcha/blog/469447