本文转载于:http://edsionte.com/techblog/archives/4314
Cgroup(Control Groups)是这样一种机制:它以分组的形式对进程使用系统资源的行为进行管理和控制。也就是说,用户通过cgroup对所有进程进行分组,再对该分组整体进行资源的分配和控制。
cgroup中的每个分组称为进程组,它包含多个进程。最初情况下,系统内的所有进程形成一个进程组(根进程组),根据系统对资源的需求,这个根进程组将被进一步细分为子进程组,子进程组内的进程是根进程组内进程的子集。而这些子进程组很有可能继续被进一步细分,最终,系统内所有的进程组形成一颗具有层次等级(hierarchy)关系的进程组树。如下图:
由于进程组可以被进一步划分,因此一个进程可能处于多个进程组中,但这些进程组必然不处于同一层级中。
另外,如果某个进程组内的进程创建了子进程,那么该子进程默认与父进程处于同一进程组中。也就是说,cgroup对改进程组的资源控制同样作用于子进程。
cgroup是一种对进程资源管理和控制的统一框架,它提供的是一种机制(mechanism),而具体的策略(policy)是通过子系统(subsystem)来完成的,子系统是cgroup对进程组进行资源控制的具体行为。机制和策略是Linux操作系统中一种经典的设计思想,所谓机制就是“我要提供哪种功能”,而策略则是“我要怎样来实现这种功能”。
cgroup中每个子系统都代表一种类型的资源,具体如下:
1) cpu子系统:该子系统为每个进程组设置一个使用CPU的权重值,以此来管理进程对cpu的访问。
2) cpuset子系统:对于多核cpu,该子系统可以设置进程组只能在指定的核上运行,并且还可以设置进程组在指定的内存节点上申请内存。
3) cpuacct子系统:该子系统只用于生成当前进程组内的进程对cpu的使用报告。
4) memory子系统:该子系统提供了以页面为单位对内存的访问,比如对进程组设置内存使用上限等,同时可以生成内存资源报告
5) blkio子系统:该子系统用于限制每个块设备的输入输出。首先,与CPU子系统类似,该系统通过为每个进程组设置权重来控制块设备对其的I/O时间;其次,该子系统也可以限制进程组的I/O带宽以及IOPS。
6) devices子系统:通过该子系统可以限制进程组对设备的访问,即该允许或禁止进程组对某设备的访问。
7) freezer子系统:该子系统可以使得进程组中的所有进程挂起。
8) net-cls子系统:该子系统提供对网络带宽的访问限制,比如对发送带宽和接收带宽进程限制。
如果要实现子系统对所属进程组的资源控制,那么就要实现该子系统对应的钩子函数。这个关系与虚拟文件系统类似,VFS提供统一的用户接口,而具体的文件操作则通过文件系统(比如ext3)对钩子函数的实现。具体关系如下图:
由图可以看出,cgroup在用户态提供统一的用户接口,而每个子系统对资源的控制功能则通过其钩子函数实现。这样使得cgroup在上层是一个统一的框架,而下层则可以实现多种资源的控制。每个子系统的钩子函数如下:
1 |
struct cgroup_subsys { |
2 |
struct cgroup_subsys_state *(*css_alloc)( struct cgroup *cgrp); |
3 |
int (*css_online)( struct cgroup *cgrp); |
4 |
void (*css_offline)( struct cgroup *cgrp); |
5 |
void (*css_free)( struct cgroup *cgrp); |
6 |
7 |
int (*can_attach)( struct cgroup *cgrp, struct cgroup_taskset *tset); |
8 |
void (*cancel_attach)( struct cgroup *cgrp, struct cgroup_taskset *tset); |
9 |
void (*attach)( struct cgroup *cgrp, struct cgroup_taskset *tset); |
10 |
void (*fork)( struct task_struct *task); |
11 |
void (* exit )( struct cgroup *cgrp, struct cgroup *old_cgrp, |
12 |
struct task_struct *task); |
13 |
void (*bind)( struct cgroup *root); |
14 |
…… …… |
15 |
} |
cgroup在Linux内核中是以文件系统的形式存在的,不过cgroup对应的这种文件系统与proc文件系统类似,都是只存在于内存中的“虚拟”文件系统。既然如此,就可以通过mount命令创建一个cgroup实例。
1 |
$ sudo mount -t cgroup -o memory memory_cgroup /dev/cgroup/ |
即在/dev/cgroup/下创建了一个memory子系统。接下来就可以通过:
1 |
$ cat /proc/filesystems | grep cgroup |
可看到系统有了cgroup类型的文件系统。
当创建了一个cgroup实例后,对应的挂载点下会有一些文件,这些文件是用户与cgroup进行交互的接口。由于cgroup位于VFS层之下,因此用户可以通过统一的文件操作接口去读取或设置子系统的参数,当然也可以直接使用echo或者cat等命令。