天天看点

初探Cgroups(控​​​制​​​组​​​群)

cgroups,其名称源自控制组群(control groups)的简写,是linux内核的一个功能,用来限制,控制与分离一个进程组群的资源(如cpu、内存、磁盘输入输出等)。最初由google的工程师提出,现在已经整合进linux内核。

cgroup的控制,统计,都是以整个cgroup来计算,即cgroup里面进程的资源使用总合。

资源限制:组可以被设置不超过设定的内存限制;这也包括虚拟内存。

优先化:一些组可能会得到大量的cpu或磁盘输入输出通量。

报告:用来衡量系统确实把多少资源用到适合的目的上。

分离:为组分离命名空间,这样一个组不会看到另一个组的进程、网络连接和文件。

控制:冻结组或检查点和重启动。

在cgroups中,所有和cgroups的交互都是通过cgroup的文件系统。cgroup通过挂载层级来建立和用户层交互的通道。

先来尝试一下挂载。

<code>1</code>

<code>#mount -t cgroup -o 子系统 层级名 挂载点</code>

<code>2</code>

<code>#比如</code>

<code>3</code>

<code>mount</code> <code>-t cgroup -o cpu,cpuset,memory cpu_and_mem </code><code>/cgroup/cpu_and_mem</code>

<code>4</code>

<code>#这条指令把cpu,cpuset,memory附加到cpu_and_mem层级,然后挂载到 /cgroup/cpu_and_mem</code>

<code>5</code>

<code>#注意要root权限执行。</code>

<code>6</code>

<code>#执行指令后,我们会发现/cgroup/cpu_and_mem多出了很多奇怪的文件。</code>

这些文件后面再说,先介绍概念。

下面在介绍概念的同时,介绍这些概念在cgroup文件系统中的表现。

初探Cgroups(控​​​制​​​组​​​群)

说明:在cgroups中,任务就是系统的一个进程。

表现:在文件系统中,有一个名为tasks文件,里保存着进程的pid。所有移动进程的操作都是通过写入pid到相应的tasks文件完成。

说明:控制族群可以组织成hierarchical的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性。

表现:可以看成挂载到挂载点的块。(为什么不说是挂载点呢?因为事实上卸载掉cgroup的文件系统后,cgroup层级依然存在。这是很多cgroup错误的原因。)

说明:一个子系统就是一个资源控制器,比如cpu子系统就是控制cpu时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。事实上子系统比cgroup更早出现。

表现:子系统是附加到层级上的,挂载上去之后出现相应的控制文件。

说明:控制族群就是一组按照某种标准划分的进程。cgroups中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用cgroups以控制族群为单位分配的资源,同时受到cgroups以控制族群为单位设定的限制。

表现:挂载点就可以认为是一个特殊的cgroup,称之为root cgroup,在里面创建文件夹就对应创建一个cgroup。

解释:mount的-o后面跟cpu,cpuset,memory等多个子系统。

注意:挂载了cpu之后,再往挂载点挂载memory不是附加多个子系统(区别层级和挂载点),这是挂载的覆盖。

解释:当我们把cpu,cpuset附加到一个层级a,挂载后。我们不能创建cpu,memory或者是cpu或者cpu,cpuset,memory这样的层级。

注意:有人会发现,可以再mount一个cpu,cpuse到别的目录下。因为这没有创建新的层级,只是把层级再挂载到另一个目录。可以发现,两个挂载点中的文件是一样的。

解释:准确的说是任务必须在不同的子系统。这是必然的的,因为每个子系统的实现都是在内核中,即使没有挂载cgroup层级,它们的数据结构都存在(子系统就好像进程的各个资源维度),所以每个进程就已经和各个子系统挂钩了。层级可以认为是子系统的展现。

反过来理解,是子系统绑定到进程,而不是进程绑定到层级。cgroup起到的是对进程分组的作用。

解释:系统中的进程(任务)创建子进程(任务)时,该子任务自动成为其父进程所在 cgroup 的成员,它总是继承其父任务的cgroup。可以认为是fork时,把子系统的控制结构也拷贝了一份。

每次在系统中创建新层级时,该系统中的所有任务都是那个层级的默认 cgroup(我们称之为 root cgroup ,此cgroup在创建层级时自动创建,后面在该层级中创建的cgroup都是此cgroup的后代)的初始成员。此时它的tasks中有所有的进程pid。

可以认为这是一个默认分组,root cgroup的设置就是默认配置,里面的子cgroup是自定义的分组,里面的配置在创建时继承了父cgroup的设置。新建的子cgroup中tasks为空,直到你移入进程。

注意:cgroup的继承指的是配置的继承,在创建新的子cgroup时,所有配置都和父cgroup相同。

我踩过的坑:我一直以为cgroup有树状的限制,比如cgorup a限制了内存最大100m,作为a的子cgroup b,限制80m,就认为,cgroup b的80m是100m的一部分。a下的进程最少用20m的内存。事实上,这个层级关系是有参数控制的。进程从root cgroup移入自己的子cgroup时,这个进程的统计数据就不累计到root cgroup了。

一个例外:memory子系统有一个memory.use_hierarchy,这是个布尔开关,默认为 0。此时不同层次间的资源限制和使用值都是独立的。当设为 1 时,子控制组进程的内存占用也会计入父控制组,并上溯到所有 memory.use_hierarchy = 1 的祖先控制组。目前只有memory子系统有此功能。

可以认为,对于进程来说每个cgroup都是处于同一个层次,包括root cgroup,不一样的只是cgroup的配置参数和划分在这个cgroup的进程。