HACK#7 Cgroup、Namespace、Linux容器

本节将介绍Cgroup与Namespace以及通过这两个功能实现的容器功能。

Cgroup

Cgroup(control group)是将任意进程进行分组化管理的Linux内核功能。Cgroup本身是提供将进程进行分组化管理的功能和接口的基础结构,I/O或内存的分配控制等具体的资源管理功能是通过这个功能来实现的。这些具体的资源管理功能称为Cgroup子系统或控制器。

Cgroup子系统有控制内存的Memory控制器、控制进程调度的CPU控制器等。运行中的内核可以使用的Cgroup子系统由/proc/cgroup来确认。

Cgroup提供了一个cgroup虚拟文件系统,作为进行分组管理和各子系统设置的用户接口。要使用Cgroup,必须挂载cgroup文件系统。这时通过挂载选项指定使用哪个子系统。这里指定debug这个没有实质功能的调试用子系统来挂载。


mount-t cgroup-o debug cgroup/cgroup


注意事项:这里所说的“虚拟文件系统”,是指procfs和sysfs这种不具有物理设备的文件系统。并不是用来接纳文件系统差异的内核内部层layerVFS。

小贴士:关于cgroup文件系统的标准化挂载要点,是由开发论坛进行讨论的,但目前尚未得出结论。这里是挂载到/cgroup。

挂载后,在挂载位置下应该可以看到下列几个文件。这些是Cgroup呈现出来的特殊文件。


ls/cgroup

cgroup.event_control debug.current_css_set debug.taskcount

cgroup.procs debug.current_css_set_cg_links notify_on_release

debug.cgroup_css_links debug.current_css_set_refcount release_agent

debug.cgroup_refcount debug.releasable tasks


文件名前缀为cgroup的以及没有前缀的文件是由Cgroup的基础结构提供的特殊文件。而前缀为debug的文件是由debug子系统提供的特殊文件。Cgroup的子系统提供的特殊文件都会像这样加上子系统的前缀。因此,根据挂载时指定的选项,即所使用的子系统不同,存在的特殊文件也不同。但是Cgroup的基础结构所提供的特殊文件则是无论指定哪种子系统都一直存在的。特殊文件分为只读文件和可读写文件。只读文件是为用户提供信息的文件。可读写的特殊文件通过写入值来更改Cgroup以及Cgroup子系统设置的文件。设置的值可以通过读入特殊文件来确认。在这些特殊文件中,最重要的是tasks特殊文件。其内容可以显示如下。</p>


cat/cgroup/tasks

1

2

3

4

……


虽然看上去只是一些数字的排列,但其实这些是属于这个分组的线程的线程ID(TID)。在这时,系统上运行的所有线程的TID都包含在/cgroup/tasks中。这就表示全部线程都属于这个分组。那么这里出现的“分组”又是什么呢?分组,就是体现为cgroup文件系统目录的线程的集合。由于/cgroup也是目录,因此它也表示一个分组。像这样位于挂载点最上层的目录是自动生成的分组,称为根分组。在这个阶段,只有/cgroup(即根分组)是系统上存在的唯一分组。

小贴士:英语中将通过Cgroup创建的分组称做cgroup,容易与表示结构的“Cgroup”混淆,所以这里仅称为“分组”。

下面尝试创建一个分组,也就是在/cgroup下创建子目录。其内容如下所示。


mkdir/cgroup/test

ls/cgroup/test

cgroup.event_control debug.current_css_set debug.taskcount

cgroup.procs debug.current_css_set_cg_links notify_on_release

debug.cgroup_css_links debug.current_css_set_refcount tasks

debug.cgroup_refcount debug.releasable


虽然是新生成的目录,但是已经有文件存在。cgroup文件系统在目录生成的同时就会在其中配置特殊文件。

/cgroup/test也和/cgroup一样有tasks。其内容如下。


cat/cgroup/test/tasks


tasks的内容似乎是空的,这表示这个分组内一个线程也没有。可以将适当的线程添加到这个分组中。要将线程添加到分组中,可以在tasks中写入该线程的TID。这里以添加shell本身为例。


echo$$

2474

echo$$>/cgroup/test/tasks

cat/cgroup/test/tasks

2474

3821


tasks的内容中包含shell的TID(也是PID,即进程ID)—2474,可以看出这个shell已经属于test分组。除此以外,这个分组内还有另一个TID为3821的线程,这是什么呢?我们再来看一下tasks的内容。


cat/cgroup/test/tasks

2474

3822


结果居然发生了变化。事实上这个改变的部分,是显示了tasks内容的cat进程的TID。最初的cat(3821)和第二次的cat(3822)是不同的进程,TID也不同,所以结果发生了变化。但是似乎并没有将cat进程添加到test分组中。其实,属于分组的进程一旦生成子进程,其子进程就会自动属于母进程。由于cat是shell的子进程,因此前者自动属于test分组。大家应该还记得,在挂载cgroup文件系统后,系统上的所有线程是属于根分组的。也就是说,除了将明确指定为新生成分组内的进程为祖先进程以外,生成的进程都属于根分组。

这时,再显示/cgroup/tasks的内容的话,应该不会显示shell的TID(2474)。这是因为shell不属于根分组,而是属于test分组。然后,再将这个shell返回到根分组。


echo 2474>/cgroup/tasks


这样,shell的TID(2474)就再次属于/cgroup/tasks,而/cgroup/test/tasks就变空。如果分组中一个线程也没有,可以进行撤销。删除目录就可以撤销分组。


rmdir/cgroup/test


表2-1是每个子系统中Cgroup都会提供的特殊文件列表。

HACK#7 Cgroup、Namespace、Linux容器 - 图1

这里仅介绍了最基本的Cgroup使用方法,也就是分组的创建、撤销和将线程添加到分组的方法。实际使用Cgroup时,应在将线程添加到分组后,在分组内的特殊文件中设置值,来控制系统的运行。