Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 6.0的lowmemorykiller機制

Android 6.0的lowmemorykiller機制

編輯:關於Android編程

最近在處理一些lowmemorykiller相關的問題,於是對lowmemorykiller機制作了一個簡單的了解。在這裡總結一下。

首先,是lowmemorykiller的一些背景知識。

眾所周知,Andorid的實質是一個linux的操作系統。所以和其他操作系統一樣,每個程序,每個進程運行,都需要一定內存空間進行支撐。而進程的內存空間只是虛擬內存,程序運行需要的是實實在在的內存(物理內存,即RAM)。所以在必要的時候,操作系統會將程序運行中申請的內存映射到RAM中。RAM作為進程運作不可缺的資源,對系統穩定性有著決定性影響。所以我們必須對內存相關有一個簡單直觀的認知。進程空間1和RAM之間的關系大致如圖:

\

 

內存相關的介紹,在這裡我只是做一個簡單介紹。假如想深入了解請自行了解操作系統相關知識。

簡單對內存有一個了解之後,我們來簡單介紹一下OOM。

OOM全稱Out Of Memory,是Linux當中,內存保護機制的一種。該機制會監控那些占用內存過大,尤其是瞬間很快消耗大量內存的進程,為了防止內存耗盡而內核將該進程殺掉。

當Kernel遇到OOM的時候,可以有2種選擇:

1) 產生kernelpanic(死機)

2) 啟動OOM killer,選擇一個或多個“合適”的進程,干掉那些選擇中的進程,從而釋放內存。

在OOM機制當中,有幾個參數是必須了解的,這幾個參數分別是oom_adj,oom_score_adj,oom_score。每個進程都會有這樣的3個參數,他們位於/proc/XXX/目錄下(XXX為進程的ID)。在Linux中,系統就是通過算分去殺死進程的。至於分數的值,就是這3個參數的值。

簡單來說,系統是這樣進行算分的:算分主要分2部分,一部分是系統打分,主要根據該進程的內存使用情況(oom_score),另一部分是用戶大份額,就是oom_score_adj。每個進程的實際得分是綜合這2個參數的值的。而oom_adj只是一個舊的接口參數,在普通的linux系統中和oom_score_adj是差不多的(但是在android中是很有用的)。

OOM是Android的LowMemoryKiller的基礎。了解完OOM之後,我們終於可以引入LowMemoryKiller了。

在Android中,及時用戶退出當前應用程序後,應用程序還是會存在於系統當中,這是為了方便程序的再次啟動。但是這樣的話,隨著打開的程序的數量的增加,系統的內存就會不足,從而需要殺掉一些進程來釋放內存空間。至於是否需要殺進程以及殺什麼進程,這個就是由Android的內部機制LowMemoryKiller機制來進行的。

Andorid的Low Memory Killer是在標准的linux lernel的OOM基礎上修改而來的一種內存管理機制。當系統內存不足時,殺死不必要的進程釋放其內存。不必要的進程的選擇根據有2個:oom_adj和占用的內存的大小。oom_adj代表進程的優先級,數值越高,優先級月低,越容易被殺死;對應每個oom_adj都可以有一個空閒進程的閥值。Android Kernel每隔一段時間會檢測當前空閒內存是否低於某個閥值。假如是,則殺死oom_adj最大的不必要的進程,如果有多個,就根據oom_score_adj去殺死進程,,直到內存恢復低於閥值的狀態。

LowMemoryKiller的值的設定,主要保存在2個文件之中,分別是/sys/module/lowmemorykiller/parameters/adj與/sys/module/lowmemorykiller/parameters/minfree。adj保存著當前系統殺進程的等級,minfree則是保存著對應的閥值。他們的對應關系如下:

\

舉個例子說明一下上表,當當前系統內存少於55296×4K(即216MB)時,Android就會找出當前oom_adj≥9的進程,根據進程的等級,先把oom_adj數值最大的進程給殺掉,釋放他的內存,當他們的oom_adj相等時,就對比他們的oom_score_adj,然後oom_score_adj越大,也越容易殺掉。

在這裡,也許有人會問,為什麼采用LowMemoryKiller而不用OOM呢?我們來對比一下兩者,就可以得出答案了。

\

使用LowMemoryKiller可以使系統內存較低時,調出進程管理器結束不必要的人進程釋放空間。在安卓中,如果等到真正的OOM時,也許進程管理器就已經沒法啟動了。

上面提到的oom_adj,其實在Android中並不只有6個,在Android 6.0中,一個設置了16個adj,adj的具體設置與描述如下:

\

對LowMemoryKiller相關的知識簡介就到這裡。下面我們就主要介紹一下lowMemorykiller的運行原理。

上面其實已經說過,LowMemoryKiller是對多個內存閥值的控制來選擇殺進程的。但是,這些閥值是怎樣聯系在一起的呢?下面就我的理解,簡單說一下其運行的原理。

