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//當前內存使用的軟限制

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目錄下。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章