日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android后台杀死系列之三:LowMemoryKiller原理(4.3-6.0)

發(fā)布時(shí)間:2025/3/15 Android 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android后台杀死系列之三:LowMemoryKiller原理(4.3-6.0) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本篇是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應(yīng)用進(jìn)程優(yōu)先級(jí)及oomAdj

    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(較老的、使用可能性更小)
    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,存在交互界面,Foreground 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)程不考慮)。

    Android應(yīng)用的優(yōu)先級(jí)是如何更新的

    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_ADJchar 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; #endifreturn 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í)現(xiàn):LMKD服務(wù)

    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/lmkdclass corecriticalsocket 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)1if (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, &param);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內(nèi)核部分:如何殺死

    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)程,殺死。

    總結(jié)

    通過本篇文章,希望大家能有以下幾點(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)程和線程


    原文地址: 

    總結(jié)

    以上是生活随笔為你收集整理的Android后台杀死系列之三:LowMemoryKiller原理(4.3-6.0)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。