本篇是Android后臺(tái)殺死系列的第三篇,前面兩篇已經(jīng)對(duì)后臺(tái)殺死注意事項(xiàng),殺死恢復(fù)機(jī)制做了分析,本篇主要講解的是Android后臺(tái)殺死原理。相對(duì)于后臺(tái)殺死恢復(fù),LowMemoryKiller原理相對(duì)簡(jiǎn)單,并且在網(wǎng)上還是能找到不少資料的,不過,由于Android不同版本在框架層的實(shí)現(xiàn)有一些不同,網(wǎng)上的分析也多是針對(duì)一個(gè)Android版本,本文簡(jiǎn)單做了以下區(qū)分對(duì)比。LowMemoryKiller(低內(nèi)存殺手)是Andorid基于oomKiller原理所擴(kuò)展的一個(gè)多層次oomKiller,OOMkiller(Out Of Memory Killer)是在Linux系統(tǒng)無法分配新內(nèi)存的時(shí)候,選擇性殺掉進(jìn)程,到oom的時(shí)候,系統(tǒng)可能已經(jīng)不太穩(wěn)定,而LowMemoryKiller是一種根據(jù)內(nèi)存閾值級(jí)別觸發(fā)的內(nèi)存回收的機(jī)制,在系統(tǒng)可用內(nèi)存較低時(shí),就會(huì)選擇性殺死進(jìn)程的策略,相對(duì)OOMKiller,更加靈活。在詳細(xì)分析其原理與運(yùn)行機(jī)制之前,不妨自己想一下,假設(shè)讓你設(shè)計(jì)一個(gè)LowMemoryKiller,你會(huì)如何做,這樣一個(gè)系統(tǒng)需要什么功能模塊呢?
進(jìn)程優(yōu)先級(jí)定義:只有有了優(yōu)先級(jí),才能決定先殺誰,后殺誰
進(jìn)程優(yōu)先級(jí)的動(dòng)態(tài)管理:一個(gè)進(jìn)程的優(yōu)先級(jí)不應(yīng)該是固定不變的,需要根據(jù)其變動(dòng)而動(dòng)態(tài)變化,比如前臺(tái)進(jìn)程切換到后臺(tái)優(yōu)先級(jí)肯定要降低
進(jìn)程殺死的時(shí)機(jī),什么時(shí)候需要挑一個(gè),或者挑多個(gè)進(jìn)程殺死
如何殺死
以上幾個(gè)問題便是一個(gè)MemoryKiller模塊需要的基本功能,Android底層采用的是Linux內(nèi)核,其進(jìn)程管理都是基于Linux內(nèi)核,LowMemoryKiller也相應(yīng)的放在內(nèi)核模塊,這也意味著用戶空間對(duì)于后臺(tái)殺死不可見,就像AMS完全不知道一個(gè)APP是否被后臺(tái)殺死,只有在AMS喚醒APP的時(shí)候,才知道APP是否被LowMemoryKiller殺死過。其實(shí)LowmemoryKiller的原理是很清晰的,先看一下整體流程圖,再逐步分析:
先記住兩點(diǎn) :
LowMemoryKiller是被動(dòng)殺死進(jìn)程
Android應(yīng)用通過AMS,利用proc文件系統(tǒng)更新進(jìn)程信息
Android會(huì)盡可能長(zhǎng)時(shí)間地保持應(yīng)用存活,但為了新建或運(yùn)行更重要的進(jìn)程,可能需要移除舊進(jìn)程來回收內(nèi)存,在選擇要Kill的進(jìn)程的時(shí)候,系統(tǒng)會(huì)根據(jù)進(jìn)程的運(yùn)行狀態(tài)作出評(píng)估,權(quán)衡進(jìn)程的“重要性“,其權(quán)衡的依據(jù)主要是四大組件。如果需要縮減內(nèi)存,系統(tǒng)會(huì)首先消除重要性最低的進(jìn)程,然后是重要性略遜的進(jìn)程,依此類推,以回收系統(tǒng)資源。在Android中,應(yīng)用進(jìn)程劃分5級(jí)(摘自Google文檔):Android中APP的重要性層次一共5級(jí):
前臺(tái)進(jìn)程(Foreground process)
可見進(jìn)程(Visible process)
服務(wù)進(jìn)程(Service process)
后臺(tái)進(jìn)程(Background process)
空進(jìn)程(Empty process)
前臺(tái)進(jìn)程
用戶當(dāng)前操作所必需的進(jìn)程。如果一個(gè)進(jìn)程滿足以下任一條件,即視為前臺(tái)進(jìn)程:
包含正在交互的Activity(resumed
包含綁定到正在交互的Activity的Service
包含正在“前臺(tái)”運(yùn)行的Service(服務(wù)已調(diào)用startForeground())
包含正執(zhí)行一個(gè)生命周期回調(diào)的Service(onCreate()、onStart() 或 onDestroy())
包含一個(gè)正執(zhí)行其onReceive()方法的BroadcastReceiver
通常,在任意給定時(shí)間前臺(tái)進(jìn)程都為數(shù)不多。只有在內(nèi)存不足以支持它們同時(shí)繼續(xù)運(yùn)行這一萬不得已的情況下,系統(tǒng)才會(huì)終止它們。 此時(shí),設(shè)備往往已達(dá)到內(nèi)存分頁狀態(tài),因此需要終止一些前臺(tái)進(jìn)程來確保用戶界面正常響應(yīng)。
可見進(jìn)程
沒有任何前臺(tái)組件、但仍會(huì)影響用戶在屏幕上所見內(nèi)容的進(jìn)程。 如果一個(gè)進(jìn)程滿足以下任一條件,即視為可見進(jìn)程:
包含不在前臺(tái)、但仍對(duì)用戶可見的 Activity(已調(diào)用其 onPause() 方法)。例如,如果前臺(tái) Activity 啟動(dòng)了一個(gè)對(duì)話框,允許在其后顯示上一Activity,則有可能會(huì)發(fā)生這種情況。
包含綁定到可見(或前臺(tái))Activity 的 Service。
可見進(jìn)程被視為是極其重要的進(jìn)程,除非為了維持所有前臺(tái)進(jìn)程同時(shí)運(yùn)行而必須終止,否則系統(tǒng)不會(huì)終止這些進(jìn)程。
服務(wù)進(jìn)程
正在運(yùn)行已使用 startService() 方法啟動(dòng)的服務(wù)且不屬于上述兩個(gè)更高類別進(jìn)程的進(jìn)程。盡管服務(wù)進(jìn)程與用戶所見內(nèi)容沒有直接關(guān)聯(lián),但是它們通常在執(zhí)行一些用戶關(guān)心的操作(例如,在后臺(tái)播放音樂或從網(wǎng)絡(luò)下載數(shù)據(jù))。因此,除非內(nèi)存不足以維持所有前臺(tái)進(jìn)程和可見進(jìn)程同時(shí)運(yùn)行,否則系統(tǒng)會(huì)讓服務(wù)進(jìn)程保持運(yùn)行狀態(tài)。
后臺(tái)進(jìn)程
包含目前對(duì)用戶不可見的 Activity 的進(jìn)程(已調(diào)用 Activity 的 onStop() 方法)。這些進(jìn)程對(duì)用戶體驗(yàn)沒有直接影響,系統(tǒng)可能隨時(shí)終止它們,以回收內(nèi)存供前臺(tái)進(jìn)程、可見進(jìn)程或服務(wù)進(jìn)程使用。 通常會(huì)有很多后臺(tái)進(jìn)程在運(yùn)行,因此它們會(huì)保存在 LRU (最近最少使用)列表中,以確保包含用戶最近查看的 Activity 的進(jìn)程最后一個(gè)被終止。如果某個(gè) Activity 正確實(shí)現(xiàn)了生命周期方法,并保存了其當(dāng)前狀態(tài),則終止其進(jìn)程不會(huì)對(duì)用戶體驗(yàn)產(chǎn)生明顯影響,因?yàn)楫?dāng)用戶導(dǎo)航回該 Activity 時(shí),Activity會(huì)恢復(fù)其所有可見狀態(tài)。 有關(guān)保存和恢復(fù)狀態(tài)、或者異常殺死恢復(fù)可以參考前兩篇 文章。
空進(jìn)程
不含任何活動(dòng)應(yīng)用組件的進(jìn)程。保留這種進(jìn)程的的唯一目的是用作緩存,以縮短下次在其中運(yùn)行組件所需的啟動(dòng)時(shí)間,這就是所謂熱啟動(dòng) 。為了使系統(tǒng)資源在進(jìn)程緩存和底層內(nèi)核緩存之間保持平衡,系統(tǒng)往往會(huì)終止這些進(jìn)程。
根據(jù)進(jìn)程中當(dāng)前活動(dòng)組件的重要程度,Android會(huì)將進(jìn)程評(píng)定為它可能達(dá)到的最高級(jí)別。例如,如果某進(jìn)程托管著服務(wù)和可見 Activity,則會(huì)將此進(jìn)程評(píng)定為可見進(jìn)程,而不是服務(wù)進(jìn)程。此外,一個(gè)進(jìn)程的級(jí)別可能會(huì)因其他進(jìn)程對(duì)它的依賴而有所提高,即服務(wù)于另一進(jìn)程的進(jìn)程其級(jí)別永遠(yuǎn)不會(huì)低于其所服務(wù)的進(jìn)程。 例如,如果進(jìn)程 A 中的內(nèi)容提供程序?yàn)檫M(jìn)程 B 中的客戶端提供服務(wù),或者如果進(jìn)程 A 中的服務(wù)綁定到進(jìn)程 B 中的組件,則進(jìn)程 A 始終被視為至少與進(jìn)程B同樣重要。
通過Google文檔,對(duì)不同進(jìn)程的重要程度有了一個(gè)直觀的認(rèn)識(shí),下面看一下量化到內(nèi)存是什么樣的呈現(xiàn)形式,這里針對(duì)不同的重要程度,做了進(jìn)一步的細(xì)分,定義了重要級(jí)別ADJ,并將優(yōu)先級(jí)存儲(chǔ)到內(nèi)核空間的進(jìn)程結(jié)構(gòu)體中去,供LowmemoryKiller參考:
ADJ優(yōu)先級(jí) | 優(yōu)先級(jí) | 對(duì)應(yīng)場(chǎng)景 |
---|---|---|
UNKNOWN_ADJ | 16 | 一般指將要會(huì)緩存進(jìn)程,無法獲取確定值 |
CACHED_APP_MAX_ADJ | 15 | 不可見進(jìn)程的adj最大值(不可見進(jìn)程可能在任何時(shí)候被殺死) |
CACHED_APP_MIN_ADJ | 9 | 不可見進(jìn)程的adj最小值(不可見進(jìn)程可能在任何時(shí)候被殺死) |
SERVICE_B_AD | 8 | B List中的Service(較老的、使用可能性更?。?/td> |
PREVIOUS_APP_ADJ | 7 | 上一個(gè)App的進(jìn)程(比如APP_A跳轉(zhuǎn)APP_B,APP_A不可見的時(shí)候,A就是屬于PREVIOUS_APP_ADJ) |
HOME_APP_ADJ | 6 | Home進(jìn)程 |
SERVICE_ADJ | 5 | 服務(wù)進(jìn)程(Service process) |
HEAVY_WEIGHT_APP_ADJ | 4 | 后臺(tái)的重量級(jí)進(jìn)程,system/rootdir/init.rc文件中設(shè)置 |
BACKUP_APP_ADJ | 3 | 備份進(jìn)程(這個(gè)不太了解) |
PERCEPTIBLE_APP_ADJ | 2 | 可感知進(jìn)程,比如后臺(tái)音樂播放 |
VISIBLE_APP_ADJ | 1 | 可見進(jìn)程(可見,但是沒能獲取焦點(diǎn),比如新進(jìn)程僅有一個(gè)懸浮Activity,Visible process) |
FOREGROUND_APP_ADJ | 0 | 前臺(tái)進(jìn)程(正在展示是APP,存在交互界面,F(xiàn)oreground process) |
PERSISTENT_SERVICE_ADJ | -11 | 關(guān)聯(lián)著系統(tǒng)或persistent進(jìn)程 |
PERSISTENT_PROC_ADJ | -12 | 系統(tǒng)persistent進(jìn)程,比如telephony |
SYSTEM_ADJ | -16 | 系統(tǒng)進(jìn)程 |
NATIVE_ADJ | -17 | native進(jìn)程(不被系統(tǒng)管理) |
以上介紹的目的只有一點(diǎn):Android的應(yīng)用進(jìn)程是有優(yōu)先級(jí)的,它的優(yōu)先級(jí)跟當(dāng)前是否存在展示界面,以及是否能被用戶感知有關(guān),越是被用戶感知的的應(yīng)用優(yōu)先級(jí)越高(系統(tǒng)進(jìn)程不考慮)。
APP中很多操作都可能會(huì)影響進(jìn)程列表的優(yōu)先級(jí),比如退到后臺(tái)、移到前臺(tái)等,都會(huì)潛在的影響進(jìn)程的優(yōu)先級(jí),我們知道Lowmemorykiller是通過遍歷內(nèi)核的進(jìn)程結(jié)構(gòu)體隊(duì)列,選擇優(yōu)先級(jí)低的殺死,那么APP操作是如何寫入到內(nèi)核空間的呢?Linxu有用戶間跟內(nèi)核空間的區(qū)分,無論是APP還是系統(tǒng)服務(wù),都是運(yùn)行在用戶空間,嚴(yán)格說用戶控件的操作是無法直接影響內(nèi)核空間的,更不用說更改進(jìn)程的優(yōu)先級(jí)。其實(shí)這里是通過了Linux中的一個(gè)proc文件體統(tǒng),proc文件系統(tǒng)可以簡(jiǎn)單的看多是內(nèi)核空間映射成用戶可以操作的文件系統(tǒng),當(dāng)然不是所有進(jìn)程都有權(quán)利操作,通過proc文件系統(tǒng),用戶空間的進(jìn)程就能夠修改內(nèi)核空間的數(shù)據(jù),比如修改進(jìn)程的優(yōu)先級(jí),在Android家族,5.0之前的系統(tǒng)是AMS進(jìn)程直接修改的,5.0之后,是修改優(yōu)先級(jí)的操作被封裝成了一個(gè)獨(dú)立的服務(wù)-lmkd,lmkd服務(wù)位于用戶空間,其作用層次同AMS、WMS類似,就是一個(gè)普通的系統(tǒng)服務(wù)。我們先看一下5.0之前的代碼,這里仍然用4.3的源碼看一下,模擬一個(gè)場(chǎng)景,APP只有一個(gè)Activity,我們主動(dòng)finish掉這個(gè)Activity,APP就回到了后臺(tái),這里要記住,雖然沒有可用的Activity,但是APP本身是沒喲死掉的,這就是所謂的熱啟動(dòng),先看下大體的流程:
現(xiàn)在直接去AMS看源碼:
ActivityManagerService
public final boolean finishActivity(IBinder token, int resultCode, Intent resultData) { ... synchronized(this) { final long origId = Binder.clearCallingIdentity(); boolean res = mMainStack.requestFinishActivityLocked(token, resultCode, resultData, "app-request", true); ... }}
一開始的流程跟startActivity類似,首先是先暫停當(dāng)前resume的Activity,其實(shí)也就是自己,
final boolean finishActivityLocked(ActivityRecord r, int index, int resultCode, Intent resultData, String reason, boolean immediate, boolean oomAdj) { ... if (mPausingActivity == null) { if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r); if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false"); startPausingLocked(false, false); } ... }
pause掉當(dāng)前Activity之后,還需要喚醒上一個(gè)Activity,如果當(dāng)前APP的Activity棧里應(yīng)經(jīng)空了,就回退到上一個(gè)應(yīng)用或者桌面程序,喚醒流程就不在講解了,因?yàn)樵贏MS恢復(fù)異常殺死APP的那篇已經(jīng)說過,這里要說的是喚醒之后對(duì)這個(gè)即將退回后臺(tái)的APP的操作,這里注意與startActivity不同的地方,看下面代碼:
ActivityStack
private final void completePauseLocked() { ActivityRecord prev = mPausingActivity; if (prev != null) { if (prev.finishing) { 1、 不同點(diǎn) <!--主動(dòng)finish的時(shí)候,走的是這個(gè)分支,狀態(tài)變換的細(xì)節(jié)請(qǐng)自己查詢代碼--> prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false); } ... 2、相同點(diǎn) if (!mService.isSleeping()) { resumeTopActivityLocked(prev); }
看一下上面的兩個(gè)關(guān)鍵點(diǎn)1跟2,1是同startActivity的completePauseLocked不同的地方,主動(dòng)finish的prev.finishing是為true的,因此會(huì)執(zhí)行finishCurrentActivityLocked分支,將當(dāng)前pause的Activity加到mStoppingActivities隊(duì)列中去,并且喚醒下一個(gè)需要走到到前臺(tái)的Activity,喚醒后,會(huì)繼續(xù)執(zhí)行stop:
private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int index, int mode, boolean oomAdj) { if (mode == FINISH_AFTER_VISIBLE && r.nowVisible) { if (!mStoppingActivities.contains(r)) { mStoppingActivities.add(r); ... } .... return r; } ... }
讓我們?cè)倩氐絩esumeTopActivityLocked繼續(xù)看,resume之后會(huì)回調(diào)completeResumeLocked函數(shù),繼續(xù)執(zhí)行stop,這個(gè)函數(shù)通過向Handler發(fā)送IDLE_TIMEOUT_MSG消息來回調(diào)activityIdleInternal函數(shù),最終執(zhí)行destroyActivityLocked銷毀ActivityRecord,
final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) { ... if (next.app != null && next.app.thread != null) { ... try { 。。。 next.app.thread.scheduleResumeActivity(next.appToken, mService.isNextTransitionForward()); ..。 try { next.visible = true; completeResumeLocked(next); } .... }
在銷毀Activity的時(shí)候,如果當(dāng)前APP的Activity堆棧為空了,就說明當(dāng)前Activity沒有可見界面了,這個(gè)時(shí)候就需要?jiǎng)討B(tài)更新這個(gè)APP的優(yōu)先級(jí),詳細(xì)代碼如下:
final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, boolean oomAdj, String reason) { ... if (hadApp) { if (removeFromApp) { // 這里動(dòng)ProcessRecord里面刪除,但是沒從history刪除 int idx = r.app.activities.indexOf(r); if (idx >= 0) { r.app.activities.remove(idx); } ... if (r.app.activities.size() == 0) { // No longer have activities, so update oom adj. mService.updateOomAdjLocked(); ... }
最終會(huì)調(diào)用AMS的updateOomAdjLocked函數(shù)去更新進(jìn)程優(yōu)先級(jí),在4.3的源碼里面,主要是通過Process類的setOomAdj函數(shù)來設(shè)置優(yōu)先級(jí):
ActivityManagerService
private final boolean updateOomAdjLocked(ProcessRecord app, int hiddenAdj, int clientHiddenAdj, int emptyAdj, ProcessRecord TOP_APP, boolean doingAll) { ... 計(jì)算優(yōu)先級(jí) computeOomAdjLocked(app, hiddenAdj, clientHiddenAdj, emptyAdj, TOP_APP, false, doingAll); 。。。 <!--如果不相同,設(shè)置新的OomAdj--> if (app.curAdj != app.setAdj) { if (Process.setOomAdj(app.pid, app.curAdj)) { ...}
Process中setOomAdj是一個(gè)native方法,原型在android_util_Process.cpp中
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;}
可以看到,在native代碼里,就是通過proc文件系統(tǒng)修改內(nèi)核信息,這里就是動(dòng)態(tài)更新進(jìn)程的優(yōu)先級(jí)oomAdj,以上是針對(duì)Android4.3系統(tǒng)的分析,之后會(huì)看一下5.0之后的系統(tǒng)是如何實(shí)現(xiàn)的。下面是4.3更新oomAdj的流程圖,注意紅色的執(zhí)行點(diǎn):
Android5.0將設(shè)置進(jìn)程優(yōu)先級(jí)的入口封裝成了一個(gè)獨(dú)立的服務(wù)lmkd服務(wù),AMS不再直接訪問proc文件系統(tǒng),而是通過lmkd服務(wù)來進(jìn)行設(shè)置,從init.rc文件中看到服務(wù)的配置。
service lmkd /system/bin/lmkd class core critical socket lmkd seqpacket 0660 system system
從配置中可以看出,該服務(wù)是通過socket與其他進(jìn)行進(jìn)程進(jìn)行通信,其實(shí)就是AMS通過socket向lmkd服務(wù)發(fā)送請(qǐng)求,讓lmkd去更新進(jìn)程的優(yōu)先級(jí),lmkd收到請(qǐng)求后,會(huì)通過/proc文件系統(tǒng)去更新內(nèi)核中的進(jìn)程優(yōu)先級(jí)。首先看一下5.0中這一塊AMS有什么改變,其實(shí)大部分流程跟之前4.3源碼類似,我們只看一下不同地方
ActivityManagerService
private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP, boolean doingAll, long now) { ... computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now); ... applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());}private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now, long nowElapsed) { boolean success = true; if (app.curRawAdj != app.setRawAdj) { app.setRawAdj = app.curRawAdj; } int changes = 0; 不同點(diǎn)1 if (app.curAdj != app.setAdj) { ProcessList.setOomAdj(app.pid, app.info.uid, app.curAdj); if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ, "Set " + app.pid + " " + app.processName + " adj " + app.curAdj + ": " + app.adjType); app.setAdj = app.curAdj; app.verifiedAdj = ProcessList.INVALID_ADJ; }
從上面的不同點(diǎn)1可以看出,5.0之后是通過ProcessList類去設(shè)置oomAdj,其實(shí)這里就是通過socket與LMKD服務(wù)進(jìn)行通信,向lmkd服務(wù)傳遞給LMK_PROCPRIO命令去更新進(jìn)程優(yōu)先級(jí):
public static final void setOomAdj(int pid, int uid, int amt) { if (amt == UNKNOWN_ADJ) return; long start = SystemClock.elapsedRealtime(); ByteBuffer buf = ByteBuffer.allocate(4 * 4); buf.putInt(LMK_PROCPRIO); buf.putInt(pid); buf.putInt(uid); buf.putInt(amt); writeLmkd(buf); long now = SystemClock.elapsedRealtime(); } private static void writeLmkd(ByteBuffer buf) { for (int i = 0; i < 3; i++) { if (sLmkdSocket == null) { if (openLmkdSocket() == false) { ... try { sLmkdOutputStream.write(buf.array(), 0, buf.position()); return; ... }
其實(shí)就是openLmkdSocket打開本地socket端口,并將優(yōu)先級(jí)信息發(fā)送過去,那么lmkd服務(wù)端如何處理的呢,init.rc里配置的服務(wù)是在開機(jī)時(shí)啟動(dòng)的,來看看lmkd服務(wù)的入口:main函數(shù)
lmkd.c函數(shù)
int main(int argc __unused, char **argv __unused) { struct sched_param param = { .sched_priority = 1, }; mlockall(MCL_FUTURE); sched_setscheduler(0, SCHED_FIFO, ¶m); if (!init()) mainloop(); ALOGI("exiting"); return 0;}
很簡(jiǎn)單,打開一個(gè)端口,并通過mainloop監(jiān)聽socket,如果有請(qǐng)求到來,就解析命令并執(zhí)行,剛才傳入的LMK_PROCPRIO命令對(duì)應(yīng)的操作就是cmd_procprio,用來更新oomAdj,其更新新機(jī)制還是通過proc文件系統(tǒng),不信?看下面代碼:
static void cmd_procprio(int pid, int uid, int oomadj) { struct proc *procp; 。。。 還是利用/proc文件系統(tǒng)進(jìn)行更新 snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid); snprintf(val, sizeof(val), "%d", lowmem_oom_adj_to_oom_score_adj(oomadj)); writefilestring(path, val); 。。。}
簡(jiǎn)單的流程圖如下,同4.3不同的地方
以上就分析完了用戶空間的操作如何影響到進(jìn)程的優(yōu)先級(jí),并且將新的優(yōu)先級(jí)寫到內(nèi)核中。最后看一下LomemoryKiller在什么時(shí)候、如何根據(jù)優(yōu)先級(jí)殺死進(jìn)程的:
LomemoryKiller屬于一個(gè)內(nèi)核驅(qū)動(dòng)模塊,主要功能是:在系統(tǒng)內(nèi)存不足的時(shí)候掃描進(jìn)程隊(duì)列,找到低優(yōu)先級(jí)(也許說性價(jià)比低更合適)的進(jìn)程并殺死,以達(dá)到釋放內(nèi)存的目的。對(duì)于驅(qū)動(dòng)程序,入口是__init函數(shù),先看一下這個(gè)驅(qū)動(dòng)模塊的入口:
static int __init lowmem_init(void){ register_shrinker(&lowmem_shrinker); return 0;}
可以看到在init的時(shí)候,LomemoryKiller將自己的lowmem_shrinker入口注冊(cè)到系統(tǒng)的內(nèi)存檢測(cè)模塊去,作用就是在內(nèi)存不足的時(shí)候可以被回調(diào),register_shrinker函數(shù)是一屬于另一個(gè)內(nèi)存管理模塊的函數(shù),如果一定要根下去的話,可以看一下它的定義,其實(shí)就是加到一個(gè)回調(diào)函數(shù)隊(duì)列中去:
void register_shrinker(struct shrinker *shrinker){ shrinker->nr = 0; down_write(&shrinker_rwsem); list_add_tail(&shrinker->list, &shrinker_list); up_write(&shrinker_rwsem);}
最后,看一下,當(dāng)內(nèi)存不足觸發(fā)回調(diào)的時(shí)候,LomemoryKiller是如何找到低優(yōu)先級(jí)進(jìn)程,并殺死的:入口函數(shù)就是init時(shí)候注冊(cè)的lowmem_shrink函數(shù)(4.3源碼,后面的都有微調(diào)但原理大概類似):
static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask){ struct task_struct *p; 。。。 關(guān)鍵點(diǎn)1 找到當(dāng)前的內(nèi)存對(duì)應(yīng)的閾值 for(i = 0; i < array_size; i++) { if (other_free < lowmem_minfree[i] && other_file < lowmem_minfree[i]) { min_adj = lowmem_adj[i]; break; } } 。。。 關(guān)鍵點(diǎn)2 找到優(yōu)先級(jí)低于這個(gè)閾值的進(jìn)程,并殺死 read_lock(&tasklist_lock); for_each_process(p) { if (p->oomkilladj < min_adj || !p->mm) continue; tasksize = get_mm_rss(p->mm); if (tasksize <= 0) continue; if (selected) { if (p->oomkilladj < selected->oomkilladj) continue; if (p->oomkilladj == selected->oomkilladj && tasksize <= selected_tasksize) continue; } selected = p; selected_tasksize = tasksize; } if(selected != NULL) { force_sig(SIGKILL, selected); rem -= selected_tasksize; } lowmem_print(4, "lowmem_shrink %d, %x, return %d\n", nr_to_scan, gfp_mask, rem); read_unlock(&tasklist_lock); return rem;}
先看關(guān)鍵點(diǎn)1:其實(shí)就是確定當(dāng)前低內(nèi)存對(duì)應(yīng)的閾值;關(guān)鍵點(diǎn)2 :找到比該閾值優(yōu)先級(jí)低,切task多的進(jìn)程,將其殺死。如何殺死的呢?很直接,通過Linux的中的信號(hào)量,發(fā)送SIGKILL信號(hào)直接將進(jìn)程殺死。到這就分析完了LomemoryKiller內(nèi)核部分如何工作的。其實(shí)很簡(jiǎn)單,一句話:被動(dòng)掃描,找到低優(yōu)先級(jí)的進(jìn)程,殺死。
通過本篇文章,希望大家能有以下幾點(diǎn)認(rèn)知:
Android APP進(jìn)程是有優(yōu)先級(jí)的的,與進(jìn)程是否被用戶感知有直接關(guān)系
APP切換等活動(dòng)都可能造成進(jìn)程優(yōu)先級(jí)的變化,都是利用AMS,并通過proc文件設(shè)置到內(nèi)核的
LowmemoryKiller運(yùn)行在內(nèi)核,在內(nèi)存需要縮減的時(shí)候,會(huì)選擇低優(yōu)先級(jí)的進(jìn)程殺死
至于更加細(xì)節(jié)的內(nèi)存的縮減、優(yōu)先級(jí)的計(jì)算也許將來會(huì)放到單獨(dú)的文章中說明,本文的目的是:能讓大家對(duì)LowmemoryKiller的概念以及運(yùn)行機(jī)制有個(gè)簡(jiǎn)單了解。
Android應(yīng)用程序啟動(dòng)過程源代碼分析
Android Framework架構(gòu)淺析之【近期任務(wù)】
Android Low Memory Killer介紹
Android開發(fā)之InstanceState詳解
對(duì)Android近期任務(wù)列表(Recent Applications)的簡(jiǎn)單分析
Android 操作系統(tǒng)的內(nèi)存回收機(jī)制
Android LowMemoryKiller原理分析 精
Android進(jìn)程生命周期與ADJ
Linux下/proc目錄簡(jiǎn)介
Android系統(tǒng)中的進(jìn)程管理:進(jìn)程的創(chuàng)建 精
Google文檔--進(jìn)程和線程
聯(lián)系客服