一文搞懂ss/css/css_set/cgroup/hierarchy的关系

本文详细介绍了Linux内核中的控制组(cgroup)机制,包括task_struct、cgroup、Cgroup subsys(ss)、cgroup hierarchy、Cgroup_Subsys_State(css)和css_set等核心概念。cgroup用于管理和限制进程资源,如CPU、内存等。ss代表资源控制器,如blkio、cpu等。cgroup hierarchy是cgroup的层级结构,每个节点代表一个cgroup。css描述了cgroup与ss的关联,而css_set则是一个包含一组css的对象,代表进程的资源限制配置。一个进程只能隶属于一个css_set,但可以属于不同层级结构的cgroup。文章强调了cgroup、css、css_set和hierarchy之间的复杂关系,并提供了相关参考链接以深入理解。
摘要由CSDN通过智能技术生成

基础定义

task_struct

在搞懂ss、css等一系列内核数据结构之前,先要了解task。

每个进程在内核中都维护了一个task_struct结构,里面包含了这个进程执行的所有信息。
cpu对进程的管理都是通过task_struct来执行的。

struct task_struct {
#ifdef CONFIG_CGROUPS
	//进程所属的css_set
	struct css_set __rcu		*cgroups;
	// 同属一个css_set的task的链表,将使用同一个css_set的task链接在一起
	struct list_head		cg_list;
#endif
    ... ...
}

Cgroup

全称control groups。是绑定了一组限制或者参数的进程集合。

cgroup数据结构实际上是cgroup结构的抽象,没有直接关联属于该cgroup的进程和资源限制,而是包含了该cgroup的相关信息。

struct cgroup {
	// cgroup所在的css
	struct cgroup_subsys_state self; 
    
    // 未使用时id=0,root cgroup始终为1,
    int id;
    // cgroup所在层级,即当前cgroup的深度
    int level;
     // 每当有个非空的css_set和这个cgroup关联的时候,就增加计数1
    int populated_cnt;
    
	struct kernfs_node *kn;		/* cgroup kernfs entry */
	struct cgroup_file procs_file;	/* handle for "cgroup.procs" */
	struct cgroup_file events_file;	/* handle for "cgroup.events" */

    // 与该cgroup关联的所有子系统,CGROUP_SUBSYS_COUNT为支持的css种类,一个种类最多与一个cgroup层级关联
	struct cgroup_subsys_state __rcu *subsys[CGROUP_SUBSYS_COUNT];

    // cgroup所在hierarchy的根节点
	struct cgroup_root *root;
    
    // 关联的css_set链表的表头(可以获取到该cgroup所有的css_set)
    struct list_head cset_links;

	// 保存BPF程序
	struct cgroup_bpf bpf;
    
    ... ...
};

在/proc/[pid]/cgroup中可以看到该进程pid所属的cgroup

# 查看一个docker容器的cgorup
[root@x86 ~] cat /proc/483664/cgroup
11:perf_event:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
10:devices:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
9:cpu,cpuacct:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
8:memory:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
7:hugetlb:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
6:cpuset:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
5:pids:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
4:blkio:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
3:net_cls:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
2:freezer:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
1:name=systemd:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282

# 查看当前所有cgroup
[root@x86 systemd]# lscgroup
freezer:/
freezer:/docker
freezer:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
net_cls:/
net_cls:/docker
net_cls:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
blkio:/
blkio:/docker
blkio:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
pids:/
pids:/docker
pids:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
cpuset:/
cpuset:/docker
cpuset:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
hugetlb:/
hugetlb:/docker
hugetlb:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
memory:/
memory:/docker
memory:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
cpu,cpuacct:/
cpu,cpuacct:/docker
cpu,cpuacct:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
devices:/
devices:/docker
devices:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
perf_event:/
perf_event:/docker
perf_event:/docker/2af72b99af9a8b883849289133a5c570a8df1e5b6506ee197a187ca655de3282
name=testno:/

Cgroup subsys(简称ss)

用来操作cgroup中进程的组件,也可以理解为资源控制器(resource controller)。

常见的subsystem

  • blkio — 为块设备设定输入 / 输出限制,比如物理驱动设备(包括磁盘、固态硬盘、USB 等)
  • cpu — 使用调度程序控制 task 对 CPU 的使用。
  • cpuacct — 自动生成 cgroup 中 task 对 CPU 资源使用情况的报告。
  • cpuset — 为 cgroup 中的 task 分配独立的 CPU(此处针对多处理器系统)和内存
  • devices — 开启或关闭 cgroup 中 task 对设备的访问。
  • freezer — 挂起或恢复 cgroup 中的 task。
  • memory — 设定 cgroup 中 task 对内存使用量的限定,并且自动生成这些 task 对内存资源使用情况的报告。
  • net_cls — 使用classid标记网络包,从而让tc可以识别由特定的cgroup中的 task生成的数据包。
  • net_prio — 提供可以动态设置每个网络接口的网络流量优先级的方法。
  • ns — 命名空间。
  • perf_event — 可以区分tasks所属的cgroup,用来对他们进行性能分析。{![perf: Linux CPU 性能探测器,详见 https://perf.wiki.kernel.org/index.php/Main_Page

在/proc/cgroups中可以看见被编译进当前内核的所有ss
文件中

  • hierarchy — subsys挂载的cgroup层级树 id
  • num_cgroups — 同一个层级中使用该子系统的cgroup数量
[root@x86 ~] cat /proc/cgroups
#subsys_name    hierarchy       num_cgroups     enabled
cpuset  		6       		4       		1
cpu     		9       		4       		1
cpuacct 		9       		4       		1
blkio   		4       		4       		1
memory  		8       		4       		1
devices 		10      		4       		1
freezer 		2       		4       		1
net_cls 		3       		4       		1
perf_event      11      		4       		1
hugetlb 		7       		4       		1
pids    		5       		4       		1

也可以通过命令查看当前支持的所有subsys

[root@x86 systemd]# lssubsys -a
\cpuset
cpu,cpuacct
blkio
memory
devices
freezer
net_cls
perf_event
hugetlb
pids

# 查看单个subsys挂载的hierarchy
[root@x86 systemd]# lssubsys –m cpu
cpu,cpuacct

# 查看当前所有subsys与对应的挂载点
[root@x86 logs]# lssubsys -am
cpuset /sys/fs/cgroup/cpuset
cpu,cpuacct /sys/fs/cgroup/cpu,cpuacct
blkio /sys/fs/cgroup/blkio
memory /sys/fs/cgroup/memory
devices /sys/fs/cgroup/devices
freezer /sys/fs/cgroup/freezer
net_cls /sys/fs/cgroup/net_cls
perf_event /sys/fs/cgroup/perf_event
hugetlb /sys/fs/cgroup/hugetlb
pids /sys/fs/cgroup/pids

内核支持的ss是有限的,系统启动的时候会初始化系统支持的所有ss。其中最重要的是它关联的 cftype 文件(cgroup_filesystem_type)。mount 和 mkdir 的时候创建的一系列文件实际上就是它们
看下内核代码中对ss的定义

struct cgroup_subsys {
    // 关联的css
	struct cgroup_subsys_state *(*css_alloc)(struct cgroup_subsys_state *parent_css);
    // 函数指针,定义了子系统ss对css_set结构的系列操作
    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);
    
    // 子系统对进程task的一系列操作
    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);
	void (*cancel_fork)(struct task_struct *task);
	void (*fork)(struct task_struct *task);
	void (*exit)(struct task_struct *task);
	void (*release)(struct task_struct *task);
	void (*bind)(struct cgroup_subsys_state *root_css);
    
    
	/* the following two fields are initialized automtically during boot */
	int id;
	const char *name;

    // 根节点指针
	struct cgroup_root *root;

    // 关联的css的idr
	struct idr css_idr;
    
    // 自动注册的基础cftype。两个指针可以指向同一个数组
	struct cftype *dfl_cftypes;	// 默认的文件系统结构
	struct cftype *legacy_cftypes;	// 继承的文件系统结构
    
    ... ...
};

在上文cgroup的定义中,可以看到,对于cgroup所关联的ss,有个变量
*subsys[CGROUP_SUBSYS_COUNT];
这个变量的初始化方式还挺有意思,可以在扩展中了解

cgroup hierarchy

知道了cgroup和ss,就不得不了解到hierarchy。也就是cgorup层级树。

cgroup 层级结构,相当于是cgroup层级树。每个节点都是一个cgroup。

以我个人理解,每个hierarchy都是对一个资源集合的配置。可以只是针对一个subsys(如cpu),也可以同时对多个subsys一起配置(如cpu,cpuact)。hierarchy的节点cgroup细化了该资源的具体配置。如下图,root为100%的cpu,两个节点cgroup中的进程分别限制40%和80%的cpu
在这里插入图片描述
关于hierarchy,有很多限制规则。这部分内容,由于翻译和其他问题(dddd),国内博客描述的十分杂乱,并且很多概念都有错误,建议查看外网文档了解更多。这里简单总结下

  1. 创建的hierarchy包含linux所有的进程,且在同一个hierarchy树中,一个进程只能属于一个节点。
  2. 每个hierarchy都可以关联0或多个subsys
  3. 一般来说,同一个subsys只能唯一一个hierarchy,但是如果两个hierarchy都没有绑定其他的subsystem,那么这一个subsys可以同时绑定这两个hierarchy。图示如下(这里国内博客基本都写错了,看官方文档应该是存在ss关联两个hierarchy的情况的)

If an active hierarchy with exactly the same set of subsystems already exists, it will be reused for the new mount. If no existing hierarchy matches, and any of the requested subsystems are in use in an existing hierarchy, the mount will fail with -EBUSY. Otherwise, a new hierarchy is activated, associated with the requested subsystems.

在这里插入图片描述
4. 但是如果这两个hierarchy中有任意一个关联了其他的subsys,那么该subsys就不能同时关联这两个hierarchy,如下图,如果cpu_mem_cg已经关联了memory,那么cpu subsys就不能同时关联cpu_cg和cpu_mem_cg。
在这里插入图片描述
5. 可以只建一个hierarchy,关联所有的subsys
6. 也可以建一个hierarchy,不关联任何subsys,如systemd。(该情况一般用于将进程分组,由应用程序自己决定要对各个cgroup做什么)
7. 创建了hierarchy后,系统所有的进程会默认加入到该hierarchy的默认root cgroup中。

Cgroup_Subsys_State(简称css)

Per-subsystem/per-cgroup state maintained by the system.
描述了一个cgroup节点和一个subsys的关联关系。

css的作用
1、维护hierarchy中的层级关系
2、辅助实现ss和cgroup之间的多对多关系 (css结构体中关联了cgroup和ss)

个人理解:css描述了一个hierarchy cgroup节点与一个ss之间的关系。css之间的父子关系可以反映一个hierarchy中的cgroup节点的父子关系。

struct cgroup_subsys_state {
    // 关联的cgroup
	struct cgroup *cgroup;

    // 关联的子系统
	struct cgroup_subsys *ss;

	// 所属的css链表
	struct list_head sibling;
	struct list_head children; // 子css组成的链表的头


	/*
	 * 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;

	// 父css
	struct cgroup_subsys_state *parent; 
    ... ...
};

css_set

A css_set is a structure holding pointers to a set of * cgroup_subsys_state objects
将一组进程绑定到cgroup,实现资源限制时,需要维护一组和它们相关联的css,这一组css就是css_set。

相当于一个具体的cgroup节点实现,包括了属于该cgroup中的所有task_struct和该cgroup相关的所有subsys限制。

进程描述符task_struct中的*cgroups指向的就是css_set指针,即指向了一组css。task可以属于多个cgroup,所以通过css_set可以获取到task所属的所有cgroup

● 一个进程只能隶属一个css_set
● 一个css_set可以包含多个进程
● 隶属同一个css_set中的进程受到相同的css_set所关联的资源限制。
● 指向某个css_set的进程会被添加到css_set的进程链表中

struct css_set {
    // css数组,每个css对应一个ss。创建后这个数组的内容是不可变的
	struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];

	// 该css_set的引用计数。如果有子系统引用该css_set,则计数+1
	refcount_t refcount;

	// 默认连接的cgroup ??这个值有什么用
	struct cgroup *dfl_cgrp;

    // 关联的task计数
	int nr_tasks;

    // 所有引用了该css_set的task链表
	struct list_head tasks;
    // mg_tasks为即将迁移进来或者移走到其他css_set的task
	struct list_head mg_tasks;
	struct list_head dying_tasks;

	// 参考https://lkml.org/lkml/2015/10/14/1059
	struct list_head task_iters;
    
    // 用于把所有css_set组成一个hash表,方便快速查找特定的css_set
    struct hlist_node hlist;
    // 指向一个cgrp_cset_links链表,
    // 列表包括所有引用了该css_set的cgroup的cgrp_cset_links
    struct list_head cgrp_links;

    // 该css_set是否无效
	bool dead;
    
    ... ...

};

各个结构的关系

cgroup、css、css_set和hierarchy的关系实例

这些结构关系比较抽象,所以简单画了一个图来举例子
在这里插入图片描述

  • 通常一个ss只能被关联到一个hierarchy中,如mem_cg及其子系统关联了memory,则cpu_cg及其子系统就不能再关联memory。特殊情况见hierarchy章节
  • 一个ss可以同时被关联到一个hierarchy的多个cgroup中,如cpu可以同时被cg1和cg2关联
  • 在同一个hierarchy中,ss和cgroup是多对多的关系
  • 一个css对应一个ss和一个cgroup的关联。如cg1将有2个相关联的css,css1关联cpu,css3关联cpuacct
  • 一个task有且仅对应一个css_set,这个css_set包含了该task所有的资源限制配置。如p1对应css_set1,包含了css1和css2,对cpu和memory进行了限制。
  • 同一个层级结构下的不同cgroup节点,相当于对相同资源的不同配置,即不同的子css_set。最终进程的css_set将由一个或多个hierarchy中的其中一个子css_set构成。比如进程组1的css_set可以包含cgrp1的关联css和cgrp3的css。
  • 在同一个hierarchy下,css_set只能关联一个cgroup。因为cgroups对同一种资源不允许有多个限制配置。如同一个css_set只能与cgrp1关联,限制进程可以使用60%的cpu时间片,不能同时再关联cgrp2,这样就出现了配置冲突。
    ● 一个cgroup可以关联多个css_set,如cgrp1可以和cgrp3组成得到css_set1,也可以和cgrp4组成得到css_set2

task和cgroup的关系

可以看我的另一篇博客【crash调试验证】获取task与cgroup更加形象地了解他们的关系

  • 没有直接的结构可以获取他们的关系
  • task可以有多个cgroup,但是这些cgroup必须属于不同的hierarchy。
  • 获取task的cgroup可以:task_struct->css_set->cgroup_subsys_state->cgroup
  • 获取cgroup的task_struct:cgroup->css_set->cgrp_cset_links->task

task和css_set的关系

  • task_struct和css_set是多对一的关系。
  • 一个task唯一对应一个css_set
  • 一个css_set可能被多个task关联
  • 一个task可以属于多个hierarchy中的cgroup,一个hierarchy的cgroup对应一个css_set。所以一个task可以对应多个css_set

cgroup和css_set的关系

  • 多对多关系。
  • 由于cgroup会包含多个task,每个task都css_set不尽相同,所以一个cgroup可能对应多个css_set
  • 一个css_set包含了多个css,每个css都对应了不同的subsys和cgroup,所以css_set可以与多个cgroup关联
  • css_set和cgroup的多对多关系通过数据结构cg_cgroup_link来关联。

关于cgroup和css_set的关系,内核引入了一个数据结构cg_cgroup_link来表示。

/* Link structure for associating css_set objects with cgroups */
struct cg_cgroup_link {
	/*
	 * List running through cg_cgroup_links associated with a
	 * cgroup, anchored on cgroup->css_sets
	 */
	struct list_head cgrp_link_list;
	struct cgroup *cgrp;
	/*
	 * List running through cg_cgroup_links pointing at a
	 * single css_set object, anchored on css_set->cg_links
	 */
	struct list_head cg_link_list;
	struct css_set *cg;
};

那为什么要使用cg_cgroup_link结构体呢?

