linux内核如何修改lowmem,技术内幕:Android对Linux内核的增强 Low Memory Killer
6 09 2013
技術內幕:Android對Linux內核的增強 Low Memory Killer
Low Memory Killer(低內存管理)
對于PC來說,內存是 至關重要。如果某個程序發生了內存泄漏,那么一般情況下系統就會將其進程Kill掉。Linux中使用一種名稱為OOM(Out Of Memory,內存不足)的機制來完成這個任務,該機制會在系統內存不足的情況下,選擇一個進程并將其Kill掉。Android則使用了一個新的機制 ——Low Memory Killer來完成同樣的任務。下面首先來看看Low Memory Killer機制的原理以及它是如何選擇將被Kill的進程的。
1.Low Memory Killer的原理和機制
Low Memory Killer在用戶空間中指定了一組內存臨界值,當其中的某個值與進程描述中的oom_adj值在同一范圍時,該進程將被Kill掉。通常,在“/sys /module/lowmemorykiller / parameters/adj”中指定oom_adj的最小值,在“/sys/module/lowmemorykiller/parameters /minfree”中儲存空閑頁面的數量,所有的值都用一個逗號將其隔開且以升序排列。比如:把“0,8”寫入到/sys/module /lowmemorykiller/parameters/adj中,把“1024,4096”寫入到/sys/module/lowmemory- killer/parameters/minfree中,就表示當一個進程的空閑存儲空間下降到4096個頁面時,oom_adj值為8或者更大的進程會 被Kill掉。同理,當一個進程的空閑存儲空間下降到1024個頁面時,oom_adj值為0或者更大的進程會被Kill掉。我們發現在 lowmemorykiller.c中就指定了這樣的值,如下所示:
static int lowmem_adj[6] = {
0,
1,
6,
12,
};
static int lowmem_adj_size = 4;
static size_t lowmem_minfree[6] = {
3*512, // 6MB
2*1024, // 8MB
4*1024, // 16MB
16*1024, // 64MB
};
static int lowmem_minfree_size = 4;
這就說明,當一個進程的空閑空間下降到3′512個頁面時,oom_adj值為0或者更大的進程會被Kill掉;當一個進程的空閑空間下降到 2′1024個頁面時,oom_adj值為10或者更大的進程會被Kill掉,依此類推。其實更簡明的理解就是滿足以下條件的進程將被優先Kill掉:
task_struct->signal_struct->oom_adj越大的越優先被Kill。
占用物理內存最多的那個進程會被優先Kill。
進程描述符中的signal_struct->oom_adj表示當內存短缺時進程被選擇并Kill的優先級,取值范圍是-17~15。如果是-17,則表示不會被選中,值越大越可能被選中。當某個進程被選中后,內核會發送SIGKILL信號將其Kill掉。
實際上,Low Memory Killer驅動程序會認為被用于緩存的存儲空間都要被釋放,但是,如果很大一部分緩存存儲空間處于被鎖定的狀態,那么這將是一個非常嚴重的錯誤,并且當正常的oom killer被觸發之前,進程是不會被Kill掉的。
2.Low Memory Killer的具體實現
在了解了Low Memory Killer的原理之后,我們再來看如何實現這個驅動。Low Memory Killer驅動的實現位于drivers/misc/lowmemorykiller.c。
該驅動的實現非常簡單,其初始化與退出操作也是我們到目前為止見過的最簡單的,代碼如下:
static int __init lowmem_init(void)
{
register_shrinker(&lowmem_shrinker);
return 0;
}
static void __exit lowmem_exit(void)
{
unregister_shrinker(&lowmem_shrinker);
}
module_init(lowmem_init);
module_exit(lowmem_exit);
在初始化函數lowmem_init中通過register_shrinker注冊了一個shrinker為lowmem_shrinker;退出時又 調用了函數lowmem_exit,通過unregister_shrinker來卸載被注冊的lowmem_shrinker。其中 lowmem_shrinker的定義如下:
static struct shrinker lowmem_shrinker = {
.shrink = lowmem_shrink,
.seeks = DEFAULT_SEEKS * 16
};
lowmem_shrink是這個驅動的核心實現,當內存不足時就會調用lowmem_shrink方法來Kill掉某些進程。下面來分析其具體實現,實現代碼如下:
static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask)
{
struct task_struct *p;
struct task_struct *selected = NULL;
int rem = 0;
int tasksize;
int i;
int min_adj = OOM_ADJUST_MAX + 1;
int selected_tasksize = 0;
int array_size = ARRAY_SIZE(lowmem_adj);
int other_free = global_page_state(NR_FREE_PAGES);
int other_file = global_page_state(NR_FILE_PAGES);
if(lowmem_adj_size < array_size)
array_size = lowmem_adj_size;
if(lowmem_minfree_size < array_size)
array_size = lowmem_minfree_size;
for(i = 0; i < array_size; i++) {
if (other_free < lowmem_minfree[i] &&
other_file < lowmem_minfree[i]) {
min_adj = lowmem_adj[i];
break;
}
}
if(nr_to_scan > 0)
lowmem_print(3, “lowmem_shrink %d, %x, ofree %d %d, ma %d\n”, nr_to_scan,
gfp_mask, other_free, other_file, min_adj);
rem = global_page_state(NR_ACTIVE_ANON) +
global_page_state(NR_ACTIVE_FILE) +
global_page_state(NR_INACTIVE_ANON) +
global_page_state(NR_INACTIVE_FILE);
if (nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) {
lowmem_print(5, “lowmem_shrink %d, %x, return %d\n”, nr_to_scan, gfp_mask,
rem);
return rem;
}
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;
lowmem_print(2, “select %d (%s), adj %d, size %d, to kill\n”,
p->pid, p->comm, p->oomkilladj, tasksize);
}
if(selected != NULL) {
lowmem_print(1, “send sigkill to %d (%s), adj %d, size %d\n”,
selected->pid, selected->comm,
selected->oomkilladj, selected_tasksize);
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;
}
可以看出,其中多處用到了global_page_state函數。有很多人找不到這個函數,其實它被定義在了linux/vmstat.h中,其參數使用zone_stat_item枚舉,被定義在linux/mmzone.h中,具體代碼如下:
enum zone_stat_item {
NR_FREE_PAGES,
NR_LRU_BASE,
NR_INACTIVE_ANON = NR_LRU_BASE,
NR_ACTIVE_ANON,
NR_INACTIVE_FILE,
NR_ACTIVE_FILE,
#ifdef CONFIG_UNEVICTABLE_LRU
NR_UNEVICTABLE,
NR_MLOCK,
#else
NR_UNEVICTABLE = NR_ACTIVE_FILE, /* 避免編譯錯誤*/
NR_MLOCK = NR_ACTIVE_FILE,
#endif
NR_ANON_PAGES,????????/* 匿名映射頁面*/
NR_FILE_MAPPED,????????/*映射頁面*/
NR_FILE_PAGES,
NR_FILE_DIRTY,
NR_WRITEBACK,
NR_SLAB_RECLAIMABLE,
NR_SLAB_UNRECLAIMABLE,
NR_PAGETABLE,
NR_UNSTABLE_NFS,
NR_BOUNCE,
NR_VMSCAN_WRITE,
NR_WRITEBACK_TEMP,????/* 使用臨時緩沖區*/
#ifdef CONFIG_NUMA
NUMA_HIT,????????????/* 在預定節點上分配*/
NUMA_MISS,????????????/* 在非預定節點上分配*/
NUMA_FOREIGN,
NUMA_INTERLEAVE_HIT,
NUMA_LOCAL,????????????/* 從本地頁面分配*/
NUMA_OTHER,????????????/* 從其他節點分配 */
#endif
NR_VM_ZONE_STAT_ITEMS };
再回過頭來看owmem_shrink函數,首先確定我們所定義的lowmem_adj和lowmem_minfree數組的大小(元素個數)是否一 致,如果不一致則以最小的為基準。因為我們需要通過比較lowmem_minfree中的空閑儲存空間的值,以確定最小min_adj值(當滿足其條件 時,通過其數組索引來尋找lowmem_adj中對應元素的值);之后檢測min_adj的值是否是初始值“OOM_ADJUST_MAX + 1”,如果是,則表示沒有滿足條件的min_adj值,否則進入下一步;然后使用循環對每一個進程塊進行判斷,通過min_adj來尋找滿足條件的具體進 程(主要包括對oomkilladj和task_struct進行判斷);最后,對找到的進程進行NULL判斷,通過 “force_sig(SIGKILL, selected)”發送一條SIGKILL信號到內核,Kill掉被選中的“selected”進程。
關于Low Memory Killer的分析就到這里,在了解了其機制和原理之后,我們發現它的實現非常簡單,與標準的Linux OOM機制類似,只是實現方式稍有不同。標準Linux的OOM Killer機制在mm/oom_kill.c中實現,且會被__alloc_pages_may_oom調用(在分配內存時,即mm /page_alloc.c中)。oom_kill.c最主要的一個函數是out_of_memory,它選擇一個bad進程Kill,Kill的方法同 樣是通過發送SIGKILL信號。在out_of_memory中通過調用select_bad_process來選擇一個進程Kill,選擇的依據在 badness函數中實現,基于多個標準來給每個進程評分,評分最高的被選中并Kill。一般而言,占用內存越多,oom_adj就越大,也就越有可能被 選中。
Comments are currently closed.
總結
以上是生活随笔為你收集整理的linux内核如何修改lowmem,技术内幕:Android对Linux内核的增强 Low Memory Killer的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux crontab环境变量,Li
- 下一篇: linux 匹配文本中的ip,linux