对于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 ;
}
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 *
};

 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 = ;
int tasksize;
int i;
int min_adj = OOM_ADJUST_MAX + ;
int selected_tasksize = ;
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 = ; 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 > )
lowmem_print(, "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 <= || min_adj == OOM_ADJUST_MAX + ) {
lowmem_print(, "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 <= )
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(, "select %d (%s), adj %d, size %d, to kill\n",
p->pid, p->comm, p->oomkilladj, tasksize);
}
if(selected != NULL) {
lowmem_print(, "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(, "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就越大,也就越有可能被选中。

深挖android low memory killer的更多相关文章

  1. Android进程回收机制LMK(Low Memory Killer)

    熟悉Android系统的童鞋都知道,系统出于体验和性能上的考虑,app在退到后台时系统并不会真正的kill掉这个进程,而是将其缓存起来.打开的应用越多,后台缓存的进程也越多.在系统内存不足的情况下,系 ...

  2. Android进程回收机制LMK(Low Memory Killer)【转】

    本文转载自:http://www.cnblogs.com/wytiger/p/5744752.html 熟悉Android系统的童鞋都知道,系统出于体验和性能上的考虑,app在退到后台时系统并不会真正 ...

  3. Android内存管理机制之一:low memory killer

    转载自http://www.miui.com/thread-29268-1-1.html 准备写这个专题之前,心里是有点忐忑的.首先Android内存管理机制相当复杂,想要讲清楚比较困难:其次对于绝大 ...

  4. OOM killer(Out Of Memory killer)

    最近接连遇到两个情况就是接连进程把kill掉 第一个情况就是有一个java进程被kill了.原因是我这个服务器上海部署了一个node服务,这个node服务大家都不熟悉.所以在使用的时候没有注意内存的使 ...

  5. 深挖的Java源代码之Integer.parseInt()vs Integer.valueOf()

    Integer.parseInt()和Integer.valueOf()都是用来将String转换为Int的,但是为什么Java会提供两个这样的方法呢,他们如果是同样的操作,岂不是多此一举? 我们来深 ...

  6. 【转帖】深挖NUMA

    深挖NUMA http://www.litrin.net/2017/10/31/深挖numa/ 首先列出本站之前相关的几篇帖子: Linux的NUMA机制 NUMA对性能的影响 cgroup的cpus ...

  7. 鸿蒙内核源码分析(文件句柄篇) | 深挖应用操作文件的细节 | 百篇博客分析OpenHarmony源码 | v69.01

    百篇博客系列篇.本篇为: v69.xx 鸿蒙内核源码分析(文件句柄篇) | 深挖应用操作文件的细节 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说 ...

  8. 安卓android WebView Memory Leak WebView内存泄漏

    Android WebView Memory Leak WebView内存泄漏 在这次开发过程中,需要用到webview展示一些界面,但是加载的页面如果有很多图片就会发现内存占用暴涨,并且在退出该界面 ...

  9. Android 性能测试——Memory Monitor 工具

    Android 性能测试--Memory Monitor 工具 Memory Monitor能做什么? 实时查看App的内存分配情况 快速判断App是否由于GC操作造成卡顿 快速判断App的Crash ...

随机推荐

  1. [AHOI2013]作业

    [AHOI2013]作业 题目大意: 给定一个长度为\(n(n\le10^5)\)的数列\(A(1\le A_i\le n)\).\(m(m\le10^6)\)次询问,每次询问区间\([l,r]\)内 ...

  2. 少女花海自拍撞亡:自拍PK火车速度,没有赢家

    心理学研究,自拍是一种自我强化的过程.人们都或多或少有着自我关注的倾向,即“自恋”.而人作为有思想的群体性社会动物,有着分享和交流的欲望.尤其是现代快节奏的生活常使人感觉“亚历山大”,自拍恰恰就成为释 ...

  3. 搭建《深入Linux内核架构》的Linux环境

    作者 彭东林 pengdonglin137@163.com 软件 Host: Ubuntu14.04 64 Qemu 2.8.0 Linux 2.6.24 busybox 1.24.2 gcc 4.4 ...

  4. 使用 MVVMLight 命令绑定(转)

    继上一篇文章的项目,我们实现了数据绑定到界面中.这篇文章我们将实现把命令绑定到按钮上,在XAML中的Button之类的都会有个Command属性可以让我们来绑定命令使用. 首先我们要实现的目标是,在界 ...

  5. Python可视化库

    转自小小蒲公英原文用Python可视化库 现如今大数据已人尽皆知,但在这个信息大爆炸的时代里,空有海量数据是无实际使用价值,更不要说帮助管理者进行业务决策.那么数据有什么价值呢?用什么样的手段才能把数 ...

  6. 移动web前端小结

    原文地址:http://blog.csdn.net/small_rice_/article/details/22690535 在智能手机横行的时代,作为一个web前端,不会编写移动web界面,的确是件 ...

  7. C#高级编程小结

    小结 这几章主要介绍了如何使用新的dynamic类型,还讨论了编译器在遇到dynamic类型时会做什么.还讨论了DLP,可以把它包含在简单的应用程序中.并通过Pythin使用DLR,执行Python脚 ...

  8. ubuntu下安装android sdk运行模拟器出现错误:

    ./emulator: error while loading shared libraries: libstdc++.so.6: cannot open shared object file: No ...

  9. WordPress基础:常用分类列表wp_list_categories

    函数:wp_list_categories($args) 作用:列出某个分类下的分类项目 用法: <ul> <?php $args= array( 'depth'=>1, 'o ...

  10. WordPress主题开发:开启导航菜单功能

    步骤一:开启导航菜单功能 <?php /* register_nav_menu( $location, $description ) 函数功能:开启导航菜单功能 @参数 string $loca ...