  • 因为cgroup和css_set之间是多对多的关系。必须添加一个中间结构来将两者联系起来,这跟数据库模式设计是一个道理。
  • cg_cgroup_link中的cgrp和cg就是此结构体的联合主键,而cgrp_link_list和cg_link_list分别连入到cgroup和css_set相应的链表,使得能从cgroup或css_set都可以进行遍历查询。
    熟悉数据库的读者很容易理解,在数据库中,如果两张表是多对多的关系,那么如果不加入第三张关系表,就必须为一个字段的不同添加许多行记录,导致大量冗余。通过从主表和副表各拿一个主键新建一张关系表,可以提高数据查询的灵活性和效率。而一个task可能处于不同的cgroup,只要这些cgroup在不同的hierarchy中,并且每个hierarchy挂载的子系统不同;另一方面,一个cgroup中可以有多个task,这是显而易见的,但是这些task因为可能还存在在别的cgroup中,所以它们对应的css_set也不尽相同,所以一个cgroup也可以对应多个css_set。在系统运行之初,内核的主函数就会对root cgroups和css_set进行初始化,每次task进行fork/exit时,都会附加(attach)/分离(detach)对应的css_set。
  • 综上所述,添加cg_cgroup_link主要是出于性能方面的考虑,一是节省了task_struct结构体占用的内存,二是提升了进程fork()/exit()的速度。

参考链接

【内核源码】https://elixir.bootlin.com/linux/v5.4.182/source/include/linux/cgroup-defs.h
【基础和补充】https://www.oschina.net/question/2918182_2316288
【cs/ss/cgroup关系】https://tech.meituan.com/2015/03/31/cgroups.html
【参数解释】https://www.cnblogs.com/muahao/p/10280998.html
【基础解释】https://man7.org/linux/man-pages/man7/cgroups.7.html
【hierarchy,下文翻译】https://blog.csdn.net/qccz123456/article/details/90768509
【hierarchy和subsys的关系】https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/resource_management_guide/sec-relationships_between_subsystems_hierarchies_control_groups_and_tasks
【内核文档】https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/cgroups.html
【cg_cgroup_link,task和cgroup的路径分析】http://linux.laoqinren.net/kernel/cgroup-source-css_set-and-cgroup/#css_set%E5%92%8Ccgroup%E4%B9%8B%E9%97%B4%E7%9A%84%E5%85%B3%E7%B3%BB

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值