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_earlycgroup_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。