理解Android進程創建流程(轉)
/frameworks/base/core/java/com/android/internal/os/
- ZygoteInit.java
- ZygoteConnection.java
- RuntimeInit.java
- Zygote.java
/frameworks/base/core/java/android/os/Process.java
/frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
/frameworks/base/core/jni/AndroidRuntime.cpp
/frameworks/base/cmds/app_process/App_main.cpp (內含AppRuntime類)
/libcore/dalvik/src/main/java/dalvik/system/ZygoteHooks.java
/art/runtime/native/dalvik_system_ZygoteHooks.cc
/art/runtime/Runtime.cc
/art/runtime/Thread.cc
/art/runtime/signal_catcher.cc
一. 概述
準備知識
本文要介紹的是進程的創建,先簡單說說進程與線程的區別。
進程:每個App
在啟動前必須先創建一個進程,該進程是由Zygote
fork出來的,進程具有獨立的資源空間,用於承載App上運行的各種Activity/Service等組件。進程對於上層應用來說是完全透明的,這也是google有意為之,讓App程序都是運行在Android Runtime。大多數情況一個App
就運行在一個進程中,除非在AndroidManifest.xml中配置Android:process
屬性,或通過native代碼fork進程。
線程:線程對應用開發者來說非常熟悉,比如每次new Thread().start()
在接下來的文章,會涉及到system_server進程和Zygote進程,下面簡要這兩個進程:
system_server
進程:是用於管理整個Java framework層,包含ActivityManager,PowerManager等各種系統服務;Zygote
進程:是Android系統的首個Java進程,Zygote是所有Java進程的父進程,包括system_server
進程以及所有的App進程都是Zygote的子進程,註意這裏說的是子進程,而非子線程。
如果想更進一步了解system_server進程和Zygote進程在整個Android系統所處的地位,可查看我的另一個文章Android系統-開篇。
進程創建圖
對於大多數的應用開發者來說創建線程比較熟悉,而對於創建進程並沒有太多的概念。對於系統工程師或者高級開發者,還是有很必要了解Android系統是如何一步步地創建出一個進程的。先來看一張進程創建過程的簡要圖:
圖解:
- App發起進程:當從桌面啟動應用,則發起進程便是Launcher所在進程;當從某App內啟動遠程進程,則發送進程便是該App所在進程。發起進程先通過binder發送消息給system_server進程;
- system_server進程:調用Process.start()方法,通過socket向zygote進程發送創建新進程的請求;
- zygote進程:在執行
ZygoteInit.main()
後便進入runSelectLoop()
循環體內,當有客戶端連接時便會執行ZygoteConnection.runOnce()方法,再經過層層調用後fork出新的應用進程; - 新進程:執行handleChildProc方法,最後調用ActivityThread.main()方法。
接下來,依次從system_server進程發起請求
到Zygote創建進程
,再到新進程的運行
這3大塊展開講解進程創建是一個怎樣的過程。
二. system_server發起請求
1. Process.start
[-> Process.java]
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] zygoteArgs) {
try {
//【見小節2】
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
throw new RuntimeException("");
}
}
2. startViaZygote
[-> Process.java]
private static ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] extraArgs)
throws ZygoteStartFailedEx {
synchronized(Process.class) {
ArrayList<String> argsForZygote = new ArrayList<String>();
argsForZygote.add("--runtime-args");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
if (niceName != null) {
argsForZygote.add("--nice-name=" + niceName);
}
if (appDataDir != null) {
argsForZygote.add("--app-data-dir=" + appDataDir);
}
argsForZygote.add(processClass);
if (extraArgs != null) {
for (String arg : extraArgs) {
argsForZygote.add(arg);
}
}
//【見小節3】
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
}
該過程主要工作是生成argsForZygote
數組,該數組保存了進程的uid、gid、groups、target-sdk、nice-name等一系列的參數。
3. zygoteSendArgsAndGetResult
[-> Process.java]
private static ProcessStartResult zygoteSendArgsAndGetResult(
ZygoteState zygoteState, ArrayList<String> args)
throws ZygoteStartFailedEx {
try {
//其中zygoteState 【見小節3.1】
final BufferedWriter writer = zygoteState.writer;
final DataInputStream inputStream = zygoteState.inputStream;
writer.write(Integer.toString(args.size()));
writer.newLine();
int sz = args.size();
for (int i = 0; i < sz; i++) {
String arg = args.get(i);
if (arg.indexOf(‘\n‘) >= 0) {
throw new ZygoteStartFailedEx(
"embedded newlines not allowed");
}
writer.write(arg);
writer.newLine();
}
writer.flush();
ProcessStartResult result = new ProcessStartResult();
//等待socket服務端(即zygote)返回新創建的進程pid;
//對於等待時長問題,Google正在考慮此處是否應該有一個timeout,但目前是沒有的。
result.pid = inputStream.readInt();
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
result.usingWrapper = inputStream.readBoolean();
return result;
} catch (IOException ex) {
zygoteState.close();
throw new ZygoteStartFailedEx(ex);
}
}
這個方法的主要功能是通過socket通道向Zygote進程發送一個參數列表,然後進入阻塞等待狀態,直到遠端的socket服務端發送回來新創建的進程pid才返回。
3.1 openZygoteSocketIfNeeded
private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
try {
//向主zygote發起connect()操作
primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET);
} catch (IOException ioe) {
...
}
}
if (primaryZygoteState.matches(abi)) {
return primaryZygoteState;
}
if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
//當主zygote沒能匹配成功,則采用第二個zygote,發起connect()操作
secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET);
}
if (secondaryZygoteState.matches(abi)) {
return secondaryZygoteState;
}
...
}
openZygoteSocketIfNeeded(abi)
方法是根據當前的abi來選擇與zygote還是zygote64來進行通信。
既然system_server進程的zygoteSendArgsAndGetResult()方法通過socket向Zygote進程發送消息,這是便會喚醒Zygote進程,來響應socket客戶端的請求(即system_server端),接下來的操作便是在Zygote來創建進程【見小節4】
三. Zygote創建進程
文章Android系統啟動-zygote篇已介紹,簡單來說就是Zygote進程是由由init進程而創建的,進程啟動之後調用ZygoteInit.main()方法,經過創建socket管道,預加載資源後,便進程runSelectLoop()方法。
4. ZygoteInit.main
[–>ZygoteInit.java]
public static void main(String argv[]) {
try {
runSelectLoop(abiList); //【見小節5】
....
} catch (MethodAndArgsCaller caller) {
caller.run(); //【見小節16】
} catch (RuntimeException ex) {
closeServerSocket();
throw ex;
}
}
後續會講到runSelectLoop()方法會拋出異常MethodAndArgsCaller
,從而進入caller.run()方法。
5. runSelectLoop
[-> ZygoteInit.java]
private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
//sServerSocket是socket通信中的服務端,即zygote進程。保存到fds[0]
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
while (true) {
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 {
//處理輪詢狀態,當pollFds有事件到來則往下執行,否則阻塞在這裏
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
...
}
for (int i = pollFds.length - 1; i >= 0; --i) {
//采用I/O多路復用機制,當接收到客戶端發出連接請求 或者數據處理請求到來,則往下執行;
// 否則進入continue,跳出本次循環。
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
//即fds[0],代表的是sServerSocket,則意味著有客戶端連接請求;
// 則創建ZygoteConnection對象,並添加到fds。//【見小節5.1】
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor()); //添加到fds.
} else {
//i>0,則代表通過socket接收來自對端的數據,並執行相應操作【見小節6】
boolean done = peers.get(i).runOnce();
if (done) {
peers.remove(i);
fds.remove(i); //處理完則從fds中移除該文件描述符
}
}
}
}
}
該方法主要功能:
- 客戶端通過openZygoteSocketIfNeeded()來跟zygote進程建立連接。zygote進程收到客戶端連接請求後執行accept();然後再創建ZygoteConnection對象,並添加到fds數組列表;
- 建立連接之後,可以跟客戶端通信,進入runOnce()方法來接收客戶端數據,並執行進程創建工作。
5.1 acceptCommandPeer
[-> ZygoteInit.java]
private static ZygoteConnection acceptCommandPeer(String abiList) {
try {
return new ZygoteConnection(sServerSocket.accept(), abiList);
} catch (IOException ex) {
...
}
}
接收客戶端發送過來的connect()操作,Zygote作為服務端執行accept()操作。 再後面客戶端調用write()寫數據,Zygote進程調用read()讀數據。
沒有連接請求時會進入休眠狀態,當有創建新進程的連接請求時,喚醒Zygote進程,創建Socket通道ZygoteConnection,然後執行ZygoteConnection的runOnce()方法。
6. runOnce
[-> ZygoteConnection.java]
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
//讀取socket客戶端發送過來的參數列表
args = readArgumentList();
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
closeSocket();
return true;
}
PrintStream newStderr = null;
if (descriptors != null && descriptors.length >= 3) {
newStderr = new PrintStream(new FileOutputStream(descriptors[2]));
}
int pid = -1;
FileDescriptor childPipeFd = null;
FileDescriptor serverPipeFd = null;
try {
//將binder客戶端傳遞過來的參數,解析成Arguments對象格式
parsedArgs = new Arguments(args);
...
int [] fdsToClose = { -1, -1 };
FileDescriptor fd = mSocket.getFileDescriptor();
if (fd != null) {
fdsToClose[0] = fd.getInt$();
}
fd = ZygoteInit.getServerSocketFileDescriptor();
if (fd != null) {
fdsToClose[1] = fd.getInt$();
}
fd = null;
//【見小節7】
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
parsedArgs.appDataDir);
} catch (Exception e) {
...
}
try {
if (pid == 0) {
//子進程執行
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
//【見小節13】
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
// 不應到達此處,子進程預期的是拋出異常ZygoteInit.MethodAndArgsCaller或者執行exec().
return true;
} else {
//父進程執行
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}
7. forkAndSpecialize
[-> Zygote.java]
public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
String instructionSet, String appDataDir) {
VM_HOOKS.preFork(); //【見小節8】
int pid = nativeForkAndSpecialize(
uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
instructionSet, appDataDir); //【見小節9】
...
VM_HOOKS.postForkCommon(); //【見小節11】
return pid;
}
VM_HOOKS是Zygote對象的靜態成員變量:VM_HOOKS = new ZygoteHooks();
7.1 Zygote進程
先說說Zygote進程,如下圖:
從圖中可知Zygote進程有4個Daemon子線程分別是ReferenceQueueDaemon,FinalizerDaemon,FinalizerWatchdogDaemon,HeapTaskDaemon。圖中線程名顯示的並不完整是由於底層的進程結構體task_struct
是由長度為16的char型數組保存,超過15個字符便會截斷。
可能有人會問zygote64進程不是還有system_server,com.android.phone等子線程,怎麽會只有4個呢?那是因為這些並不是Zygote子線程,而是Zygote的子進程。在圖中用紅色圈起來的是進程的VSIZE,virtual size),代表的是進程虛擬地址空間大小。線程與進程的最為本質的區別便是是否共享內存空間,圖中VSIZE和Zygote進程相同的才是Zygote的子線程,否則就是Zygote的子進程。
8. preFork
[-> ZygoteHooks.java]
public void preFork() {
Daemons.stop(); //停止4個Daemon子線程【見小節8.1】
waitUntilAllThreadsStopped(); //等待所有子線程結束【見小節8.2】
token = nativePreFork(); //完成gc堆的初始化工作【見小節8.3】
}
8.1 Daemons.stop
public static void stop() {
HeapTaskDaemon.INSTANCE.stop(); //Java堆整理線程
ReferenceQueueDaemon.INSTANCE.stop(); //引用隊列線程
FinalizerDaemon.INSTANCE.stop(); //析構線程
FinalizerWatchdogDaemon.INSTANCE.stop(); //析構監控線程
}
此處守護線程Stop方式是先調用目標線程interrrupt()方法,然後再調用目標線程join()方法,等待線程執行完成。
8.2 waitUntilAllThreadsStopped
private static void waitUntilAllThreadsStopped() {
File tasks = new File("/proc/self/task");
// 當/proc中線程數大於1,就出讓CPU直到只有一個線程,才退出循環
while (tasks.list().length > 1) {
Thread.yield();
}
}
8.3 nativePreFork
nativePreFork通過JNI最終調用如下方法:
[-> dalvik_system_ZygoteHooks.cc]
static jlong ZygoteHooks_nativePreFork(JNIEnv* env, jclass) {
Runtime* runtime = Runtime::Current();
runtime->PreZygoteFork(); // 見下文
if (Trace::GetMethodTracingMode() != TracingMode::kTracingInactive) {
Trace::Pause();
}
//將線程轉換為long型並保存到token,該過程是非安全的
return reinterpret_cast<jlong>(ThreadForEnv(env));
}
至於runtime->PreZygoteFork的過程:
void Runtime::PreZygoteFork() {
// 堆的初始化工作。這裏就不繼續再往下追art虛擬機
heap_->PreZygoteFork();
}
VM_HOOKS.preFork()的主要功能便是停止Zygote的4個Daemon子線程的運行,等待並確保Zygote是單線程(用於提升fork效率),並等待這些線程的停止,初始化gc堆的工作, 並將線程轉換為long型並保存到token
9. nativeForkAndSpecialize
nativeForkAndSpecialize()通過JNI最終調用調用如下方法:
[-> com_android_internal_os_Zygote.cpp]
static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
jint debug_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring se_name,
jintArray fdsToClose, jstring instructionSet, jstring appDataDir) {
// 將CAP_WAKE_ALARM賦予藍牙進程
jlong capabilities = 0;
if (uid == AID_BLUETOOTH) {
capabilities |= (1LL << CAP_WAKE_ALARM);
}
//【見流程10】
return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags,
rlimits, capabilities, capabilities, mount_external, se_info,
se_name, false, fdsToClose, instructionSet, appDataDir);
}
10. ForkAndSpecializeCommon
[-> com_android_internal_os_Zygote.cpp]
static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
jint debug_flags, jobjectArray javaRlimits,
jlong permittedCapabilities, jlong effectiveCapabilities,
jint mount_external,
jstring java_se_info, jstring java_se_name,
bool is_system_server, jintArray fdsToClose,
jstring instructionSet, jstring dataDir) {
//設置子進程的signal信號處理函數
SetSigChldHandler();
//fork子進程 【見流程10.1】
pid_t pid = fork();
if (pid == 0) { //進入子進程
DetachDescriptors(env, fdsToClose); //關閉並清除文件描述符
if (!is_system_server) {
//對於非system_server子進程,則創建進程組
int rc = createProcessGroup(uid, getpid());
}
SetGids(env, javaGids); //設置設置group
SetRLimits(env, javaRlimits); //設置資源limit
int rc = setresgid(gid, gid, gid);
rc = setresuid(uid, uid, uid);
SetCapabilities(env, permittedCapabilities, effectiveCapabilities);
SetSchedulerPolicy(env); //設置調度策略
//selinux上下文
rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
if (se_info_c_str == NULL && is_system_server) {
se_name_c_str = "system_server";
}
if (se_info_c_str != NULL) {
SetThreadName(se_name_c_str); //設置線程名為system_server,方便調試
}
//在Zygote子進程中,設置信號SIGCHLD的處理器恢復為默認行為
UnsetSigChldHandler();
//等價於調用zygote.callPostForkChildHooks() 【見流程10.2】
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, debug_flags,
is_system_server ? NULL : instructionSet);
...
} else if (pid > 0) {
//進入父進程,即Zygote進程
}
return pid;
}
10.1 fork()
fork()采用copy on write技術,這是linux創建進程的標準方法,調用一次,返回兩次,返回值有3種類型。
- 父進程中,fork返回新創建的子進程的pid;
- 子進程中,fork返回0;
- 當出現錯誤時,fork返回負數。(當進程數超過上限或者系統內存不足時會出錯)
fork()的主要工作是尋找空閑的進程號pid,然後從父進程拷貝進程信息,例如數據段和代碼段,fork()後子進程要執行的代碼等。 Zygote進程是所有Android進程的母體,包括system_server和各個App進程。zygote利用fork()方法生成新進程,對於新進程A復用Zygote進程本身的資源,再加上新進程A相關的資源,構成新的應用進程A。其中下圖中Zygote進程的libc、vm、preloaded classes、preloaded resources是如何生成的,可查看另一個文章Android系統啟動-zygote篇,見下圖:
copy-on-write過程:當父子進程任一方修改內存數據時(這是on-write時機),才發生缺頁中斷,從而分配新的物理內存(這是copy操作)。
copy-on-write原理:寫時拷貝是指子進程與父進程的頁表都所指向同一個塊物理內存,fork過程只拷貝父進程的頁表,並標記這些頁表是只讀的。父子進程共用同一份物理內存,如果父子進程任一方想要修改這塊物理內存,那麽會觸發缺頁異常(page fault),Linux收到該中斷便會創建新的物理內存,並將兩個物理內存標記設置為可寫狀態,從而父子進程都有各自獨立的物理內存。
10.2 Zygote.callPostForkChildHooks
[-> Zygote.java]
private static void callPostForkChildHooks(int debugFlags, boolean isSystemServer,
String instructionSet) {
//調用ZygoteHooks.postForkChild()
VM_HOOKS.postForkChild(debugFlags, isSystemServer, instructionSet);
}
[-> ZygoteHooks.java]
public void postForkChild(int debugFlags, String instructionSet) {
//【見流程10.3】
nativePostForkChild(token, debugFlags, instructionSet);
Math.setRandomSeedInternal(System.currentTimeMillis());
}
在這裏,設置了新進程Random隨機數種子為當前系統時間,也就是在進程創建的那一刻就決定了未來隨機數的情況,也就是偽隨機。
10.3 nativePostForkChild
nativePostForkChild通過JNI最終調用調用如下方法:
[-> dalvik_system_ZygoteHooks.cc]
static void ZygoteHooks_nativePostForkChild(JNIEnv* env, jclass, jlong token, jint debug_flags,
jstring instruction_set) {
//此處token是由[小節8.3]創建的,記錄著當前線程
Thread* thread = reinterpret_cast<Thread*>(token);
//設置新進程的主線程id
thread->InitAfterFork();
..
if (instruction_set != nullptr) {
ScopedUtfChars isa_string(env, instruction_set);
InstructionSet isa = GetInstructionSetFromString(isa_string.c_str());
Runtime::NativeBridgeAction action = Runtime::NativeBridgeAction::kUnload;
if (isa != kNone && isa != kRuntimeISA) {
action = Runtime::NativeBridgeAction::kInitialize;
}
//【見流程10.4】
Runtime::Current()->DidForkFromZygote(env, action, isa_string.c_str());
} else {
Runtime::Current()->DidForkFromZygote(env, Runtime::NativeBridgeAction::kUnload, nullptr);
}
}
10.4 DidForkFromZygote
[-> Runtime.cc]
void Runtime::DidForkFromZygote(JNIEnv* env, NativeBridgeAction action, const char* isa) {
is_zygote_ = false;
if (is_native_bridge_loaded_) {
switch (action) {
case NativeBridgeAction::kUnload:
UnloadNativeBridge(); //卸載用於跨平臺的橋連庫
is_native_bridge_loaded_ = false;
break;
case NativeBridgeAction::kInitialize:
InitializeNativeBridge(env, isa);//初始化用於跨平臺的橋連庫
break;
}
}
//創建Java堆處理的線程池
heap_->CreateThreadPool();
//重置gc性能數據,以保證進程在創建之前的GCs不會計算到當前app上。
heap_->ResetGcPerformanceInfo();
if (jit_.get() == nullptr && jit_options_->UseJIT()) {
//當flag被設置,並且還沒有創建JIT時,則創建JIT
CreateJit();
}
//設置信號處理函數
StartSignalCatcher();
//啟動JDWP線程,當命令debuger的flags指定"suspend=y"時,則暫停runtime
Dbg::StartJdwp();
}
關於信號處理過程,其代碼位於signal_catcher.cc文件中,後續會單獨講解。
11. postForkCommon
[-> ZygoteHooks.java]
public void postForkCommon() {
Daemons.start();
}
public static void start() {
ReferenceQueueDaemon.INSTANCE.start();
FinalizerDaemon.INSTANCE.start();
FinalizerWatchdogDaemon.INSTANCE.start();
HeapTaskDaemon.INSTANCE.start();
}
VM_HOOKS.postForkCommon的主要功能是在fork新進程後,啟動Zygote的4個Daemon線程,java堆整理,引用隊列,以及析構線程。
12. forkAndSpecialize小結
該方法主要功能:
- preFork: 停止Zygote的4個Daemon子線程的運行,初始化gc堆;
- nativeForkAndSpecialize:調用
fork()
創建新進程,設置新進程的主線程id,重置gc性能數據,設置信號處理函數等功能。 - postForkCommon:啟動4個Deamon子線程。
其調用關系鏈:
Zygote.forkAndSpecialize
ZygoteHooks.preFork
Daemons.stop
ZygoteHooks.nativePreFork
dalvik_system_ZygoteHooks.ZygoteHooks_nativePreFork
Runtime::PreZygoteFork
heap_->PreZygoteFork()
Zygote.nativeForkAndSpecialize
com_android_internal_os_Zygote.ForkAndSpecializeCommon
fork()
Zygote.callPostForkChildHooks
ZygoteHooks.postForkChild
dalvik_system_ZygoteHooks.nativePostForkChild
Runtime::DidForkFromZygote
ZygoteHooks.postForkCommon
Daemons.start
時序圖: 點擊查看大圖
到此App進程已完成了創建的所有工作,接下來開始新創建的App進程的工作。在前面ZygoteConnection.runOnce方法中,zygote進程執行完forkAndSpecialize()
後,新創建的App進程便進入handleChildProc()
方法,下面的操作運行在App進程。
四. 新進程運行
在前面[流程6]runOnce()過程中調用forkAndSpecialize()創建完新進程後,返回值pid=0(即運行在子進程)繼續開始執行handleChildProc()方法。
13. handleChildProc
[-> ZygoteConnection.java]
private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {
//關閉Zygote的socket兩端的連接
closeSocket();
ZygoteInit.closeServerSocket();
if (descriptors != null) {
try {
Os.dup2(descriptors[0], STDIN_FILENO);
Os.dup2(descriptors[1], STDOUT_FILENO);
Os.dup2(descriptors[2], STDERR_FILENO);
for (FileDescriptor fd: descriptors) {
IoUtils.closeQuietly(fd);
}
newStderr = System.err;
} catch (ErrnoException ex) {
Log.e(TAG, "Error reopening stdio", ex);
}
}
if (parsedArgs.niceName != null) {
//設置進程名
Process.setArgV0(parsedArgs.niceName);
}
if (parsedArgs.invokeWith != null) {
//據說這是用於檢測進程內存泄露或溢出時場景而設計,後續還需要進一步分析。
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
pipeFd, parsedArgs.remainingArgs);
} else {
//執行目標類的main()方法 【見流程14】
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs, null);
}
}
14. zygoteInit
[–>RuntimeInit.java]
public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
redirectLogStreams(); //重定向log輸出
commonInit(); // 通用的一些初始化【見流程14.1】
nativeZygoteInit(); // zygote初始化 【見流程14.2】
applicationInit(targetSdkVersion, argv, classLoader); // 應用初始化【見流程14.3】
}
14.1 commonInit
[–>RuntimeInit.java]
private static final void commonInit() {
// 設置默認的未捕捉異常處理方法
Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
// 設置市區,中國時區為"Asia/Shanghai"
TimezoneGetter.setInstance(new TimezoneGetter() {
public String getId() {
return SystemProperties.get("persist.sys.timezone");
}
});
TimeZone.setDefault(null);
//重置log配置
LogManager.getLogManager().reset();
new AndroidConfig();
// 設置默認的HTTP User-agent格式,用於 HttpURLConnection。
String userAgent = getDefaultUserAgent();
System.setProperty("http.agent", userAgent);
// 設置socket的tag,用於網絡流量統計
NetworkManagementSocketTagger.install();
}
默認的HTTP User-agent格式,例如:
"Dalvik/1.1.0 (Linux; U; Android 6.0.1;LenovoX3c70 Build/LMY47V)".
14.2 nativeZygoteInit
nativeZygoteInit()所對應的jni方法如下:
[–>AndroidRuntime.cpp]
static void com_android_internal_os_RuntimeInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
{
//此處的gCurRuntime為AppRuntime,是在AndroidRuntime.cpp中定義的
gCurRuntime->onZygoteInit();
}
14.2.1 onZygoteInit
[–>app_main.cpp]
virtual void onZygoteInit()
{
sp<ProcessState> proc = ProcessState::self();
proc->startThreadPool(); //啟動新binder線程
}
- ProcessState::self():主要工作是調用open()打開/dev/binder驅動設備,再利用mmap()映射內核的地址空間,將Binder驅動的fd賦值ProcessState對象中的變量mDriverFD,用於交互操作。startThreadPool()是創建一個新的binder線程,不斷進行talkWithDriver().
- startThreadPool(): 啟動Binder線程池, 詳見進程的Binder線程池工作過程
14.3 applicationInit
[–>RuntimeInit.java]
private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
//true代表應用程序退出時不調用AppRuntime.onExit(),否則會在退出前調用
nativeSetExitWithoutCleanup(true);
//設置虛擬機的內存利用率參數值為0.75
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
final Arguments args;
try {
args = new Arguments(argv); //解析參數
} catch (IllegalArgumentException ex) {
return;
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//調用startClass的static方法 main() 【見流程15】
invokeStaticMain(args.startClass, args.startArgs, classLoader);
}
此處args.startClass為”android.app.ActivityThread”。
15. invokeStaticMain
[–>RuntimeInit.java]
private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
Class<?> cl = Class.forName(className, true, classLoader);
Method m = cl.getMethod("main", new Class[] { String[].class });
int modifiers = m.getModifiers();
...
//通過拋出異常,回到ZygoteInit.main()。這樣做好處是能清空棧幀,提高棧幀利用率。【見流程16】
throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}
invokeStaticMain()方法中拋出的異常MethodAndArgsCaller
caller,該方法的參數m
是指main()方法, argv
是指ActivityThread. 根據前面的【流程4】中可知,下一步進入caller.run()方法,也就是MethodAndArgsCaller.run()。
16. MethodAndArgsCaller
[–>ZygoteInit.java]
public static class MethodAndArgsCaller extends Exception
implements Runnable {
public void run() {
try {
//根據傳遞過來的參數,此處反射調用ActivityThread.main()方法【見流程17】
mMethod.invoke(null, new Object[] { mArgs });
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(ex);
}
}
}
到此,總算是進入到了ActivityThread類的main()方法。
17. ActivityThread.main
[–> ActivityThread.java]
public static void main(String[] args) {
...
Environment.initForCurrentUser();
...
Process.setArgV0("<pre-initialized>");
//創建主線程looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
//attach到系統進程
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
//主線程進入循環狀態
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
五. 總結
Process.start()方法是阻塞操作,等待直到進程創建完成並返回相應的新進程pid,才完成該方法。
當App第一次啟動時或者啟動遠程Service,即AndroidManifest.xml文件中定義了process:remote屬性時,都需要創建進程。比如當用戶點擊桌面的某個App圖標,桌面本身是一個app(即Launcher App),那麽Launcher所在進程便是這次創建新進程的發起進程,該通過binder發送消息給system_server進程,該進程承載著整個java framework的核心服務。system_server進程從Process.start開始,執行創建進程,流程圖(以進程的視角)如下:
點擊查看大圖
上圖中,system_server
進程通過socket IPC通道向zygote
進程通信,zygote
在fork出新進程後由於fork調用一次,返回兩次,即在zygote進程中調用一次,在zygote進程和子進程中各返回一次,從而能進入子進程來執行代碼。該調用流程圖的過程:
- system_server進程(
即流程1~3
):通過Process.start()方法發起創建新進程請求,會先收集各種新進程uid、gid、nice-name等相關的參數,然後通過socket通道發送給zygote進程; - zygote進程(
即流程4~12
):接收到system_server進程發送過來的參數後封裝成Arguments對象,圖中綠色框forkAndSpecialize()方法是進程創建過程中最為核心的一個環節(詳見流程6),其具體工作是依次執行下面的3個方法:- preFork():先停止Zygote的4個Daemon子線程(java堆內存整理線程、對線下引用隊列線程、析構線程以及監控線程)的運行以及初始化gc堆;
- nativeForkAndSpecialize():調用linux的fork()出新進程,創建Java堆處理的線程池,重置gc性能數據,設置進程的信號處理函數,啟動JDWP線程;
- postForkCommon():在啟動之前被暫停的4個Daemon子線程。
- 新進程(
即流程13~15
):進入handleChildProc()方法,設置進程名,打開binder驅動,啟動新的binder線程;然後設置art虛擬機參數,再反射調用目標類的main()方法,即Activity.main()方法。
再之後的流程,如果是startActivity則將要進入Activity的onCreate/onStart/onResume等生命周期;如果是startService則將要進入Service的onCreate等生命周期。
Tips: [小節11]RuntimeInit.java的方法nativeZygoteInit()會調用到onZygoteInit(),這個過程中有startThreadPool()創建Binder線程池。也就是說每個進程無論是否包含任何activity等組件,一定至少會包含一個Binder線程。
轉自:http://gityuan.com/2016/03/26/app-process-create/
理解Android進程創建流程(轉)