首先,LowMemoryKiller是隨著系統的啟動而啟動的。當前主要的LowMemoryKiller的代碼主要在\system\core\lmkd的目錄下,之前的代碼\kernel\drivers\staging\android\lowmemorykiller.c已經不再使用。

LowMemoryKiller在系統啟動的時候就已經由init進程一並啟動了。LowMemoryKiller啟動就是,就會不斷監測系統的運行情況和內存情況,當內存少於minfree限定的閥值的時候,lowMemoryKiller遍歷當前進程的oom_score_adj,把大於對應閥值的進程進行kill操作。例如,在當前設置中,當系統內存少於315M時,系統就會自動把進程中oom_score_adj的值少於1000的殺掉,當系統內存少於216時,系統就會自動把進程中oom_score_adj的值少於529的殺掉,如此類推。

至於oom_adj和oom_score_adj是由誰去控制並寫入的呢?

在系統當中,oom_adj和oom_score_adj是由ActivityManagerService去控制的,上層應用的啟動都離不開AcitivityManagerService的調用與分配資源。有關oom_adj與oom_score_adj會在以後分析ActivityManagerService的時候加入相對詳細的論述。在這裡就不詳細說明。

有關Minfree的值的寫入,其實可以找到很多個地方,但是在最開始(還在用lowmemorykiller.c)的時候,是可以在lowmemorykiller.c中設置的。但是現在已經不用lowmemorykiller.c了,所以相對設置的地方也不一樣了。經查找驗證,Minfree的閥值控制,是由ActivictyManagerService和lowmemorykiller一並控制寫入的。

在ActivictyManagerService中,會調用ProcessList的applyDisplaySize()方法,從而調用updateOomLevels()的方法,開始算出相關的閥值.代碼如下:

private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {

……

final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;

for (int i=0; i=0或minfree_adj!=0時,mOomMinFree的值還要再算一次

if (minfree_abs >= 0) {

for (int i=0; i上面可以看出,LowMemoryKiller的閥值是通過多個判斷然後算出來的。不過,在正常情況下,除了4,5等級之外,LowMemoryKiller的0-3的閥值其實就是mOomMinFreeHigh32Bit[]裡面的值。我們以後假如需要對LowMemoryKiller的閥值進行定制,只需要針對這個方法去修改即可。另外,剛剛說到,Minfree的閥值控制,是由ActivictyManagerService和LowMemoryKiller一並控制寫入的。ActivictyManagerService把值確定了,通過socket機制,和LowMemoryKiller進行通訊,然後LowMemoryKiller就會把值寫入minfree中。在LowMemoryKiller的代碼(lmkd.c)中,接受寫入minfree的關鍵代碼如下:static void ctrl_connect_handler(uint32_t events __unused) {

//……

alen = sizeof(addr);

//接收到ActivictyManagerService傳過來的閥值

ctrl_dfd = accept(ctrl_lfd, &addr, &alen);

if (ctrl_dfd < 0) {

ALOGE("lmkd control socket accept failed; errno=%d", errno);

return;

}

ALOGI("ActivityManager connected");

maxevents++;

epev.events = EPOLLIN;

//將閥值保持進minfree

epev.data.ptr = (void *)ctrl_data_handler;

//……

ctrl_connect_handler()在接受到數據之後,經過多步的處理最後會調用writefilestring()將值進行寫入。static void writefilestring(char *path, char *s) {

int fd = open(path, O_WRONLY);

int len = strlen(s);

int ret;

if (fd < 0) {

ALOGE("Error opening %s; errno=%d", path, errno);

return;

}

ret = write(fd, s, len);

if (ret < 0) {

ALOGE("Error writing %s; errno=%d", path, errno);

} else if (ret < len) {

ALOGE("Short write on %s; length=%d", path, ret);

}

close(fd);

}

Minfree的值是每次開機都會進行寫入的,所以假如我們只是單純地在手機上,直接修改adj和minfree的值,重啟之後是不會生效的。那假如我們想定制某個應用的adj呢?假如只是定制單個應用的adj,其實我們可以在ActivityManagerService中的computeOomAdjLocked方法中進行定制。系統會調用這個方法不停更新正在運行的進程的adj。例如,假如我們想修改launcher默認的adj(默認launcher在後台運行時的adj為6),我們可以在computeOomAdjLocked中的: if (app == mHomeProcess) {

if (adj > ProcessList.HOME_APP_ADJ) {

// This process is hosting what we currently consider to be the

// home app, so we don't want to let it go into the background.

adj = ProcessList.HOME_APP_ADJ;

schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;

app.cached = false;

app.adjType = "home";

}

if (procState > ActivityManager.PROCESS_STATE_HOME) {

procState = ActivityManager.PROCESS_STATE_HOME;

}

}進行修改即可。我們可以通過app這個對象去獲得更新的進程的包名以便定制。有關LowMemoryKiller的分析就到這裡。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved