深入浅出cgroup

语言: CN / TW / HK

一、什么是 cgroup

Cgroup是linux内核用来控制系统资源的机制, 它将操作系统中的所有进程以组为单位划分,给这一组进程定义对某一类资源特定的访问权限。 Cgroup用子系统(subsystem)来描述所能控制的系统资源, 子系统具有多种类型,每个类型的子系统都代表一种系统资源,比如 freezer、CPU、memory IO 等。以 freezer子系统为例,这个子系统可以对一组线程批量冻结,使用下面命令将打开freezer子系统: mount cgroup none /dev/freezer freezer

该命令将子系统挂载于 /dev/freezer目录,接下来可以在/dev/freezer目录下创建若干个目录,例如目录top、background,每个目录代表一组线程的资源分配行为, cgroup实例描述,那么多级的目录以cgroup实例的形式组成了一个树形结构 。接下来,可以给 top组或者background组配置组内进程,

echo 1199 > /dev/freezer/top/cgroup.procs

当前 cgroup版本已经不支持同一进程组内不同线程分属于子系统内不同cgroup。配置完top组内的线程后,可以通过操作freezer.state节点配置组内所有线程冻结:

echo FROZEN > /dev/freezer/top/freezer.state

解冻组内所有线程:

echo THAW > /dev/freezer/top/freezer.state

二、 关键数据结构以及相互关系

Cgroup子系统以cgroup_subsys结构体描述, 子系统中每个目录由 cgroup结构体描述 ,这个目录维护了一组线程的资源访问属性。子系统的 cgroup_subsys结构体通过cgroup_root结构体与子系统根目录的cgroup建立联系。目录之间的树形结构通过cgroup结构体中的cgroup_subsys_state结构体描述,cgroup_subsys_state结构体中有指向父目录的parent 指针、以及兄弟和孩子链表。如图:

由于线程与 cgroup目录具有多对多 的关系,即一个 cgroup 目录中含有多个线程,一个线程属于不同子系统中的多个 cgroup linux通过css_set结构体以及cgrp_cset_link结构体 来描述这个多对多的关系。 css_set 结构代表一组线程,这些线程在各个子系统内属于同一 cgroup目录;cgrp_cset_link 代表一个 css_set和一个cgroup的映射关系,cgroup有一个cgrp_cset_link 链表,通过这个链表可以找到这个 cgroup目录下所有线程组成的css_set,同样,css_set也维护一个cgrp_cset_link 链表,通过这个链表,可以找到这个 css_set在各个子系统中所归属的cgroup。 如下图 css_set Z下的所有线程属于freezer子系统的cgroupA以及memory子系统的cgroupC, css_set X下的所有线程属于freezer子系统的cgroupA

三、cgroup核心逻辑

当某个子系统被挂载使能后,系统中所有线程默认处于子系统根目录所代表的 cgroup实例中。

1.  配置 task到目标cgroup

用户或者应用程序通过往 cgroup.procs节点写入pid,cgroup.procs节点的write函数对应到cgroup_procs_write。

cgroup_kn_lock_live函数根据当前节点目录解析到该目录所对应的cgroup实体,通过写权限检查以后,进入到主逻辑cgroup_attach_task函数里。

cgroup_migrate_prepare_dst 函数保存当前 task所在css_set结构,

cgroup_migrate_prepare_dst函数利用保存的当前css_set,查找是否存在满足条件的目标css_set,如果不存在满足条件的目标css_set,则创建一个新的css_set,插入多对多二维关系链表中。接下来cgroup_migrate函数将task从源css_set迁移到目标css_set中。

2.  操控 cgroup属性

freezer子系统为例,通过freezer.state节点控制cgroup组的冻结与解冻,这个节点对应到freezer_write函数,实际的逻辑在freezer_change_state函数中。

freezer_change_state函数第一个参数属于freezer结构体,它通过内嵌的cgroup_subsys_state结构与cgroup关联,也就是说每个freezer结构体直接对应到freezer子系统的一个目录。接下来看函数逻辑,css_for_each_descendant_pre循环体里对当前目录以及每个子孙目录所代表cgroup实体执行freezer_apply_state函数。freezer_apply_state函数通过调用freeze_cgroup和unfreeze_cgroup函数实际操作cgroup内每个task的冻结与解冻。

四、小结

cgroup除了实现了freezer子系统,还实现了控制组IO调度的blkio cgroup、控制cpu核资源组分配的cpuset cgroup、控制cpu运行时间组分配的cpu cgroup、控制memory组分配的momory cgroup以及控制网络带宽组分配的cls_net cgroup,相关代码读者可自行研究。

参考资料:

1.https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/kernel/cgroup/cgroup.c?h=v5.4.143

2.https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/kernel/cgroup/legacy_freezer.c?h=v5.4.143

3.https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/kernel/cgroup/freezer.c?h=v5.4.143

长按关注

内核工匠微信

Linux 内核黑科技 | 技术文章 | 精选教程