從源碼角度看AMS.startProcessLocked

NO IMAGE

簡介

(本文原地址在我的博客CheapTalks, 歡迎大家來看看~)

注:本篇文章的所有源碼與註釋都可以在YogiAi/Process.start中找到,只想閱讀代碼的同學可以直奔主題。

眾所周知,Android 系統是基於 Linux 內核的移動操作系統。而 Linux 又是通過 fork 來複制進程,複製的時候只是創建唯一識別符等輕量操作,真正對於資源的使用是藉助了寫時複製的機制(copy-on-write)。進程與線程的概念在 Linux 的世界中只有資源擁有的差別,本質上它們的內核實現都使用了 task_struct 這同一個結構體,都擁有著各自的 PID, PPID。

在 Android 自成的上層 framework 世界中,進程的概念被層層的封裝後已經很模糊了,基本開發者完成開發過程只需熟悉Activity, BroadcastReceiver, Service等四大組件的作用與使用場景,再加上網絡訪問、數據存儲、UI 繪製等等組合而成的業務邏輯即可完成一個很不錯的應用。

然而研究 Android 的進程啟動時機與實現原理對於進階學習還是大有裨益的,不僅能夠學到進程線程的內功知識,也能夠學到設計大師的封裝奧妙。Android 應用進程的創建是通過 fork Zygote 進程來實現的,所以所有的應用進程的 PPID 都是 Zygote 的 PID。複製 Zygote 的實例後會得到一個虛擬機實例,除此之外,新建的進程還會獲取到一個消息循環、 Binder 的進程通信池以及一個 Binder 主線程。

在這一篇文章中,我將詳細解析Android 進程的創建過程。

system_server 端

AMS.startProcessLocked

ActivityManagerService 運行在 system_server 進程,為應用提供各種服務。系統可能因為發送廣播,啟動服務,運行 Activity 等原因啟動一個新的進程。這時候會 binder call 到 AMS,調用 startProcessLocked 方法進行處理。

    final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
String hostingType, ComponentName hostingName, boolean allowWhileBooting) {
...
if (app == null) {
app = newProcessRecordLocked(null, info, processName);
mProcessNames.put(processName, info.uid, app);
} else {
// If this is a new package in the process, add the package to the list
app.addPackage(info.packageName);
}
...
startProcessLocked(app, hostingType, hostingNameStr);
return (app.pid != 0) ? app : null;
}
    private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
...
try {
// 獲得創建的應用程序進程的用戶 ID,用戶組 ID
int uid = app.info.uid;
int[] gids = null;
try {
gids = mContext.getPackageManager().getPackageGids(
app.info.packageName);
} catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "Unable to retrieve gids", e);
}
...
// 創建進程,並制定這個進程的路口時 ActivityThread 的靜態方法 main
int pid = Process.start("android.app.ActivityThread",
mSimpleProcessManagement ? app.processName : null, uid, uid,
gids, debugFlags, null);
...
}

Process.start

進程的啟動入口在 Process.start 方法,start 方法只做了初始化參數的工作,真正的複製進程工作是在 zygote 進行完成的,system_server 與 zygote 進程的通信使用的 socket。system_server 會將參數信息寫入到 socket 中,然後阻塞等待 zygote 的迴應。

    public static final int start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags,
String[] zygoteArgs)
{
// 是否支持 Binder 進程間通信的機制
if (supportsProcesses()) {
try {
// 如果支持, 就請求 Zygote 來創建一個應用程序進程
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
} else {
// Running in single-process mode
// 如果不支持, 就使用一個線程來模擬進程
Runnable runnable = new Runnable() {
public void run() {
Process.invokeStaticMain(processClass);
}
};
// Thread constructors must not be called with null names (see spec). 
if (niceName != null) {
new Thread(runnable, niceName).start();
} else {
new Thread(runnable).start();
}
return 0;
}
}
    private static int startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int debugFlags,
String[] extraArgs)
throws ZygoteStartFailedEx {
int pid;
synchronized(Process.class) {
// 初始化進程的啟動參數列表
ArrayList<String> argsForZygote = new ArrayList<String>();
...
// 初始化完畢
pid = zygoteSendArgsAndGetPid(argsForZygote);
}     
...
return pid;
}
    private static int zygoteSendArgsAndGetPid(ArrayList<String> args)
