cgroup之memory cgroup(二)

memory cgroup起作用主要是限制各个进程使用内存大小,当其值超越限制值时会发生oom,讲当前进程清理掉。

memcg oom的主要运行流程如下:

首先进程在申请内存时会进行try_charge操作,此时会进行oom检测,如果是oom,则会把当前的memcg赋值给进程的memcg_in_oom成员,然后在缺页中断中会对当前进程是否存在oom进行判断,如果是,则发送oom events事件,然后清理进程。调用流程如下:

malloc->try_charge()->mem_cgroup_oom()------------------------>进程oom

handle_mm_fault()->mem_cgroup_oom_synchronize()->mem_cgroup_oom_notify()---------------->发送oom events

                                                                                       ->mem_cgroup_out_of_memory()----------------->执行杀进程操作

在memory cgroup中有几个关键节点:

memory.memsw.limit_in_bytes//带上swap空间的最大值

memory.memsw.usage_in_bytes//当前带上swap的内存占用

memory.memsw.max_usage_in_bytes//历史使用的最大内存占用,包含swap空间占用

memory.soft_limit_in_bytes//当前内存使用的软限制

扫描二维码关注公众号,回复: 5828025 查看本文章

memory.limit_in_bytes//当前内存使用的硬限制

memory.max_usage_in_bytes//当前历史的最大内存占用

memory.usage_in_bytes//当前的内存占用

各个cgroup的内存用量通过page_counter结构来统计,内存申请成功和释放通过charge和uncharge来完成统计值的更新,在charge过程中会根据page的类型更新percpu类型结构体mem_cgroup_stat_cpu中的count值,如果是anon page,则为rss类型,如果是file page,则是cache类型,还会更新page in/out个数:

static void mem_cgroup_charge_statistics(struct mem_cgroup *memcg,
					 struct page *page,
					 bool compound, int nr_pages)
{	

    if (PageAnon(page))
		__this_cpu_add(memcg->stat->count[MEM_CGROUP_STAT_RSS],
				nr_pages);
	else
		__this_cpu_add(memcg->stat->count[MEM_CGROUP_STAT_CACHE],
				nr_pages);

………………
	if (nr_pages > 0)
		__this_cpu_inc(memcg->stat->events[MEM_CGROUP_EVENTS_PGPGIN]);
	else {
		__this_cpu_inc(memcg->stat->events[MEM_CGROUP_EVENTS_PGPGOUT]);
		nr_pages = -nr_pages; /* for event */
	}
}

 读取cgroup内存占用时分为两部分,一部分为root目录以及其他,root目录通过统计CPUstat下的rss和cache类page数之和来实现,另外的通过读取cgroup的page_counter结构体的count值获取:

static unsigned long mem_cgroup_usage(struct mem_cgroup *memcg, bool swap)
{
	unsigned long val = 0;

	if (mem_cgroup_is_root(memcg)) {-----------根目录
		struct mem_cgroup *iter;

		for_each_mem_cgroup_tree(iter, memcg) {
			val += mem_cgroup_read_stat(iter,
					MEM_CGROUP_STAT_CACHE);---------------(加上cache值)
			val += mem_cgroup_read_stat(iter,
					MEM_CGROUP_STAT_RSS);--------------(加上anon页值)
			if (swap)
				val += mem_cgroup_read_stat(iter,
						MEM_CGROUP_STAT_SWAP);------------(如果需要,加上swap值)
		}
	} else {---------------非根目录
		if (!swap)
			val = page_counter_read(&memcg->memory);
		else
			val = page_counter_read(&memcg->memsw);
	}
	return val;
}

当usage超过soft_limit时,failcnt会加1,后面如果因为cgroup内存占用超过硬限制触发oom会根据failcnt值来确定需要清理的进程。

bool page_counter_try_charge(struct page_counter *counter,
			     unsigned long nr_pages,
			     struct page_counter **fail)
{
	struct page_counter *c;

	for (c = counter; c; c = c->parent) {
		long new;
		new = atomic_long_add_return(nr_pages, &c->count);
		if (new > c->limit) {
			atomic_long_sub(nr_pages, &c->count);
			/*
			 * This is racy, but we can live with some
			 * inaccuracy in the failcnt.
			 */
			c->failcnt++;
			*fail = c;
			goto failed;
		}
		/*
		 * Just like with failcnt, we can live with some
		 * inaccuracy in the watermark.
		 */
		if (new > c->watermark)
			c->watermark = new;
	}
	return true;

failed:
	for (c = counter; c != *fail; c = c->parent)
		page_counter_cancel(c, nr_pages);

	return false;
}

注:通过查看Android kernel代码可以看到当前Android P memory cgroup的内存限制功能并没有完全enable,只是在lmkd工作过程中计算内存压力值时获取了root目录下面的总的内存占用和内存+swap的占用值,当前的memory cgroup不存在父子关系,各层cgroup之间的use_hierarchy的值均为0。即各个进程的内存占用总值并没有通过逐级累加的方式统计到root目录下。 

猜你喜欢

转载自blog.csdn.net/zsj100213/article/details/89151442