1. 前言
OS世界的三大天尊之一,ActivityManagerService,下文將稱其AMS,他的作用包括進程管理,內存管理,組件管理等,作用毋庸置疑,學習AMS,跟蹤過程,弄懂原理,對系統開發定製有極大的幫助,對應用開發也可以借鑑他的架構設計自己的獨有模式,並且可以全局分析上層可能出現的問題。有時間就填坑吧,哈哈哈。
2. UI相關
作爲Android系統核心應用,SystemUI負責反饋系統及應用狀態並與用戶保持大量的交互。耳熟能詳的三欄:StatusBar(狀態欄)、NavigationBar(導航欄)與Notification Panel(通知欄),以及Recents(近期任務界面),使用起來方便又快捷。另外Keyguard(鎖屏界面)也是屬於SystemUI的一部分。並且在Android8.0代碼中,Keyguard模塊已經從外部被合併到SystemUI源碼目錄下。
3. 任務棧相關
4. 組件相關
AMS與四大組件的註冊,啓動,註銷相關,這裏只說一下Activity組件的註冊,啓動,註銷過程,此過程大體如下:
中間細節圖如下:
上圖是根據android4.4.2所繪,中間還有一個細節需要提一下,如下:
framworks/base/services/java/com/android/server/am/ActivityStackSupervisor.java
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);
//1
if (app != null && app.thread != null) {
try {
if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
|| !"android".equals(r.info.packageName)) {
// Don't add this if it is a platform component that is marked
// to run in multiple processes, because this is actually
// part of the framework so doesn't make sense to track as a
// separate apk in the process.
app.addPackage(r.info.packageName, mService.mProcessStats);
}
realStartActivityLocked(r, app, andResume, checkConfig);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity "
+ r.intent.getComponent().flattenToShortString(), e);
}
}
//2
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);
}
從註釋1的判斷來看,說明當前的Activity所在的進程存在的話,則執行realStartActivityLocked,也就是上圖所繪流程。當前Activity所在的進程不存在的情況了則走註釋2處的startProcessLocked的函數。
這裏與app的冷啓動與熱啓動相關。這部分之前有寫過相關的內容,下面我則挪過來用好了,懶~
4.1 客戶端請求
frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
try {
......
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
Process.ProcessStartResult startResult= Process.start("android.app.ActivityThread",
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, null);
.......
}
}
===========/frameworks/base/core/java/android/os/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[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
===========/frameworks/base/core/java/android/os/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[] extraArgs)
throws ZygoteStartFailedEx {
synchronized(Process.class) {
……
return zygoteSendArgsAndGetResult(argsForZygote);
}
}
startViaZygote的絕大部分代碼都在處理傳遞到Zygote中的參數,與Zygote通信通過zygoteSendArgsAndGetResult()方法完成:
===========/frameworks/base/core/java/android/os/Process.java =========
private static ProcessStartResult zygoteSendArgsAndGetResult(ArrayList<String> args)
throws ZygoteStartFailedEx {
openZygoteSocketIfNeeded();//確保和Zygote通信的socket已被打開
try {
sZygoteWriter.write(Integer.toString(args.size()));
sZygoteWriter.newLine();
int sz = args.size();
for (int i = 0; i < sz; i++) {//發送請求參數到Zygote
String arg = args.get(i);
if (arg.indexOf('\n') >= 0) {
throw new ZygoteStartFailedEx(
"embedded newlines not allowed");
}
sZygoteWriter.write(arg);
sZygoteWriter.newLine();
}
sZygoteWriter.flush();
ProcessStartResult result = new ProcessStartResult();
result.pid = sZygoteInputStream.readInt();
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
result.usingWrapper = sZygoteInputStream.readBoolean();
return result;
} catch (IOException ex) {
try {
if (sZygoteSocket != null) {
sZygoteSocket.close();
}
} catch (IOException ex2) {
// we're going to fail anyway
Log.e(LOG_TAG,"I/O exception on routine close", ex2);
}
sZygoteSocket = null;
throw new ZygoteStartFailedEx(ex);
}
}
4.2 處理客戶端請求
首先是到ZygoteInit.java main函數這裏開始觀察
===/frameworks/base/core/java/com/android/internal/os/ZygoyeInit.java ====
public static void main(String argv[]) {
……
runSelectLoop();
……
}
核心代碼在runSelectLoop函數的實現中,繼續往下看
===/frameworks/base/core/java/com/android/internal/os/ZygoyeInit.java ====
private static void runSelectLoop() throws MethodAndArgsCaller {
……
white(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);
}
}
}
}
上面函數實現中,最終會調用到runOnce函數
===frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java ===
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;
}
if (args == null) {
// EOF reached.
closeSocket();
return true;
}
/** the stderr of the most recent request, if avail */
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 {
parsedArgs = new Arguments(args);
applyUidSecurityPolicy(parsedArgs, peer, peerSecurityContext);
applyRlimitSecurityPolicy(parsedArgs, peer, peerSecurityContext);
applyCapabilitiesSecurityPolicy(parsedArgs, peer, peerSecurityContext);
applyInvokeWithSecurityPolicy(parsedArgs, peer, peerSecurityContext);
applyseInfoSecurityPolicy(parsedArgs, peer, peerSecurityContext);
applyDebuggerSystemProperty(parsedArgs);
applyInvokeWithSystemProperty(parsedArgs);
int[][] rlimits = null;
if (parsedArgs.rlimits != null) {
rlimits = parsedArgs.rlimits.toArray(intArray2d);
}
if (parsedArgs.runtimeInit && parsedArgs.invokeWith != null) {
FileDescriptor[] pipeFds = Libcore.os.pipe();
childPipeFd = pipeFds[1];
serverPipeFd = pipeFds[0];
ZygoteInit.setCloseOnExec(serverPipeFd, true);
}
//fork一個新進程
pid=Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.debugFlags, rlimits,parsedArgs.mountExternal, parsedArgs.seInfo, parsedArgs.niceName);
} catch (IOException ex) {
logAndPrintError(newStderr, "Exception creating pipe", ex);
} catch (ErrnoException ex) {
logAndPrintError(newStderr, "Exception creating pipe", ex);
} catch (IllegalArgumentException ex) {
logAndPrintError(newStderr, "Invalid zygote arguments", ex);
} catch (ZygoteSecurityException ex) {
logAndPrintError(newStderr,
"Zygote security policy prevents request: ", ex);
}
try {
if (pid == 0) {//子進程
// in child
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
// should never get here, the child is expected to either
// throw ZygoteInit.MethodAndArgsCaller or exec().
return true;
} else {//父進程
// in parent...pid of < 0 means failure
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}
===frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java ===
private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {
closeSocket();//關閉子進程中,從Zygote fork過來的服務端socket
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 {
......
}
}
===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);
}
===frameworks/base/core/java/com/android/internal/os/RuntimeInit.java ==
private static void applicationInit(int targetSdkVersion, String[] argv)
throws ZygoteInit.MethodAndArgsCaller {
// If the application calls System.exit(), terminate the process
// immediately without running any shutdown hooks. It is not possible to
// shutdown an Android application gracefully. Among other things, the
// Android runtime shutdown hooks close the Binder driver, which can cause
// leftover running threads to crash before the process actually exits.
nativeSetExitWithoutCleanup(true);
// We want to be fairly aggressive about heap utilization, to avoid
// holding on to a lot of memory that isn't needed.
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());
// let the process exit
return;
}
// Remaining arguments are passed to the start class's static main
invokeStaticMain(args.startClass, args.startArgs);
}
===frameworks/base/core/java/com/android/internal/os/RuntimeInit.java ===
private static void invokeStaticMain(String className, String[] argv)
throws ZygoteInit.MethodAndArgsCaller {
Class<?> cl;
try {
cl = Class.forName(className);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}
Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}
int modifiers = m.getModifiers();
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
}
/*
* This throw gets caught in ZygoteInit.main(), which responds
* by invoking the exception's run() method. This arrangement
* clears up all the stack frames that were required in setting
* up the process.
*/
throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}
最終是通過反射調用到ActivityThread.java的main函數中。
5. 進程相關
5.1 進程分類
1、foreground process
殺死foreground需要用戶響應,因爲這個安全優先級是最高的
是用戶操作所必須的,任一時間下,僅有少數進程會處於前臺,僅當內存實在無法供給它們維持同時運行時纔會被殺死。
2、visible process
activity不在前端顯示,但也沒有完全隱藏,能夠看得見,比如彈出一個對話框
可視進程依然被視爲是很重要的,非到不殺死它們便無法維持前臺進程運行時,纔會被殺死。
3、Service process
正在運行的,不在上述兩種狀態的service
是由 startService() 方法啓動的服務,它不會變成上述兩類。儘管服務進程不會直接爲用戶所見,但它們一般都在做着用戶所關心的事情(比如在後臺播放mp3或者從網上下載東 西)。所以系統會盡量維持它們的運行,除非系統內存不足以維持前臺進程和可視進程的運行需要。
4、background process
不可見狀態的activity進程,onstop被調用
包含目前不爲用戶所見的activity(Activity對象的 onStop() 方法已被調用)。這些進程與用戶體驗沒有直接的聯繫,可以在任意時間被殺死以回收內存供前臺進程、可視進程以及服務進程使用。一般來說,會有很多背景進程 運行,所以它們一般存放於一個LRU(最後使用)列表中以確保最後被用戶使用的activity最後被殺死。
5、empty process
沒有運行任何component的進程,保留這個進程主要是爲了緩存的需要
5.2 進程管理
關於Android系統的內存回收機制,相信大家都不陌生,Android基於各個應用進程承載四大組件的狀態對應用進程進行重要性評估,並在系統內存緊張時根據重要性由低到高來選擇殺死應用進程,以達到釋放內存的目的。重要性評估由AMS執行,具體來說就是由updateOomAdjLocked函數決定,這個函數作用就是更新應用進程的重要性。
應用進程(ProcessRecord)的重要性由三個狀態值表示:
adj:LMK殺進程的評分依據
procState:指示進程狀態
schedGroup:指示進程調度策略
framworks/base/services/java/com/android/server/am/ActivityManagerService.java
final void updateOomAdjLocked() {
//此處省略部分代碼
for (int i=N-1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null) {
app.procStateChanged = false;
final boolean wasKeeping = app.keeping;
//關注點1
computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
//此處省略部分代碼
//關注點2
applyOomAdjLocked(app, wasKeeping, TOP_APP, true, false, now);
// Count the number of process types.
switch (app.curProcState) {
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
mNumCachedHiddenProcs++;
numCached++;
if (numCached > cachedProcessLimit) {
//關注點3
killUnneededProcessLocked(app, "cached #" + numCached);
}
break;
case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
if (numEmpty > ProcessList.TRIM_EMPTY_APPS
&& app.lastActivityTime < oldTime) {
killUnneededProcessLocked(app, "empty for "
+ ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime)
/ 1000) + "s");
} else {
numEmpty++;
if (numEmpty > emptyProcessLimit) {
killUnneededProcessLocked(app, "empty #" + numEmpty);
}
}
break;
default:
mNumNonCachedProcs++;
break;
}
//此處省略部分代碼
}
1、調用computeOomAdjLocked來計算進程的oom_adj的值;
2、調用applyOomAdjLocked來更新進程的oom_adj的值;
3、調用killUnneededProcessLocked來kill掉不適用的進程。
updateOomAdjLocked在後面還做了一些對內存的處理優化操作,這裏就不介紹了;這裏有一個疑問,底層和上層都有kill進程的過程,其中有什麼區別呢,我的想法是這樣的,底層的kill主要發生在進程出現Low Memory的時候纔去做,而上層在AMS中是系統在檢測到內存不足時清理緩存的一個重要過程。
下面再看上面關注點2調用的函數:
private final boolean applyOomAdjLocked(ProcessRecord app, boolean wasKeeping,
ProcessRecord TOP_APP, boolean doingAll, boolean reportingProcessState, long now) {
//省略
if (app.curAdj != app.setAdj) {
if (Process.setOomAdj(app.pid, app.curAdj)) {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(
TAG, "Set " + app.pid + " " + app.processName +
" adj " + app.curAdj + ": " + app.adjType);
app.setAdj = app.curAdj;
} else {
success = false;
Slog.w(TAG, "Failed setting oom adj of " + app + " to " + app.curAdj);
}
}
//省略
return success;
}
curAdj是computeOomAdjLocked計算出的adj值,通過調用Process.setOomAdj(int pid, int amt)往下進行設置。
updateOomAdjLocked在後面還做了一些對內存的處理優化操作,這裏就不介紹了;這裏有一個疑問,底層和上層都有kill進程的過程,其中有什麼區別呢,我的想法是這樣的,底層的kill主要發生在進程出現Low Memory的時候纔去做,而上層在AMS中是系統在檢測到內存不足時清理緩存的一個重要過程。
public static final native boolean setOomAdj(int pid, int amt);
5.3 底層實現
framworks/base/core/jni/android_util_Process.cpp
jboolean android_os_Process_setOomAdj(JNIEnv* env, jobject clazz,
jint pid, jint adj)
{
#ifdef HAVE_OOM_ADJ
char text[64];
sprintf(text, "/proc/%d/oom_adj", pid);
int fd = open(text, O_WRONLY);
if (fd >= 0) {
sprintf(text, "%d", adj);
write(fd, text, strlen(text));
close(fd);
}
return true;
#endif
return false;
}
可以看到這段函數打開了對應的位置的oom_adj文件,並將adj的值寫到這個文件中,也即是達到了更新對應進程oom_adj值的效果。
5.4 LMK原理
內核LMK的原理很簡單:首先註冊了shrinker,在內存緊張的時候會觸發lowmem_shrink(linux 高版本是lowmem_scan)方法,這個方法要做的就是找到一個進程,然後殺掉他,釋放一些內存。
獲取剩餘內存的大小,和Minfree內存閥值做比較,找到對應的內存閥值,找到對應的adj值。
遍歷所有的進程,大於該adj的值的進程是要殺掉的目標進程, 但是並不是全部殺掉,而是找到adj最大的進程殺掉,如果最大adj有多個相同adj進程,則殺掉佔用內存最大的一個。
5.5 後記
andorid 5.0及其後面版本跟上面會有一定差異,最大的區別是與驅動通信的方式的改變。
整個流程圖如下(網圖,自己懶得畫了):