throws ZygoteStartFailedEx {
int pid;
// 創建一個連接到 Zygote 的 LocalSocket 對象
openZygoteSocketIfNeeded();
try {
// 將要創建的應用程序的進程啟動參數傳到 LocalSocket 對象中
sZygoteWriter.write(Integer.toString(args.size()));
sZygoteWriter.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");
}
sZygoteWriter.write(arg);
sZygoteWriter.newLine();
}
sZygoteWriter.flush();
// Should there be a timeout on this?
// 通過 Socket 讀取 Zygote 創建成功的進程 PID
// Socket 對端的請求在 ZygoteInit.runSelectLoopMode中進行處理
// 讀取成功之後會對 PID 進行檢查,無異常的話就會推出
pid = sZygoteInputStream.readInt();
if (pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
} catch (IOException ex) {
...
}
return pid;
}

zygote 端

ZygoteInit.main

zygote 進程的 Socket服務端是在此處進行創建初始化的,當接受到 socket 客戶端的請求時會進行處理。本質上是啟動了一個無限循環來處理客戶端的請求。這裡 system_server 與 zygote 是典型的 C/S 架構。

    public static void main(String argv[]) {
try {
VMRuntime.getRuntime().setMinimumHeapSize(5 * 1024 * 1024);
// Start profiling the zygote initialization.
SamplingProfilerIntegration.start();
// 在 Zygote 服務端註冊一個 Socket Server, 用來創建新進程
registerZygoteSocket();
...
if (ZYGOTE_FORK_MODE) {
runForkMode();
} else {
// 開始處理進程創建的 Socket 請求
runSelectLoopMode();
}
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
// ActivityThread 的靜態方法在這被回調執行
// 這裡間接調用方法,巧妙的利用了異常處理機制來清理前面的調用棧
caller.run();
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}
    private static void runSelectLoopMode() throws MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList();
ArrayList<ZygoteConnection> peers = new ArrayList();
FileDescriptor[] fdArray = new FileDescriptor[4];
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
int loopCount = GC_LOOP_COUNT;
while (true) {
...
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);
}
}
}
}

ZygoteConnection.runOnce

在 zygote 進程主要執行這三個操作:

  1. 調用 Zygote.forkAndSpecialize 進行進程複製操作
  2. 調用 handleChildProc 處理新建進程資源初始化,如創建 Binder 線程池,啟動一個主線程消息隊列
  3. 調用 handleParentProc 將新建進程的 PID 返回給 system_server,表示創建結果
    boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
// 讀取啟動參數
args = readArgumentList();
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
Log.w(TAG, "IOException on command socket " + ex.getMessage());
closeSocket();
return true;
}
...
int pid;
try {
// 將 String 數組封裝成 Arguments
parsedArgs = new Arguments(args);
applyUidSecurityPolicy(parsedArgs, peer);
applyDebuggerSecurityPolicy(parsedArgs);
applyRlimitSecurityPolicy(parsedArgs, peer);
applyCapabilitiesSecurityPolicy(parsedArgs, peer);
int[][] rlimits = null;
if (parsedArgs.rlimits != null) {
rlimits = parsedArgs.rlimits.toArray(intArray2d);
}
// fork 操作
// 將會有兩個進程從這裡返回
// PID=0意味著是子進程
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids, parsedArgs.debugFlags, rlimits);
} catch (IllegalArgumentException ex) {
logAndPrintError (newStderr, "Invalid zygote arguments", ex);
pid = -1;
} catch (ZygoteSecurityException ex) {
logAndPrintError(newStderr,
"Zygote security policy prevents request: ", ex);
pid = -1;
}
if (pid == 0) {
// in child
// 創建出的新進程
handleChildProc(parsedArgs, descriptors, newStderr);
// should never happen
return true;
} else { /* pid != 0 */
// in parent...pid of < 0 means failure
// 父進程將在這裡進行處理
return handleParentProc(pid, descriptors, parsedArgs);
}
}
    private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {
...
if (parsedArgs.runtimeInit) {
// 在新創建的應用程序進程中初始化運行時庫,創建一個 Binder 線程池
RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
} else {
ClassLoader cloader;
// 獲取 classloader
if (parsedArgs.classpath != null) {
cloader
= new PathClassLoader(parsedArgs.classpath,
ClassLoader.getSystemClassLoader());
} else {
cloader = ClassLoader.getSystemClassLoader();
}
// 讀取 ActivityThread 類名
String className;
try {
className = parsedArgs.remainingArgs[0];
} catch (ArrayIndexOutOfBoundsException ex) {
logAndPrintError (newStderr,
"Missing required class name argument", null);
return;
}
String[] mainArgs
= new String[parsedArgs.remainingArgs.length - 1];
System.arraycopy(parsedArgs.remainingArgs, 1,
mainArgs, 0, mainArgs.length);
try {
// 觸發 ActivityThread.main 方法
ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
} catch (RuntimeException ex) {
logAndPrintError (newStderr, "Error starting. ", ex);
}
}
}
    private boolean handleParentProc(int pid,
FileDescriptor[] descriptors, Arguments parsedArgs) {
...
try {
// 在這裡通過 Socket 通知對端進程已經創建成功,並返回 PID
mSocketOutStream.writeInt(pid);
} catch (IOException ex) {
Log.e(TAG, "Error reading from command socket", ex);
return true;
}
...
return false;
}

