目录
1.2 struct cgroup_subsys_state是什么,以及与strcut cgroup关系
1.6 /sys/fs/cgroup/memcg/test1目录结构怎么知道对应哪个控制组?
了解Linux Cgroup机制的都清楚,cgroup机制最终是通过树形结构表示的,既然是树形结构,我们必须搞清楚这个树形结构是怎么创建起来的,以及在内核源码中是怎么表达这种树形结构的,我们是否非常清楚怎么遍历整个树,本文将基于Linux5.15源码分析cgroup树形结构的创建过程。
假设我们有如下层级结构:
/sys/fs/cgroup/memcg/app
/sys/fs/cgroup/memcg/app/test1
/sys/fs/cgroup/memcg/app/test2
app就是test1和test2的父节点,test1和test2是兄弟节点,我们按照这个例子具体展开分析。
1.cgroup基础理论和数据结构
第一个很容易想到的问题就是test1是一个控制组,那么这个控制组在内核是用什么数据结构表示呢?这个数据结构还要能够表达出树形结构;同时我们怎么知道这个控制组是一个memory,还是blkio,还是其他呢?这就要从内核控制组的源码开始入手,内核通过struct cgroup表示一个控制组。
注意:这里说的cgroup并不是我们经常看到的mem_cgroup这种,cgroup结构体代表的是一个通用的控制组概念,而mem_cgroup代表的是具体的某个控制,内核称为subsys cgroup:为什么叫子控制组,因为内核中包含非常多的子控制组,比如memory或者io等等,具体在内核include/linux/cgroup_subsys.h
/* generate an array of cgroup subsystem pointers */
#define SUBSYS(_x) [_x ## _cgrp_id] = &_x ## _cgrp_subsys,
struct cgroup_subsys *cgroup_subsys[] = {
#include <linux/cgroup_subsys.h>
};
#undef SUBSYS
#if IS_ENABLED(CONFIG_CPUSETS)
SUBSYS(cpuset)
#endif
#if IS_ENABLED(CONFIG_CGROUP_SCHED)
SUBSYS(cpu)
#endif
#if IS_ENABLED(CONFIG_CGROUP_CPUACCT)
SUBSYS(cpuacct)
#endif
#if IS_ENABLED(CONFIG_BLK_CGROUP)
SUBSYS(io)
#endif
#if IS_ENABLED(CONFIG_MEMCG)
SUBSYS(memory)
#endif
#if IS_ENABLED(CONFIG_CGROUP_DEVICE)
SUBSYS(devices)
#endif
#if IS_ENABLED(CONFIG_CGROUP_FREEZER)
SUBSYS(freezer)
#endif
#if IS_ENABLED(CONFIG_CGROUP_NET_CLASSID)
SUBSYS(net_cls)
#endif
#if IS_ENABLED(CONFIG_CGROUP_PERF)
SUBSYS(perf_event)
#endif
#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO)
SUBSYS(net_prio)
#endif
#if IS_ENABLED(CONFIG_CGROUP_HUGETLB)
SUBSYS(hugetlb)
#endif
#if IS_ENABLED(CONFIG_CGROUP_PIDS)
SUBSYS(pids)
#endif
#if IS_ENABLED(CONFIG_CGROUP_RDMA)
SUBSYS(rdma)
#endif
#if IS_ENABLED(CONFIG_CGROUP_MISC)
SUBSYS(misc)
#endif
1.1 struct cgroup_subsys是什么
通过上面的介绍我们知道linux内核定义类似memory,io,cpu等几种具体的子控制组,cgroup_subsys数据结构本质上其实不代表某个具体的控制组实体,而是控制组实体的操控的结构体,定义了具体控制实体的函数,比如创建一个子控制组实体mem_cgroup:
/*
* Control Group subsystem type.
* See Documentation/admin-guide/cgroup-v1/cgroups.rst for details
*/
struct cgroup_subsys {
struct cgroup_subsys_state *(*css_alloc)(struct cgroup_subsys_state *parent_css);
int (*css_online)(struct cgroup_subsys_state *css);
void (*css_offline)(struct cgroup_subsys_state *css);
void (*css_released)(struct cgroup_subsys_state *css);
void (*css_free)(struct cgroup_subsys_state *css);
void (*css_reset)(struct cgroup_subsys_state *css);
void (*css_rstat_flush)(struct cgroup_subsys_state *css, int cpu);
int (*css_extra_stat_show)(struct seq_file *seq,
struct cgroup_subsys_state *css);
int (*can_attach)(struct cgroup_taskset *tset);
void (*cancel_attach)(struct cgroup_taskset *tset);
void (*attach)(struct cgroup_taskset *tset);
void (*post_attach)(void);
int (*can_fork)(struct task_struct *task,
struct css_set *cset);
void (*cancel_fork)(struct task_struct *task, struct css_set *cset);
void (*fork)(struct task_struct *task);
void (*exit)(struct task_struct *task);
void (*release)(struct task_struct *task);
...
}
1.2 struct cgroup_subsys_state是什么,以及与strcut cgroup关系
我们知道cgroup_subsys不代表具体的控制组实体,那么像mem_cgroup这种具体的子控制组是用什么数据结构表示:cgroup_subsys_state,这个类是所有具体子控制组对象的基类,就是说mem_cgroup的基类就是cgroup_subsys_state。注意:struct cgroup的struct cgroup_subsys_state是通过self字段表示,主要用来表示cgroup本身,而不是指向具体的subsys cgroup,具体的subsys cgroup是有struct cgroup的subsys[]数组存储的。
/*
* Per-subsystem/per-cgroup state maintained by the system. This is the
* fundamental structural building block that controllers deal with.
*
* Fields marked with "PI:" are public and immutable and may be accessed
* directly without synchronization.
*/
struct cgroup_subsys_state {
/* PI: the cgroup that this css is attached to */
struct cgroup *cgroup;
/* PI: the cgroup subsystem that this css is attached to */
struct cgroup_subsys *ss;
/* reference count - access via css_[try]get() and css_put() */
struct percpu_ref refcnt;
/* siblings list anchored at the parent's ->children */
struct list_head sibling;
struct list_head children;
/* flush target list anchored at cgrp->rstat_css_list */
struct list_head rstat_css_node;
/*
* PI: Subsys-unique ID. 0 is unused and root is always 1. The
* matching css can be looked up using css_from_id().
*/
int id;
unsigned int flags;
/*
* Monotonically increasing unique serial number which defines a
* uniform order among all csses. It's guaranteed that all
* ->children lists are in the ascending order of ->serial_nr and
* used to allow interrupting and resuming iterations.
*/
u64 serial_nr;
/*
* Incremented by online self and children. Used to guarantee that
* parents are not offlined before their children.
*/
atomic_t online_cnt;
/* percpu_ref killing and RCU release */
struct work_struct destroy_work;
struct rcu_work destroy_rwork;
/*
* PI: the parent css. Placed here for cache proximity to following
* fields of the containing structure.
*/
struct cgroup_subsys_state *parent;
};
struct mem_cgroup {
struct cgroup_subsys_state css;
/* Private memcg ID. Used to ID objects that outlive the cgroup */
struct mem_cgroup_id id;
/* Accounted resources */
struct page_counter memory; /* Both v1 & v2 */
union {
struct page_counter swap; /* v2 only */
struct page_counter memsw; /* v1 only */
};
/* Legacy consumer-oriented counters */
struct page_counter kmem; /* v1 only */
struct page_counter tcpmem; /* v1 only */
/* Range enforcement for interrupt charges */
struct work_struct high_work;
unsigned long soft_limit;
/* vmpressure notifications */
struct vmpressure vmpressure;
...
}
我们知道基类一般要做一些公共的操作,那么cgroup的树形结构正好放在cgroup_subsys_state数据结构中,因为树形结构是每一种控制组都需要的基础逻辑,而某个具体的subsys cgroup可以定义该类控制组差异化的定字段。
树形结构通过cgroup_subsys_state的几个重要的字段完成:parent,sibling,children
按照本文刚开始的例子说明:
parent:当前控制组的父控制组。如果当前控制组指test1,那么parent指向的就是app控制组
children:当前控制组的子控制组。如果当前控制组是app,那么children就包括了test1,test2
sibling:兄弟控制组,test1和test2就是兄弟控制组。
那么上面的三个字段构建树形的呢?children相当于一个list_head的head,那么sibling就是add到children的每个元素,如下:
1.3 struct cgroup是什么
通过上面的分析,我们已经知道cgroup是指控制组的概念,如果我们在