Linux系统中经常有个需求就是希望能限制某个或者某些进程的所能使用的系统资源。比如:有分配好的特定比例的cpu时间,IO时间,可用内存大小等。于是就出现了cgroup的概念,cgroup就是control group,最初由google的工程师提出,后来被整合进Linux内核中。
Cgroup是Linux内核提供的一种可以限制、记录、隔离进程组所使用的物理资源的机制。它是将任意进程进行分组化管理的Linux内核功能。cgroup本身提供将进程进行分组化管理的功能和接口的基础结构。
Cgroups提供了以下功能:
1 限制进程组可以使用的资源数量。比如:memory子系统可以为进程组设定一个memory使用上限,一旦进程组使用的内存达到限额再申请内存,就会出发OOM。
2 进程组的优先级控制。比如:可以使用cpu子系统为某个进程组分配特定cpu share
3 记录进程组使用的资源数量。比如:可以使用cpuacct子系统记录某个进程组使用的cpu时间
4 进程组隔离。比如:使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间
5 进程组控制。比如:使用freezer子系统可以将进程组挂起和恢复。
所以就有了以下几个概念:
task:在cgroup中,task就是一个进程
control group:是一组按照某种标准划分的进程。Cgroups中的资源控制都是以control group为单位实现的。一个进程可以加入某个control group中,也从一个进程组迁移到另一个control group中。一个进程组的进程可以使用cgroups以control group为单位分配的资源,同时受到cgroups以control group为单位设定的限制。
hierarchy:control group可以形成树形的结构,有父节点,子节点,每个节点都是一个control group,子节点继承父节点的特定属性。
subsystem:一个subsystem就是一个资源控制器,subsystem必须附加到一个hierarchy上才能起作用,一个subsystem附加到某个hierarchy级以后,这个hierarchy上的所有control group都受到这个subsystem的控制。可以使用lssubsys -al来列出系统支持多少种subsystem。
注意:是先挂载subsystem,然后才有control group的。意思就是比如想限制某些进程的资源,那么,我会先挂载memory子系统,然后在memory子系统中创建一个cgroup节点,在这个节点中,将需要控制的进程id写入,并且将控制的属性写入。
cgroup的子系统:
1 blkio:这个子系统设置限制每个块设备的输入输出控制。例如:磁盘,光盘以及usb等等。
2 cpu:这个子系统使用调度程序为cgroup任务提供cpu的访问。
3 cpuacct:产生cgroup任务的cpu资源报告。
4 cpuset:如果是多核心的cpu,这个子系统会为cgroup任务分配单独的cpu和内存。
5 devices:允许或拒绝cgroup任务对设备的访问。
6 freezer:暂停或者恢复cgroup中的任务。
7 memory:设置每个cgroup中任务使用的内存限制,以及任务使用内存产生的内存资源报告。
8 net_cls:标记每个网络包,可以允许tc识别从cgroup中生成的数据包。
9 ns:名称空间子系统。
相互关系:
1 每次在系统中创建新层级时,该系统中的所有任务都是那个层级默认 cgroup的初始成员。
2 一个子系统最多只能附加到一个层级。
3 一个层级可以附加多个子系统。
4 一个任务可以是多个cgroup的成员,但是这些cgroup必须在不同的层级。
5 系统中的进程创建子进程时,该子任务自动成为其父进程所在 cgroup 的成员。然后可根据需要将该子任务移动到不同的 cgroup 中,但开始时它总是继承其父任务的cgroup。
cgroup的用户空间管理是通过cgroup文件系统实现的,比如要创建一个层级:
mount -t cgroup -o cpu,cpuset,memory cpu_and_mem /cgroup/cpu_and_mem
这个命令就创建一个名为cpu_and_mem的层级,这个层级上附加了cpu,cpuset,memory三个子系统,并把层级挂载到了/cgroup/cpu_and_mem.
接着创建一个cgroup:
cd /group/cpu_and_mem
mkdir foo
以上两个命令我们就在刚才的层级下面创建了一个叫foo的cgroup,进入foo目录用ls命令查看,就会发现一些文件,这些文件都是和cgroups相关子系统相关的文件,这些文件的值就是对相应cgroup的控制。在这些文件中有一个叫做tasks的文件,里面包含了属于这个cgroup的进程的进程号。初创建的cgroup里面tasks文件是空的,tasks文件不仅可读,还可写,你把一个进程号写入到某个cgroup目录的tasks文件里面,则这个进程就加入了相应的cgroup。
以上就是手动创建cgroup的过程,下面看看linux 发行版是怎样自动化这个过程的,以ubuntu12.04为例:
ubuntu12.04 安装cgroup
apt-get install cgroup-bin
cgroup的主要配置文件:
/etc/init/cgred.conf
/etc/init/libvirt-cgconfig-wait.conf
/etc/init/cgconfig.conf
/etc/default/cgred
/etc/default/cgconfig
/etc/cgrules.conf
/etc/cgconfig.conf
/etc/cgconfig.conf是cgroup的配置文件,用来定义cgroup组的定义,以及挂载点的配置。该文件主要由mount和group两个字段构成:
mount:
mount {
<controller> = <path>;
}
# controller:内核子系统的名称
# path:该子系统的挂载点
例如:cpu = /sys/fs//cgroup/cpu;就相当于执行如下命令:
mkdir /sys/fs/cgroup/cpu
mount -t cgroup -o cpu cpu /sys/fs/cgroup/cpu
group:
group <name> {
[<permissions>]
<controller> {
<param name> = <param value>;
…
}
…
}
# name: 指定cgroup的名称
# permissions:可选项,指定cgroup对应的挂载点文件系统的权限,root用户拥有所有权限。
# controller:子系统的名称
# param name 和 param value:子系统的属性及其属性值
例如:
mount {
cpu = /sys/fs/cgroups/cpu;
cpuacct = /sys/fs/cgroups/cpu;
}
group daemons/www { # 定义daemons/www(web服务器进程)组
perm {
task {
uid = root;
gid = webmaster;
}
admin {
uid = root;
gid = root;
}
}
cpu {
cpu.shares = 1000;
}
}
上面命令相当于执行了下面的命令:
mkdir /sys/fs/cgroups/cpu
mount -t cgroup -o cpu,cpuacct cpu /sys/fs/cgroups/cpu
mkdir /sys/fs/cgroups/cpu/daemons
mkdir /sys/fs/cgroups/cpu/daemons/www
chown root:root /sys/fs/cgroups/cpu/daemons/www/*
chown root:webmaster /sys/fs/cgroups/cpu/daemons/www/tasks
echo 1000 > /sys/fs/cgroups/cpu/daemons/www/cpu.shares