Zygote.forkAndSpecialize

進行進程創建的工作也是主要做了三件事:

  1. 調用 ZygoteHooks.preFork 停止上次創建進程的 daemon 線程
  2. 調用 nativeForkAndSpecialize 在 c++層創建新進程
  3. 調用 ZygoteHooks.postForkCoomon 在父進程、子進程中啟動 daemon 線程
    public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, String instructionSet, String appDataDir) {
VM_HOOKS.preFork();
// Resets nice priority for zygote process.
resetNicePriority();
// 進入內核層進行進程的 fork 操作
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
fdsToIgnore, instructionSet, appDataDir);
...
VM_HOOKS.postForkCommon();
return pid;
}

native層

Zygote.cpp

這裡先看進程的複製

static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring se_name,
jintArray fdsToClose,
jintArray fdsToIgnore,
jstring instructionSet, jstring appDataDir) {
...
return ForkAndSpecializeCommon(env, uid, gid, gids, runtime_flags,
rlimits, capabilities, capabilities, mount_external, se_info,
se_name, false, fdsToClose, fdsToIgnore, instructionSet, appDataDir);
}
// Utility routine to fork zygote and specialize the child process.
static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
jint runtime_flags, jobjectArray javaRlimits,
jlong permittedCapabilities, jlong effectiveCapabilities,
jint mount_external,
jstring java_se_info, jstring java_se_name,
bool is_system_server, jintArray fdsToClose,
jintArray fdsToIgnore,
jstring instructionSet, jstring dataDir) {
SetSigChldHandler();
sigset_t sigchld;
sigemptyset(&sigchld);
sigaddset(&sigchld, SIGCHLD);
...
// 進行 fork
pid_t pid = fork();
if (pid == 0) {
// 子進程操作
PreApplicationInit();
// Clean up any descriptors which must be closed immediately
DetachDescriptors(env, fdsToClose);
...
// Keep capabilities across UID change, unless we're staying root.
if (uid != 0) {
EnableKeepCapabilities(env);
}
SetInheritable(env, permittedCapabilities);
DropCapabilitiesBoundingSet(env);
...
if (!is_system_server) {
// 如果不是 system_server 進程,需要創建進程組
int rc = createProcessGroup(uid, getpid());
if (rc != 0) {
if (rc == -EROFS) {
ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
} else {
ALOGE("createProcessGroup(%d, %d) failed: %s", uid, pid, strerror(-rc));
}
}
}
SetGids(env, javaGids);
SetRLimits(env, javaRlimits);
...
SetCapabilities(env, permittedCapabilities, effectiveCapabilities, permittedCapabilities);
// 設置進程調度策略
SetSchedulerPolicy(env);
} else if (pid > 0) {
// Zygote 進程將會執行這裡
...
}
return pid;
}

fork.cpp

調用 clone 方法進行進程的複製,複製完成後父進程與子進程會調用各自的回調方法

int fork() {
__bionic_atfork_run_prepare();
pthread_internal_t* self = __get_thread();
int result = clone(nullptr,
nullptr,
(CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD),
nullptr,
nullptr,
nullptr,
&(self->tid));
if (result == 0) {
// Update the cached pid, since clone() will not set it directly (as
// self->tid is updated by the kernel).
self->set_cached_pid(gettid());
// 調用子進程的回調,具體代碼參考 pthread.atfork.cpp
__bionic_atfork_run_child();
} else {
__bionic_atfork_run_parent();
}
return result;
}

pthread_atfork.cpp

  template<typename F>
