cgroup之 memory cgroup(一)
如前文所述,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
以上是目录和节点的创建全过程