现在的位置: 首页 > 综合 > 正文

Low Memory Killer分析

2013年08月25日 ⁄ 综合 ⁄ 共 6208字 ⁄ 字号 评论关闭

Chipset: MSM8X25Q

Codebase: Android4.1

Kernel: 3.4.0

 

概念:

         Lowmemory killer是android基于oom killer做了改进。两者区别:

Oom killer: 当系统内存不足时,会根据当前进程的内存使用状况以及oom score来Kill掉某个进程。

Low memorykiller: 会周期性的检查系统内存状况,当系统内存小于其设定的返回时,根据 oom score和当前rss占用内存情况来杀掉进程。

         两者大同小异,不过lowmemory killer概念更简单,直接看代码吧。

         文件路径:android\kernel\drivers\staging\android\Lowmemorykiller.c

初始化:

static int __init lowmem_init(void)
{
	/*通过此接口注册之后,系统在内存回收释放时,kswap内核线程会遍历
Shrinker链表,并调用通过此接口注册的shrink函数。*/
	register_shrinker(&lowmem_shrinker);
	return 0;
}

static struct shrinker lowmem_shrinker = {
	.shrink = lowmem_shrink,	//核心函数在此!
	.seeks = DEFAULT_SEEKS * 16
};

核心调用:

在说核心函数之前,先了解下lowmemory killer的基础。

它利用lowmem_adj和lowmem_minfree这两个数组来作为评判当前内存不足的标准。如,当前系统空闲内存是63M时,比owmem_minfree[3]小,那么就会选择比lowmem_adj[3]的oom score adj大的进程中找到一个oom score adj最大的将其杀掉,当两个进程的oom score adj一样时,会选择内存占用最多杀掉!数组其他部分以此类推。

static int lowmem_adj[6] = {
	0,
	1,
	6,
	12,
};
static int lowmem_adj_size = 4;
static int lowmem_minfree[6] = {
	3 * 512,	/* 6MB */
	2 * 1024,	/* 8MB */
	4 * 1024,	/* 16MB */
	16 * 1024,	/* 64MB */
};
static int lowmem_minfree_size = 4;

lowmem_shrink():

static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
{
	struct task_struct *tsk;
	struct task_struct *selected = NULL;
	int rem = 0;
	int tasksize;
	int i;
	int min_score_adj = OOM_SCORE_ADJ_MAX + 1;
	int selected_tasksize = 0;
	int selected_oom_score_adj;
	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) -
						global_page_state(NR_SHMEM);
	/* 用户空间修改了lowmem_minfree和 lowmem_adj的情况就成立,后面再说*/
	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++) {
		/*系统总空闲内存或者文件占有内存数比当前设定的lowmem_minfree[i]小,
那么就取出相对应的oom score adj作为后面寻找最大oom score adj对应进程的基本
标准。*/
		if (other_free < lowmem_minfree[i] &&
		    other_file < lowmem_minfree[i]) {
			min_score_adj = lowmem_adj[i];
			break;
		}
	}
	if (sc->nr_to_scan > 0)
		lowmem_print(3, "lowmem_shrink %lu, %x, ofree %d %d, ma %d\n",
				sc->nr_to_scan, sc->gfp_mask, other_free,
				other_file, min_score_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 (sc->nr_to_scan <= 0 || min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
		lowmem_print(5, "lowmem_shrink %lu, %x, return %d\n",
			     sc->nr_to_scan, sc->gfp_mask, rem);
		return rem;
	}
	/*暂存,因为后面min_score_adj 会被覆盖。*/
	selected_oom_score_adj = min_score_adj;

	rcu_read_lock();
	for_each_process(tsk) {
		struct task_struct *p;
		int oom_score_adj;
		/*kernel thread不能Kill掉。*/
		if (tsk->flags & PF_KTHREAD)
			continue;
		/*处于die状态的进程不管。*/
		if (time_before_eq(jiffies, lowmem_deathpending_timeout)) {
			if (test_task_flag(tsk, TIF_MEMDIE)) {
				rcu_read_unlock();
				return 0;
			}
		}
		/*有mm的thread才能操作*/
		p = find_lock_task_mm(tsk);
		if (!p)
			continue;
		/*得到task对应的oom_score_adj*/
		oom_score_adj = p->signal->oom_score_adj;
		/*比当前的基本标准小就忽略。*/
		if (oom_score_adj < min_score_adj) {
			task_unlock(p);
			continue;
		}
		/*获得此进程的rss内存占用大小*/
		tasksize = get_mm_rss(p->mm);
		task_unlock(p);
		if (tasksize <= 0)
			continue;
		/*前后两进程开始比较了*/
		if (selected) {
			/*如果当前task的oom score比上次小,则不做处理*/
			if (oom_score_adj < selected_oom_score_adj)
				continue;
			/*如果前后两个task的oom score一样,而且此task 占有
内存比上次的task小时,也不做处理。*/
			if (oom_score_adj == selected_oom_score_adj &&
			    tasksize <= selected_tasksize)
				continue;
		}
		/*否则保存oom score和占用内存大小值*/
		selected = p;
		selected_tasksize = tasksize;
		selected_oom_score_adj = oom_score_adj;
		lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n",
			     p->pid, p->comm, oom_score_adj, tasksize);
	}
	if (selected) {
		lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n",
			     selected->pid, selected->comm,
			     selected_oom_score_adj, selected_tasksize);
		lowmem_deathpending_timeout = jiffies + HZ;
		/*发送SIGKILL信号杀死进程*/
		send_sig(SIGKILL, selected, 0);
		set_tsk_thread_flag(selected, TIF_MEMDIE);
		rem -= selected_tasksize;
	}
	lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n",
		     sc->nr_to_scan, sc->gfp_mask, rem);
	rcu_read_unlock();
	return rem;
}

