cgroup源码分析2——cgroup的初始化
Contents
本文详细分析了cgroup
初始化的过程。
本文基于
3.10.0-862.el7.x86_64
版本kernel进行分析。
在分析初始化之前,我们需要看一下层级和子系统对应的结构体以及几个重要的全局变量。
层级对应的结构体为:cgroupfs_root
,子系统对应的结构体为cgroup_subsys
.
cgroupfs_root 结构
|
|
sb
指向该层级关联的文件系统超级块subsys_mask
和actual_subsys_mask
分别指向将要附加到层级的子系统和现在实际附加到层级的子系统,在子系统附加到层级时使用。hierarchy_id
是该层级唯一的idtop_cgroup
指向该层级的根cgroupnumber_of_cgroups
记录该层级cgroup的个数root_list
是一个嵌入的list_head,用于将系统所有的层级连成链表subsys_list
是一个链表,该链表将附着于该挂载点上的子系统链接到一起
cgroup_subsys 结构
子系统对应的数据结构为:cgroup_subsys
|
|
cgroup_subsys
定义了一组操作,让各个子系统根据各自的需要去实现。这个相当于C++
中抽象基类,然后各个特定的子系统对应cgroup_subsys
则是实现了相应操作的子类。
类似的思想还被用在了cgroup_subsys_state
中,cgroup_subsys_state
并未定义控制信息,而只是定义了各个子系统都需要的共同信息,比如该cgroup_subsys_state
从属的cgroup
。然后各个子系统再根据各自的需要去定义自己的进程控制信息结构体,最后在各自的结构体中将cgroup_subsys_state
包含进去,这样通过Linux
内核的container_of
等宏就可以通过cgroup_subsys_state
来获取相应的结构体。
几个全局变量
init_css_set
是默认的css_set
。在还没有其他cgroup子系统
被mount
时,它被init
和其子进程
来使用。css_set_count
用来描述当前系统上有多少个css_set
。rootnode
是一个dummy hierarchy
,它只有一个cgroup
,所有的进程都属于这个cgroup
。dummytop
是一个指向rootnode.top_cgroup
的缩写。roots
是一个链表头,将所有的cgroupfs_root
都链接到了一起。root_count
表示有多少个cgroupfs_root
。init_css_set_link
: 用于链接init_css_set
和dummytop
的cg_cgroup_link
。struct cgroup_subsys *subsys[CGROUP_SUBSYS_COUNT]
数组,该数组保存了所有子系统(cgroup_subsys
)的信息
cgroups的初始化
在内核过程中,由于各个cgroup
子系统的特点,cgroup
的初始分为两部分:
cgroup_init_early
cgroup_init
cgroup_init_early
cgroup_init_early
用来初始化需要尽早初始化的子系统。一般这些需要尽早初始化的子系统都包括:cpuset
,cpu
,cpuacct
。
|
|
该函数的主要功能如下:
- 初始化
init_css_set
中的成员变量和几个全局变量css_set_count
、root_count
、rootnode
和init_css_set_link
。 - 初始化
cgroup
子系统cpuset
,cpu
,cpuacct
。
cgroup_init
cgroup_init
用来完成cgroup
的初始化,其代码如下:
|
|
主要完成了如下工作:
- 初始化了其他几个子系统
- 初始化
cgroup_backing_dev_info
- 根据
use_id
是否为true
,进行必要的初始化 - 将
init_css_set
这个目前唯一的css_set
添加到hash
表css_set_table
中 - 创建目录
/sys/fs/cgroup
- 注册
cgroup
文件系统类型 - 创建
/proc/cgroups
cgroup_init_subsys
以上两个方法中都调用了cgroup_init_subsys
,其代码如下:
|
|
- 初始化
cgroup_subsys
的cftsets
- 分配
css
- 初始化
dummytop
和init_css_set
中对应的subsys
数组 - 调用
online_css
初始化后mount前这些数据结构的关系图
我所使用的系统为centos7,默认cgroup各个子系统的挂载是systemd完成的,由于没有办法让systemd不进行挂载,所以在系统启动之后,我们手动umount掉这些cgroup子系统的挂载,用来分析内核里的数据结构。操作方法如下:
|
|
可以看出,除了/sys/fs/cgroup/systemd不能umount外,其他子系统都umount成功了。此时/proc/cgroups
的输出如下:
|
|
可以看出,每个子系统的hierarchy id为0,且只有一个cgroup,即dummytop这个cgroup。
此时,我们就可以通过crash来分析这些数据结构的关系:
- 由于系统上挂载了systemd这个cgroup,再加上rootnode这个dummy cgrouproot_fs,总共有两个cgrouproot_fs,所以root_count=2,而只有systemd对应的cgroupfs_root被链接到了roots这个链表上。
|
|
- rootnode的subsys_list应该包含了那13个未挂载的cgroup_subsys
|
|
- 系统上这些cgroup对应的cgroup_subsys的成员root都执行了rootnode:
|
|
- 这个时刻,系统上只有一个css_set,即
init_css_set
, 所有的进程的css_set都执向它:
|
|
- dummytop和init_css_set的成员subsys执行的css都相同:
|
|
所以,cgroup_init_early
和cgroup_init
执行完后,这些数据结构之间的关系如下图所示:
/proc/cgroups 实现分析
在cgroup
初始化函数cgroup_init
中会调用如下函数进行注册/proc/cgroups
接口:
|
|
proc_cgroupstats_operations
的实现如下:
|
|
从上可以看出,这些信息都来自于数组subsys
和其成员subsys->root
(类型为cgroupfs_root
)。/proc/cgroups
示例如下:
|
|
从上可以看出,
- 第一列是
cgroup
子系统的名称 - 第二列的
hierarchy
从2
开始,那么编号为1
的hierarchy
是什么呢?hierarchy_id
是动态分配,linux
系统启动时,先挂载了一个未附加任何子系统的层级systemd
,所以systemd
的hierarchy_id
为1 - 第三列说明该子系统中
cgroups
的个数 - 第四列说明该子系统是否使能。
内核中数组subsys
保存了系统上的不同的cgroup
子系统信息。
|
|
其中CGROUP_SUBSYS_COUNT
的值为系统上支持的cgroup
的个数,包括编译进内核的和编译成模块的。
注意,由于
IS_SUBSYS_ENABLED
的定义,这里只会初始化编译进内核模块的子cgroup。
使用crash
工具,可以查看内核中cgroup
子系统的情况:
|
|
在内核中,每个cgroup
子系统,都会有一个subsys
的定义的结构体。对于pids
子系统,其对应的subsys
定义为:
|
|
crash 查看cgroup的一些数据结构的关系
NOTE: 这里是centos 7启动后,默认情况下,各个cgroup子系统都已经被systemd挂载的情况。
|
|
从上可以看出,rootnode
的成员allcg_list
将属于该cgroupfs_root
的所有的cgroup
的链接到了一起,一般情况下,系统上属于rootnode
的cgroup
只有一个,即dummy_top
,其名称为/
。
当系统mount
很多cgroup
层级后,全局变量roots
作为链表头,将系统上所有的层级都链接到了一起。
|
|
可以看出,roots
这个链表将系统上所有的cgroupfs_root
链接到了一起,注意,这个链表中不包括rootnode
这个cgroupfs_root
,因为这个rootnode
的hierarchy_id
为0
,这个链表中没有hierarchy_id
为0
的结点。
|
|
关于dummytop
:
|
|
dummytop
是一个特殊的cgroup
,其没有兄弟和孩子cgroup
,其subsys
包含了所有的控制子系统,即rootnode.top_cgroup.subsys
数组中每个成员都不为null
。这些css
是在cgroup_init_subsys
函数中创建的。初始化时这些css
的cgroup
成员都指向了dummytop
,在后续mount
各个cgroup
子系统时会进行调整。即mount
时会创建新的cgroupfs_root
, 并将对应的subsys
跟新创建的cgroupfs_root
建立对应的关系,当umount
或者remount
时,需要删除的子系统,将会移动到rootnode
这个cgroupfs_root
中。
cgroup subsys中的use_id
只有memory cgroup
中的use_id
为true
。