Android系統中的進程管理:進程的創建
對于操作系統來說,進程管理是其最重要的職責之一。
考慮到這部分的內容較多,因此會拆分成幾篇文章來講解。
本文是進程管理系統文章的第一篇,會講解Android系統中的進程創建。
本文適合Android平臺的應用程序開發者,也適合對于Android系統內部實現感興趣的讀者。
概述
Android系統以Linux內核為基礎,所以對于進程的管理自然離不開Linux本身提供的機制。例如:
-
通過fork來創建進行
-
通過信號量來管理進程
-
通過proc文件系統來查詢和調整進程狀態
等
對于Android來說,進程管理的主要內容包括以下幾個部分內容:
-
進程的創建
-
進程的優先級管理
-
進程的內存管理
-
進程的回收和死亡處理
本文會專門講解進程的創建,其余部分將在后面的文章中講解。
主要模塊
為了便于下文的講解,這里先介紹一下Android系統中牽涉到進程創建的幾個主要模塊。
同時為了便于讀者更詳細的了解這些模塊,這里也同時提供了這些模塊的代碼路徑。
這里提到的代碼路徑是指AOSP的源碼數中的路徑。
本文以Android N版本的代碼為示例,所用到的 Source Code Tags 是:android-7.0.0_r1。
相關模塊:
-
app_process
代碼路徑: frameworks/base/cmds/app_process
說明:app_process是一個可執行程序,該程序的主要作用是啟動 zygote 和 system_server 進程。
-
Zygote
代碼路徑: frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
說明: zygote 進程是所有應用進程的父進程,這是系統中一個非常重要的進程,下文我們會詳細講解。
-
ActivityManager
代碼路徑: frameworks/base/services/core/java/com/android/server/am/
說明:am是ActivityManager的縮寫。
這個目錄下的代碼負責了Android全部四大組件(Activity,Service,ContentProvider,BroadcastReceiver)的管理,并且還掌控了所有應用程序進程的創建和進程的優先級管理。
因此,這個部分的內容將是本系列文章講解的重點。
進程與線程
Android官方開發網站的這篇文章: Processes and Threads 非常好的介紹了Android系統中進程相關的一些基本概念和重要知識。
在閱讀下文之前,請務必將這篇文章瀏覽一遍。
關于進程
在Android系統中,進程可以大致分為 系統進程 和 應用進程 兩大類。
系統進程是系統內置的(例如: init , zygote , system_server 進程),屬于操作系統必不可少的一部分。系統進程的作用在于:
-
管理硬件設備
-
提供訪問設備的基本能力
-
管理應用進程
應用進程是指應用程序運行的進程。這些應用程序可能是系統出廠自帶的(例如Launcher,電話,短信等應用),也可能是用戶自己安裝的(例如:微信,支付寶等)。
系統進程的數量通常是固定的(出廠或者系統升級之后就確定了),并且系統進程通常是一直存活,常駐內存的。系統進程的異常退出將可能導致設備無法正常使用。
而應用程序和應用進程在每個人使用的設備上通常是各不一樣的。如何管理好這些不確定的應用進程,就是操作系統本身要仔細考慮的內容。也是衡量一個操作系統好壞的標準之一。
在本文中,我們會介紹 init , zygote 和 system_server 三個系統進程。
除此之外, 本系列文章將會把主要精力集中在講解Android系統如何管理應用進程上 。
init進程
init進程是一切的開始,在Android系統中,所有進程的進程號都是不確定的,唯獨init進程的進程號一定是1。
因為這個進程一定是系統起來的第一個進程。并且,init進程掌控了整個系統的啟動邏輯。
我們知道,Android可能運行在各種不同的平臺,不同的設備上。因此,啟動的邏輯是不盡相同的。
為了適應各種平臺和設備的需求,init進程的初始化工作通過 init.rc 配置文件來管理。
你可以在AOSP源碼的 system/core/rootdir/ 路徑找到這些配置文件。
配置文件的主入口文件是 init.rc ,這個文件會通過 import 引入其他幾個文件。
在本文中,我們統稱這些文件為 init.rc 。
init.rc通過 Android Init Language 來進行配置。
init.rc中配置了系統啟動的時候該做哪些事情,以及啟動哪些系統進程。
這其中有兩個特別重要的進程就是: zygote 和 system_server 進程。
-
zygote的中文意思是“受精卵“。這是一個很有寓意的名稱:所有的應用進程都是由 zygote fork出來的子進程,因此zygote進程是所有應用進程的父進程。
-
system_server這個進程正如其名稱一樣,這是一個系統服務器。Framework層的幾乎所有服務都位于這個進程中。這其中就包括管理四大組件的 ActivityManagerService 。
Zygote進程
init.rc文件會根據平臺不一樣,選擇下面幾個文件中的一個來啟動zygote進程:
-
init.zygote32.rc
-
init.zygote32_64.rc
-
init.zygote64.rc
-
init.zygote64_32.rc
這幾個文件的內容是大致一致的,僅僅是為了不同平臺服務的。這里我們以init.zygote32.rc的文件為例,來看看其中的內容:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
在這段配置文件中,啟動了一個名稱叫做 zygote 的服務進程。這個進程是通過 /system/bin/app_process 這個可執行程序創建的。
并且在啟動這個可執行程序的時候,傳遞了`-Xzygote /system/bin --zygote --start-system-server
class main` 這些參數。
要知道這里到底做了什么,我們需要看一下app_process的源碼。
app_process的源碼在這個路徑: frameworks/base/cmds/app_process/app_main.cpp 。
這個文件的main函數的有如下代碼:
int main(int argc, char* const argv[])
{
...
while (i < argc) {
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true;
...
}
...
if (!className.isEmpty()) {
...
} else {
...
if (startSystemServer) {
args.add(String8("start-system-server"));
}
}
...
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
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.");
return 10;
}
}
這里會判斷,
-
如果執行這個命令時帶了 --zygote 參數,就會通過 runtime.start 啟動 com.android.internal.os.ZygoteInit 。
-
如果參數中帶有 --start-system-server 參數,就會將 start-system-server 添加到args中。
這段代碼是C++實現的。在執行這段代碼的時候還沒有任何Java的環境。而 runtime.start 就是啟動Java虛擬機,并在虛擬機中啟動指定的類。于是接下來的邏輯就在ZygoteInit.java中了。
這個文件的 main 函數主要代碼如下:
public static void main(String argv[]) {
...
try {
...
boolean startSystemServer = false;
String socketName = "zygote";
String abiList = null;
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
...
}
}
...
registerZygoteSocket(socketName);
...
preload();
...
Zygote.nativeUnmountStorageOnInit();
ZygoteHooks.stopZygoteNoThreadCreation();
if (startSystemServer) {
startSystemServer(abiList, socketName);
}
Log.i(TAG, "Accepting command socket connections");
runSelectLoop(abiList);
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}
在這段代碼中,我們主要關注如下幾行:
-
通過 registerZygoteSocket(socketName); 注冊 Zygote Socket
-
通過 preload(); 預先加載所有應用都需要的公共資源
-
通過 startSystemServer(abiList, socketName); 啟動 system_server
-
通過 runSelectLoop(abiList); 在Looper上等待連接
這里需要說明的是:zygote進程啟動之后,會啟動一個socket套接字,并通過Looper一直在這個套接字上等待連接。
所有應用進程都是通過發送數據到這個套接字上,然后由zygote進程創建的。
這里還有一點說明的是:
在Zygote進程中,會通過 preload 函數加載需要應用程序都需要的公共資源。
預先加載這些公共資源有如下兩個好處:
-
加快應用的啟動速度因為這些資源已經在zygote進程啟動的時候加載好了
-
通過共享的方式節省內存這是Linux本身提供的機制:父進程已經加載的內容可以在子進程中進行共享,而不用多份數據拷貝(除非子進程對這些數據進行了修改。)
preload的資源主要是Framework相關的一些基礎類和Resource資源,而這些資源正是所有應用都需要的:
開發者通過Android SDK開發應用所調用的API實現都在Framework中。
static void preload() {
Log.d(TAG, "begin preload");
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "BeginIcuCachePinning");
beginIcuCachePinning();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadClasses");
preloadClasses();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadResources");
preloadResources();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
preloadOpenGL();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
preloadSharedLibraries();
preloadTextResources();
WebViewFactory.prepareWebViewInZygote();
endIcuCachePinning();
warmUpJcaProviders();
Log.d(TAG, "end preload");
}
system_server進程
上文已經提到,zygote進程起來之后會根據需要啟動 system_server 進程。
system_server 進程中包含了大量的系統服務。例如:
-
負責網絡管理的NetworkManagementService
-
負責窗口管理的WindowManagerService
-
負責震動管理的VibratorService
-
負責輸入管理的InputManagerService
等等。關于system_server,我們今后會其他的文章中專門講解,這里不做過多說明。
在本文中,我們只關注 system_server 中的 ActivityManagerService 這個系統服務。
ActivityManagerService
上文中提到:zygote進程在啟動之后會啟動一個socket,然后一直在這個socket等待連接。
而會連接它的就是 ActivityManagerService 。因為 ActivityManagerService 掌控了所有應用進程的創建。
所有應用程序的進程都是由 ActivityManagerService 通過socket發送請求給 Zygote 進程,然后由zygote fork創建的。
ActivityManagerService 通過 Process.start 方法來請求 zygote 創建進程:
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 {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
這個函數會將啟動進程所需要的參數組裝好,并通過socket發送給zygote進程。然后zygote進程根據發送過來的參數將進程fork出來。
在 ActivityManagerService 中,調用 Process.start 的地方是下面這個方法:
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
...
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
...
}
下文中我們會看到,所有四大組件進程的創建,都是調用這里的 startProcessLocked 這個方法而創建的。
對于每一個應用進程,在 ActivityManagerService 中,都有一個 ProcessRecord 與之對應。這個對象記錄了應用進程的所有詳細狀態。
PS:對于 ProcessRecord 的內部結構,在下一篇文章中,我們會講解。
為了查找方便,對于每個 ProcessRecord 會存在下面兩個集合中。
-
按名稱和uid組織的集合:
/**
* All of the applications we currently have running organized by name.
* The keys are strings of the application package name (as
* returned by the package manager), and the keys are ApplicationRecord
* objects.
*/
final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>();
-
按pid組織的集合:
/**
* All of the processes we currently have running organized by pid.
* The keys are the pid running the application.
*
* <p>NOTE: This object is protected by its own lock, NOT the global
* activity manager lock!
*/
final SparseArray<ProcessRecord> mPidsSelfLocked = new SparseArray<ProcessRecord>();
下面這幅圖小節了上文的這些內容:
關于應用組件
Processes and Threads 提到:
“ 當某個應用組件啟動且該應用沒有運行其他任何組件時,Android 系統會使用單個執行線程為應用啟動新的 Linux 進程。 ”
因此,四大組件中的任何一個先起來都會導致應用進程的創建。下文我們就詳細看一下,它們啟動時,各自是如何導致應用進程的創建的。
PS:四大組件的管理本身又是一個比較大的話題,限于篇幅關系,這里不會非常深入的講解,這里主要是講解四大組件與進程創建的關系。
在應用程序中,開發者通過:
-
startActivity(Intent intent) 來啟動Activity
-
startService(Intent service) 來啟動Service
-
sendBroadcast(Intent intent) 來發送廣播
-
ContentResolver 中的接口來使用ContentProvider
這其中, startActivity , startService 和 sendBroadcast 還有一些重載方法。
其實這里提到的所有這些方法,最終都是通過Binder調用到ActivityManagerService中,由其進行處理的。
這里特別說明一下:應用進程和 ActivityManagerService 所在進程(即 system_server 進程)是相互獨立的,兩個進程之間的方法通常是不能直接互相調用的。
而Android系統中,專門提供了Binder框架來提供進程間通訊和方法調用的能力。
調用關系如下圖所示:
Activity與進程創建
在ActivityManagerService中,對每一個運行中的Activity都有一個 ActivityRecord 對象與之對應,這個對象記錄Activity的詳細狀態。
ActivityManagerService中的 startActivity 方法接受 Context.startActivity 的請求,該方法代碼如下:
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
resultWho, requestCode, startFlags, profilerInfo, bOptions,
UserHandle.getCallingUserId());
}
Activity的啟動是一個非常復雜的過程。這里我們簡單介紹一下背景知識:
-
ActivityManagerService中通過Stack和Task來管理Activity
-
每一個Activity都屬于一個Task,一個Task可能包含多個Activity。一個Stack包含多個Task
-
ActivityStackSupervisor類負責管理所有的Stack
-
Activity的啟動過程會牽涉到:
-
Intent的解析
-
Stack,Task的查詢或創建
-
Activity進程的創建
-
Activity窗口的創建
-
Activity的生命周期調度
-
Activity的管理結構如下圖所示:
在Activity啟動的最后,會將前一個Activity pause,將新啟動的Activity resume以便被用戶看到。
在這個時候,如果發現新啟動的Activity進程還沒有啟動,則會通過 startSpecificActivityLocked 將其啟動。整個調用流程如下:
-
ActivityManagerService.activityPaused =>
-
ActivityStack.activityPausedLocked =>
-
ActivityStack.completePauseLocked =>
-
ActivityStackSupervisor.ensureActivitiesVisibleLocked =>
-
ActivityStack.makeVisibleAndRestartIfNeeded =>
-
ActivityStackSupervisor.startSpecificActivityLocked =>
-
ActivityManagerService.startProcessLocked
ActivityStackSupervisor.startSpecificActivityLocked 關鍵代碼如下:
void startSpecificActivityLocked(ActivityRecord r,
boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
r.task.stack.setLaunchTime(r);
if (app != null && app.thread != null) {
...
}
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);
}
這里的 ProcessRecord app 描述了Activity所在進程。
Service與進程創建
Service的啟動相對于Activity來說要簡單一些。
在ActivityManagerService中,對每一個運行中的Service都有一個 ServiceRecord 對象與之對應,這個對象記錄Service的詳細狀態。
ActivityManagerService中的 startService 方法處理 Context.startService API的請求,相關代碼:
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, String callingPackage, int userId)
throws TransactionTooLargeException {
...
synchronized(this) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
ComponentName res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid, callingPackage, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}
這段代碼中的 mServices 對象是 ActiveServices 類型的,這個類專門負責管理活動的Service。
啟動Service的調用流程如下:
-
ActivityManagerService.startService =>
-
ActiveServices.startServiceLocked =>
-
ActiveServices.startServiceInnerLocked =>
-
ActiveServices.bringUpServiceLocked =>
-
ActivityManagerService.startProcessLocked
ActiveServices.bringUpServiceLocked 會判斷如果Service所在進程還沒有啟動,
則通過 ActivityManagerService.startProcessLocked 將其啟動。相關代碼如下:
// Not running -- get it started, and enqueue this service record
// to be executed when the app comes up.
if (app == null && !permissionsReviewRequired) {
if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
"service", r.name, false, isolated, false)) == null) {
String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
+ r.intent.getIntent() + ": process is bad";
Slog.w(TAG, msg);
bringDownServiceLocked(r);
return msg;
}
if (isolated) {
r.isolatedProc = app;
}
}
這里的 mAm 就是ActivityManagerService。
Provider與進程創建
在ActivityManagerService中,對每一個運行中的ContentProvider都有一個 ContentProviderRecord 對象與之對應,這個對象記錄ContentProvider的詳細狀態。
開發者通過ContentResolver中的 insert , delete , update , query 這些API來使用ContentProvider。在ContentResolver的實現中,無論使用這里的哪個接口,ContentResolver都會先通過 acquireProvider 這個方法來獲取到一個類型為 IContentProvider 的遠程接口。這個遠程接口對接了ContentProvider的實現提供方。
同一個ContentProvider可能同時被多個模塊使用,而調用ContentResolver接口的進程只是ContentProvider的一個客戶端而已,真正的ContentProvider提供方是運行自身的進程中的,兩個進程的通訊需要通過Binder的遠程接口形式來調用。如下圖所示:
ContentResolver.acquireProvider 最終會調用到 ActivityManagerService.getContentProvider 中,該方法代碼如下:
@Override
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String name, int userId, boolean stable) {
enforceNotIsolatedCaller("getContentProvider");
if (caller == null) {
String msg = "null IApplicationThread when getting content provider "
+ name;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
// The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
// with cross-user grant.
return getContentProviderImpl(caller, name, null, stable, userId);
}
而在 getContentProviderImpl 這個方法中,會判斷對應的ContentProvider進程有沒有啟動,
如果沒有,則通過 startProcessLocked 方法將其啟動。
Receiver與進程創建
開發者通過 Context.sendBroadcast 接口來發送廣播。 ActivityManagerService.broadcastIntent 方法了對應廣播發送的處理。
廣播是一種一對多的消息形式,廣播接受者的數量是不確定的。因此發送廣播本身可能是一個很耗時的過程(因為要逐個通知)。
在ActivityManagerService內部,是通過隊列的形式來管理廣播的:
-
BroadcastQueue 描述了一個廣播隊列
-
BroadcastRecord 描述了一個廣播事件
在 ActivityManagerService 中,如果收到了一個發送廣播的請求,會先創建一個 BroadcastRecord 接著將其放入 BroadcastQueue 中。
然后通知隊列自己去處理這個廣播。然后 ActivityManagerService 自己就可以繼續處理其他請求了。
廣播隊列本身是在另外一個線程處理廣播的發送的,這樣保證的 ActivityManagerService 主線程的負載不會太重。
在 BroadcastQueue.processNextBroadcast(boolean fromMsg) 方法中真正實現了通知廣播事件到接受者的邏輯。在這個方法,如果發現接受者(即BrodcastReceiver)還沒有啟動,便會通過 ActivityManagerService.startProcessLocked 方法將其啟動。相關如下所示:
final void processNextBroadcast(boolean fromMsg) {
...
// Hard case: need to instantiate the receiver, possibly
// starting its application process to host it.
ResolveInfo info =
(ResolveInfo)nextReceiver;
ComponentName component = new ComponentName(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);
...
// Not running -- get it started, to be executed when the app comes up.
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Need to start app ["
+ mQueueName + "] " + targetProcess + " for broadcast " + r);
if ((r.curApp=mService.startProcessLocked(targetProcess,
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
"broadcast", r.curComponent,
(r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
== null) {
// Ah, this recipient is unavailable. Finish it if necessary,
// and mark the broadcast record as ready for the next.
Slog.w(TAG, "Unable to launch app "
+ info.activityInfo.applicationInfo.packageName + "/"
+ info.activityInfo.applicationInfo.uid + " for broadcast "
+ r.intent + ": process is bad");
logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
r.state = BroadcastRecord.IDLE;
return;
}
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;
}
}
至此,四大組件的啟動就已經分析完了。
結束語
進程管理本身是一個非常大的話題,本文講解了Android系統中進程創建的相關內容。
來自:https://segmentfault.com/a/1190000007290725