如前文所述,memcg的整體框架如下:
對於memcg,作爲一個cgroup的subsystem,它遵循hierarchy的所有規則,另外,對於hierarchy中cgroup的層級對memcg管理規則的影響,主要分兩方面:
1、 如果不啓用hierarchy,即mem_cgroup->use_hierarchy =false,則所有的memcg之間都是互相獨立,互不影響的,即使是父子cgroup之間,也跟兩個單獨創建的cgroup一樣。
2、 如果啓用hierarchy,即mem_cgroup->use_hierarchy =true,則memcg的統計需要考慮hierarchy中的層級關係,其影響因素主要有:
a. Charge/uncharge如果子cgroup中charge/uncharge了一個page,則其父cgroup和所有祖先cgroup都要charge/uncharge該page。
b. Reclaim因爲父cgroup的統計中包含了所有子cgroup中charge的page,所以在回收父cgroup中使用的內存時,也可以回收子cgroup中進程使用的內存。
c. Oom因爲父cgroup的統計中包含了所有子cgroup中charge的page,所以如果父cgroup需要出發oom,則oom可以考慮殺死子cgroup中的進程,達到釋放內存的效果。
前面已經講過初始化cgroup的基本流程,下面將初始化過程中文件目錄以及節點的創建:
在系統初始化過程中會mount一個路徑/dev/memcg/,同時創建兩個路徑/dev/memcg/apps/和/dev/memcg/system/:
# root memory control cgroup, used by lmkd
mkdir /dev/memcg 0700 root system
mount cgroup none /dev/memcg nodev noexec nosuid memory
# app mem cgroups, used by activity manager, lmkd and zygote
mkdir /dev/memcg/apps/ 0755 system system
# cgroup for system_server and surfaceflinger
mkdir /dev/memcg/system 0550 system system
其中/dev/memcg爲root目錄。
mount的主要流程如下:
sys_mount(fs/namespace.c)
-->do_mount(kernel_dev, dir_name, kernel_type, flags, (void *)data_pate)
-->do_new_mount (&path, type_page, flags, mnt_flags,dev_name, data_page)
--> vfs_kern_mount(type, flags, name, data)
--> mount_fs(type, flags, name, data)
--> type->mount(type, flags, name, data)
--> cgroup_mount(fs_type, flags, unused_dev_name, data)
mkdir的流程如下:
sys_mkdir(fs/namei.c)
-->sys_mkdirat(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
-->vfs_mkdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode)
-->dir->i_op->mkdir(dir, dentry, mode)
-->cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, umode_t mode)
-->kernfs_create_dir(parent->kn, name, mode, cgrp)
cgroup在創建目錄的同時,會在該目錄下創建相關的文件節點:
cgroup.clone_children
cgroup.event_control
cgroup.procs
cgroup.sane_behavior
注:相關節點的創建與否,創建在哪個目錄下,根目錄能否創建,與節點描述結構體struct cftype的flags有關。
memory cgroup以 cgroup爲單位,每個cgroup下面會有很多的文件節點和目錄:
目錄的創建流程前面已經說明,大部分的節點的創建流程如下:
cgroup_init()①-->cgroup_add_cftypes()-->cgroup_apply_cftypes()-->cgroup_addrm_files()-->cgroup_add_file()
②-->cgroup_add_dfl_cftypes()-->cgroup_add_cftypes()-->cgroup_apply_cftypes()-->cgroup_addrm_files()
-->cgroup_add_file()
③-->cgroup_add_legacy_cftypes()-->cgroup_add_cftypes()-->cgroup_apply_cftypes()-->cgroup_addrm_files()
-->cgroup_add_file()
cgroup節點分爲默認節點和從父目錄繼承,如果這兩者不相同,則走第一條線,否則分別走第二和第三條線,memory cgroup不一致,故走第二和第三條:
tatic struct cftype memory_files[] = {
{
.name = "current",
.flags = CFTYPE_NOT_ON_ROOT,
.read_u64 = memory_current_read,
},
{
.name = "low",
.flags = CFTYPE_NOT_ON_ROOT,
.seq_show = memory_low_show,
.write = memory_low_write,
},
{
.name = "high",
.flags = CFTYPE_NOT_ON_ROOT,
.seq_show = memory_high_show,
.write = memory_high_write,
},
……………………………………
}
static struct cftype mem_cgroup_legacy_files[] = {
{
.name = "usage_in_bytes",
.private = MEMFILE_PRIVATE(_MEM, RES_USAGE),
.read_u64 = mem_cgroup_read_u64,
},
{
.name = "max_usage_in_bytes",
.private = MEMFILE_PRIVATE(_MEM, RES_MAX_USAGE),
.write = mem_cgroup_reset,
.read_u64 = mem_cgroup_read_u64,
},
……………………
}
struct cgroup_subsys memory_cgrp_subsys = {
……………………
.dfl_cftypes = memory_files,
.legacy_cftypes = mem_cgroup_legacy_files,
.early_init = 0,
};
還有一部分cgroup共有的一些節點的創建流程如下:
cgroup_mkdir()-->css_populate_dir()-->cgroup_addrm_files()-->cgroup_add_file()
共有節點特指如下節點:
cgroup.clone_children
cgroup.event_control
cgroup.procs
cgroup.sane_behavior
以上是目錄和節點的創建全過程