void walk_forward(F f) {
for (atfork_t* it = first_; it != nullptr; it = it->next) {
f(it);
}
}
void __bionic_atfork_run_prepare() {
// We lock the atfork list here, unlock it in the parent, and reset it in the child.
// This ensures that nobody can modify the handler array between the calls
// to the prepare and parent/child handlers.
pthread_mutex_lock(&g_atfork_list_mutex);
// Call pthread_atfork() prepare handlers. POSIX states that the prepare
// handlers should be called in the reverse order of the parent/child
// handlers, so we iterate backwards.
g_atfork_list.walk_backwards([](atfork_t* it) {
if (it->prepare != nullptr) {
it->prepare();
}
});
}
void __bionic_atfork_run_child() {
g_atfork_list_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
pthread_mutex_lock(&g_atfork_list_mutex);
g_atfork_list.walk_forward([](atfork_t* it) {
if (it->child != nullptr) {
it->child();
}
});
pthread_mutex_unlock(&g_atfork_list_mutex);
}

ZygoteHooks.cc

再看在執行進程 fork 前後的虛擬機操作

static jlong ZygoteHooks_nativePreFork(JNIEnv* env, jclass) {
Runtime* runtime = Runtime::Current();
CHECK(runtime->IsZygote()) << "runtime instance not started with -Xzygote";
runtime->PreZygoteFork();
// Grab thread before fork potentially makes Thread::pthread_key_self_ unusable.
return reinterpret_cast<jlong>(ThreadForEnv(env));
}
static void ZygoteHooks_nativePostForkChild(JNIEnv* env, jclass, jlong token, jint debug_flags,
jstring instruction_set) {
Thread* thread = reinterpret_cast<Thread*>(token);
// Our system thread ID, etc, has changed so reset Thread state.
thread->InitAfterFork();
EnableDebugFeatures(debug_flags);
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;
}
Runtime::Current()->DidForkFromZygote(env, action, isa_string.c_str());
} else {
Runtime::Current()->DidForkFromZygote(env, Runtime::NativeBridgeAction::kUnload, nullptr);
}
}

runtime.cc

自此,新進程創建成功,相關的環境也已經初始化完畢

void Runtime::PreZygoteFork() {
heap_->PreZygoteFork();
}
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;
}
}
// Create the thread pools.
// 創建 java 堆處理線程池
heap_->CreateThreadPool();
if (jit_options_.get() != nullptr && jit_.get() == nullptr) {
// Create the JIT if the flag is set and we haven't already create it (happens for run-tests).
// 創建 JIT
CreateJit();
jit_->CreateInstrumentationCache(jit_options_->GetCompileThreshold());
jit_->CreateThreadPool();
}
// 設置信號處理函數
StartSignalCatcher();
// Start the JDWP thread. If the command-line debugger flags specified "suspend=y",
// this will pause the runtime, so we probably want this to come last.
// 啟動JDWP線程,當命令debuger的flags指定"suspend=y"時,則暫停runtime
Dbg::StartJdwp();
}

ActivityThread.main

回到 java framework 層,新進程啟動成功後會運行 ActivityThread 的 main, 開啟一個主線程的消息隊列,等待與 system_server 進行交互。

    public static final void main(String[] args) {
SamplingProfilerIntegration.start();
Process.setArgV0("<pre-initialized>");
// 創建主線程消息循環
// 每一個應用程序啟動完成之後都會自動的進行這個消息循環
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
// 創建 ActivityThread 實例
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
if (Process.supportsProcesses()) {
throw new RuntimeException("Main thread loop unexpectedly exited");
}
thread.detach();
String name = (thread.mInitialApplication != null)
? thread.mInitialApplication.getPackageName()
: "<unknown>";
Slog.i(TAG, "Main thread of " + name + " is now exiting");
}

總結

  1. Android 應用的進程創建入口點在 AMS.startProcessLocked, 通過調用 Process.start 來發出一個 socket request 來請求 zygote 進程進行 fork, 創建一個新的進程。
  2. 本質上覆制進程只需要調用 fork 方法即可,但是 Android 對於新進程的操作有著額外的封裝。一個新進程的誕生做了以下三點工作:
    • 寫時複製了 zygote 進程
    • 開啟了一個 Binder 進程池方便進程 IPC 操作
    • 開啟了一個主線程消息循環
  3. 進程的創建工作是持有者 AMS 的鎖進行的,如果 Zygote 因為 CPU 負載過高或者內存缺乏等等原因創建進程的速度變慢,使得 system_server 其它 Binder 線程阻塞,那麼很有可能會造成第三方應用間接的耗時和ANR

相關文章

微信小程序的知識點

微信小程序中的swiper用法

總結微信小程序中的一些知識

從源碼角度看CPU相關日誌