九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
Android后臺(tái)殺死系列之三:LowMemoryKiller原理(4.3

本篇是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) :

  1. LowMemoryKiller是被動(dòng)殺死進(jìn)程

  2. 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(較老的、使用可能性更?。?/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)程不考慮)。

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_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í)現(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/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, &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)程和線程

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
【轉(zhuǎn)】怎樣使一個(gè)Android應(yīng)用不被殺死?(整理)
Android的進(jìn)程優(yōu)先級(jí)與進(jìn)程回收
慎用內(nèi)存釋放軟件,剖析1Gram手機(jī)的內(nèi)存原理
一種提高Android應(yīng)用進(jìn)程存活率新方法 | SkySeraph
關(guān)于 Android 進(jìn)程保活,你所需要知道的一切
Android下寫一個(gè)永遠(yuǎn)不會(huì)被KILL掉的進(jìn)程/服務(wù)
更多類似文章 >>
生活服務(wù)
熱點(diǎn)新聞
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服