lowmem_minfree/lowmem_adj修改:

注意到代码里还有几个函数:

__module_param_call(MODULE_PARAM_PREFIX, adj,
		    &lowmem_adj_array_ops,
		    .arr = &__param_arr_adj,
		    S_IRUGO | S_IWUSR, -1);
static struct kernel_param_ops lowmem_adj_array_ops = {
	.set = lowmem_adj_array_set,
	.get = lowmem_adj_array_get,
	.free = lowmem_adj_array_free,
};

static const struct kparam_array __param_arr_adj = {
	.max = ARRAY_SIZE(lowmem_adj),
	.num = &lowmem_adj_size,
	.ops = ¶m_ops_int,
	.elemsize = sizeof(lowmem_adj[0]),
	.elem = lowmem_adj,
};

这几个函数也比较简单,主要功能是允许用户空间修改lowmem_adj 和lowmem_minfree这两个数组,大家可自行分析。不过要注意的是数组大小最大为6!

         操作这两个数组的文件在:

android\frameworks\base\services\java\com\android\server\am\ProcessList.java

lowmem_adj对应的是mOomAdj数组的值,各个宏表示不同等级的oom score adj.

private final int[] mOomAdj = new int[] {
    FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
    BACKUP_APP_ADJ, HIDDEN_APP_MIN_ADJ, HIDDEN_APP_MAX_ADJ
};

通过updateOomLevels函数来修改数组值:

private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
        // Scale buckets from avail memory: at 300MB we use the lowest values to
        // 700MB or more for the top values.
        float scaleMem = ((float)(mTotalMemMb-300))/(700-300);

        // Scale buckets from screen size.
        int minSize = 320*480;  //  153600
        int maxSize = 1280*800; // 1024000  230400 870400  .264
        float scaleDisp = ((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);
        //Slog.i("XXXXXX", "scaleDisp=" + scaleDisp + " dw=" + displayWidth + " dh=" + displayHeight);

        StringBuilder adjString = new StringBuilder();
        StringBuilder memString = new StringBuilder();

        float scale = scaleMem > scaleDisp ? scaleMem : scaleDisp;
        if (scale < 0) scale = 0;
        else if (scale > 1) scale = 1;
		/*分辨率的不同会影响当前设定的lowmem_minfree。*/
        for (int i=0; i<mOomAdj.length; i++) {
            long low = mOomMinFreeLow[i];
            long high = mOomMinFreeHigh[i];
            mOomMinFree[i] = (long)(low + ((high-low)*scale));

            if (i > 0) {
                adjString.append(',');
                memString.append(',');
            }
            adjString.append(mOomAdj[i]);
            memString.append((mOomMinFree[i]*1024)/PAGE_SIZE);
        }
        Slog.e("Kris", "******************************* MINFREE: " + memString);
        if (write) {
			/*写进kernel~*/
            writeFile("/sys/module/lowmemorykiller/parameters/adj", adjString.toString());
            writeFile("/sys/module/lowmemorykiller/parameters/minfree", memString.toString());
        }
        // GB: 2048,3072,4096,6144,7168,8192
        // HC: 8192,10240,12288,14336,16384,20480
}

 // These are the low-end OOM level limits.  This is appropriate for an
 // HVGA or smaller phone with less than 512MB.  Values are in KB.
    private final long[] mOomMinFreeLow = new long[] {
            8192, 12288, 16384,
            24576, 28672, 32768
    };
    // These are the high-end OOM level limits.  This is appropriate for a
    // 1280x800 or larger screen with around 1GB RAM.  Values are in KB.
    private final long[] mOomMinFreeHigh = new long[] {
            32768, 40960, 49152,
            57344, 65536, 81920
    };

2013/04/27

抱歉!评论已关闭.