全局数组cgroup_subsys[]在内核中应用很多,最常用的就是通过for_each_subsy()宏来遍历系统中的预定义的各个struct cgroup_subsys *ss:
可以看到宏 "for_each_subsys" 就是遍历全局数组cgroup_subsys[]; 但是这个cgroup_subsys[]数组的各个元素是什么呢?它又在哪里定义的呢?
说起来它还真的有点复杂,还是搞了那么些幺蛾子。它的定义在kernel/cgroup.c中:
#define for_each_subsys(ss, ssid) \
for ((ssid) = 0; (ssid) < CGROUP_SUBSYS_COUNT && \
(((ss) = cgroup_subsys[ssid]) || true); (ssid)++)
可以看到宏 "for_each_subsys" 就是遍历全局数组cgroup_subsys[]; 但是这个cgroup_subsys[]数组的各个元素是什么呢?它又在哪里定义的呢?
说起来它还真的有点复杂,还是搞了那么些幺蛾子。它的定义在kernel/cgroup.c中:
/* generate an array of cgroup subsystem pointers */
#define SUBSYS(_x) [_x ## _cgrp_id] = &_x ## _cgrp_subsys,
static struct cgroup_subsys *cgroup_subsys[] = {
#include <linux/cgroup_subsys.h>
};
#undef SUBSYS
咦,奇怪,这个cgroup_subsys[]数组里面居然是一个头文件。编译器在编译的时候会展开头文件<linux/cgroup_subsys.h>:
...
SUBSYS(cpuset)
SUBSYS(cpu)
SUBSYS(cpuacct)
SUBSYS(io)
SUBSYS(memory)
SUBSYS(devices)
SUBSYS(freezer)
SUBSYS(net_cls)
SUBSYS(perf_event)
SUBSYS(net_prio)
SUBSYS(hugetlb)
SUBSYS(pids)
SUBSYS(debug)
.....
为了更好的理解这个头文件内容,我去掉了一些边边角角,就得到上面的代码。即cgroup_subsys[]看起来有点像这样:
#define SUBSYS(_x) [_x ## _cgrp_id] = &_x ## _cgrp_subsys,
static struct cgroup_subsys *cgroup_subsys[] = {
SUBSYS(cpuset)
SUBSYS(cpu)
SUBSYS(cpuacct)
SUBSYS(io)
SUBSYS(memory)
SUBSYS(devices)
SUBSYS(freezer)
SUBSYS(net_cls)
SUBSYS(perf_event)
SUBSYS(net_prio)
SUBSYS(hugetlb)
SUBSYS(pids)
SUBSYS(debug)
};
#undef SUBSYS
我们还注意到"SUBSYS()" 在这里宏定义成了这个东西:
#define SUBSYS(_x) [_x ## _cgrp_id] = &_x ## _cgrp_subsys,
将这个“SUBSYS”宏展开代入到cgroup_subsys[]中,看起来就是这样:
static struct cgroup_subsys *cgroup_subsys[] = {
[cpuset_cgrp_id] = &cpuset_cgrp_subsys,
[cpu_cgrp_id] = &cpu_cgrp_subsys,
[cpuacct_cgrp_id] = &cpuacct_cgrp_subsys,
[io_cgrp_id] = &_cgrp_subsys,
[memory_cgrp_id] = &memory_cgrp_subsys,
[devices_cgrp_id] = &devices_cgrp_subsys,
[freezer_cgrp_id] = &freezer_cgrp_subsys,
[net_cls_cgrp_id] = &net_cls_cgrp_subsys,
[perf_event_cgrp_id] = &perf_event_cgrp_subsys,
[net_prio_cgrp_id] = &net_prio_cgrp_subsys,
[hugetlb_cgrp_id] = &_hugetlbcgrp_subsys,
[pids_cgrp_id] = &pids_cgrp_subsys,
[debug_cgrp_id] = &debug_cgrp_subsys,
};
God,我们很接近真面目了,cgroup_subsys[]数组中的各个元素可以猜到就是&cpuset_cgrp_subsys,&cpuset_cgrp_subsys,&cpuacct_cgrp_subsys,&io_cgrp_subsys......这些就是cgroup中各个子系统cgroup_subsys的全局变量结构。这些xxx_cgrp_subsys全局变量在内核编译好后就已经初始化ok、分配好空间。
我们拿cpu子系统struct cgroup_subsys cpu_cgrp_subsys这个结构来举例,它定义在kernel/sched/core.c文件中:
struct cgroup_subsys cpu_cgrp_subsys = {
.css_alloc = cpu_cgroup_css_alloc,
.css_released = cpu_cgroup_css_released,
.css_free = cpu_cgroup_css_free,
.fork = cpu_cgroup_fork,
.can_attach = cpu_cgroup_can_attach,
.attach = cpu_cgroup_attach,
.legacy_cftypes = cpu_files,
.early_init = 1,
};
好了,说完了各个元素,我们再来说说数组中元素的index:"_x## _cgrp_id"。
各个元素的索引"_x## _cgrp_id"也是通过SUBSY宏的把戏在include/linux/cgroup-defs.h中定义的:
/* define the enumeration of all cgroup subsystems */
#define SUBSYS(_x) _x ## _cgrp_id,
#define SUBSYS_TAG(_t) CGROUP_ ## _t, \
__unused_tag_ ## _t = CGROUP_ ## _t - 1,
enum cgroup_subsys_id {
#include <linux/cgroup_subsys.h>
CGROUP_SUBSYS_COUNT,
};
#undef SUBSYS_TAG
#undef SUBSYS
有了前面对cgroup_subsys[]定义的分析,这里应该是轻车熟路了。即各个子系统的索引id "_x## _cgrp_id" 是通过枚举类型enum cgroup_subsys_id {} + <linux/cgroup_subsys.h> + SUBSYS(_x)宏来定义的。
好了,这样以来,有了数组元素索引 和 数组元素,自然这个cgroup_subsys[]元素的定义也就终于真想大白了。
你,懂了么?
最后讲一点,为什么cgroup_subsys[]的定义要通过SUBSYS()宏 + "#include <linux/cgroup_subsys.h>" 的方式来定义呢?因为在内核中,有多个cgroup相关的结构都要用到子系统相关的名字的定义,如上面的xxx_cgrp_id以及xxx_cgrp_subsys等等。这样定义的好处就是提高了代码复用率,减少了重复代码。各种相关定义不用再写一长串,只需要短短几行就可以定义完成。