提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、主要功能
- 二、基本概念
- 三、cgroups子系统介绍
- 四、cgroups 层级结构
- 五、数据结构
前言
Linux CGroup全称Linux Control Group, 是Linux内核的一个功能,用来限制,控制与分离一个进程组群的资源(如CPU、内存、磁盘输入输出等)。
一、主要功能
Linux CGroupCgroup 可让您为系统中所运行任务(进程)的用户定义组群分配资源 — 比如 CPU 时间、系统内存、网络带宽或者这些资源的组合。您可以监控您配置的 cgroup,拒绝 cgroup 访问某些资源,甚至在运行的系统中动态配置您的 cgroup。
cgroups的一个设计目标是为不同的应用情况提供统一的接口,从控制单一进程到操作系统层虚拟化
具体功能有:
- 限制进程组可以使用的资源数量(Resource limiting )。比如:memory子系统可以为进程组设定一个memory使用上限,一旦进程组使用的内存达到限额再申请内存,就会出发OOM(out of memory)。
- 进程组的优先级控制(Prioritization )。比如:可以使用cpu子系统为某个进程组分配特定cpu share。
- 记录进程组使用的资源数量(Accounting )。比如:可以使用cpuacct子系统记录某个进程组使用的cpu时间
- 进程组隔离(Isolation)。比如:使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。
- 进程组控制(Control)。比如:使用freezer子系统可以将进程组挂起和恢复。
二、基本概念
Cgroups主要由task,cgroup,subsystem及hierarchy构成:
task:在Cgroups中,task就是系统的一个进程。
cgroup:Cgroups中的资源控制都以cgroup为单位实现的。cgroup表示按照某种资源控制标准划分而成的任务组,包含一个或多个子系统。一个任务可以加入某个cgroup,也可以从某个cgroup迁移到另外一个cgroup。
subsystem:Cgroups中的subsystem就是一个资源调度控制器(Resource Controller)。比如CPU子系统可以控制CPU时间分配,内存子系统可以限制cgroup内存使用量。
hierarchy:hierarchy由一系列cgroup以一个树状结构排列而成,每个hierarchy通过绑定对应的subsystem进行资源调度。hierarchy中的cgroup节点可以包含零或多个子节点,子节点继承父节点的属性。整个系统可以有多个hierarchy。
三、cgroups子系统介绍
- cpu 子系统,主要限制进程的 cpu 使用率。
- cpuacct 子系统,可以统计 cgroups 中的进程的 cpu 使用报告。
- cpuset 子系统,可以为 cgroups 中的进程分配单独的 cpu 节点或者内存节点。
- memory 子系统,可以限制进程的 memory 使用量。
- blkio 子系统,可以限制进程的块设备 io。
- devices 子系统,可以控制进程能够访问某些设备。
- net_cls 子系统,可以标记 cgroups 中进程的网络数据包,然后可以使用 tc 模块(traffic control)对数据包进行控制。
- net_prio — 这个子系统用来设计网络流量的优先级
- freezer 子系统,可以挂起或者恢复 cgroups 中的进程。
- ns 子系统,可以使不同 cgroups 下面的进程使用不同的 namespace
- hugetlb — 这个子系统主要针对于HugeTLB系统进行限制,这是一个大页文件系统。
四、cgroups 层级结构
内核使用 cgroup 结构体来表示一个 control group 对某一个或者某几个 cgroups 子系统的资源限制。cgroup 结构体可以组织成一颗树的形式,每一棵cgroup 结构体组成的树称之为一个 cgroups 层级结构。
- cgroups层级结构可以 attach 一个或者几个 cgroups 子系统,当前层级结构可以对其 attach 的 cgroups 子系统进行资源的限制。每一个 cgroups 子系统只能被 attach 到一个层级结构中。
- 创建了 cgroups 层级结构中的节点(cgroup 结构体)之后,可以把进程加入到某一个节点的控制任务列表中,一个节点的控制列表中的所有进程都会受到当前节点的资源限制。同时某一个进程也可以被加入到不同的 cgroups 层级结构的节点中,因为不同的 cgroups 层级结构可以负责不同的系统资源。所以说进程和 cgroup 结构体是一个多对多的关系。
上面这个图从整体结构上描述了进程与 cgroups 之间的关系。最下面的P代表一个进程。每一个进程的描述符中有一个指针指向了一个辅助数据结构css_set(cgroups subsystem set)。 指向某一个css_set的进程会被加入到当前css_set的进程链表中。一个进程只能隶属于一个css_set,一个css_set可以包含多个进程,隶属于同一css_set的进程受到同一个css_set所关联的资源限制。
上图中的”M×N Linkage”说明的是css_set通过辅助数据结构可以与 cgroups 节点进行多对多的关联。但是 cgroups 的实现不允许css_set同时关联同一个cgroups层级结构下多个节点。 这是因为 cgroups 对同一种资源不允许有多个限制配置。
一个css_set关联多个 cgroups 层级结构的节点时,表明需要对当前css_set下的进程进行多种资源的控制。而一个 cgroups 节点关联多个css_set时,表明多个css_set下的进程列表受到同一份资源的相同限制。
3、一个task不能存在于同一个hierarchy的不同cgroup,但可以存在在不同hierarchy中的多个cgroup
系统每次新建一个hierarchy时,该系统上的所有task默认构成了这个新建的hierarchy的初始化cgroup,这个cgroup也称为root cgroup。
对于你创建的每个hierarchy,task只能存在于其中一个cgroup中,即一个task不能存在于同一个hierarchy的不同cgroup中,但是一个task可以存在在不同hierarchy中的多个cgroup中。
如果操作时把一个task添加到同一个hierarchy中的另一个cgroup中,则会从第一个cgroup中移除。
4、子task继承父task cgroup的关系
系统中的任何一个task(Linux中的进程)fork自己创建一个子task(子进程)时,子task会自动的继承父task cgroup的关系,在同一个cgroup中,但是子task可以根据需要移到其它不同的cgroup中。父子task之间是相互独立不依赖的。
五、数据结构
进程的task_struct字段中就有指向某个css_set的指针。其中cgroups指针指向了一个css_set结构。cg_list是一个list_head结构,用于将连到同一个css_set的进程组织成一个链表。
struct task_struct {...
struct css_set __rcu *cgroups;struct list_head cg_list;
...
}
css_set存储了与进程相关的cgroups信息
struct css_set {atomic_t refcount;struct hlist_node hlist;struct list_head tasks;struct list_head cg_links;struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];struct rcu_head rcu_head;};
其中refcount是该css_set的引用数,因为一个css_set可以被多个进程共用,只要这些进程的cgroups信息相同,比如:在所有已创建的层级里面都在同一个cgroup里的进程。
hlist用于把所有css_set组织成一个hash表,这样内核可以快速查找特定的css_set。
tasks指向所有连到此css_set的进程连成的链表。
cg_links指向一个由struct cg_cgroup_link连成的链表。
subsys是一个指针数组,存储一组指向cgroup_subsys_state的指针。通过这个指针数组,进程就可以获得相应的cgroups控制信息了。
cgroup_subsys_state存放进程与一个特定子系统相关的信息。
struct cgroup_subsys_state {struct cgroup *cgroup;atomic_t refcnt;unsigned long flags;struct css_id *id;};
cgroup指针指向了一个cgroup结构,也就是进程属于的cgroup。进程受到子系统的控制,实际上是通过加入到特定的cgroup实现的,因为cgroup在特定的层级上,而子系统又是附加到曾经上的。通过以上三个结构,进程就可以和cgroup连接起来了:task_struct->css_set->cgroup_subsys_state->cgroup。
cgroup存放子系统的相关信息
struct cgroup {unsigned long flags; atomic_t count;struct list_head sibling; struct list_head children; struct cgroup *parent; struct dentry *dentry; struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];struct cgroupfs_root *root;struct cgroup *top_cgroup;struct list_head css_sets;struct list_head release_list;struct list_head pidlists;struct mutex pidlist_mutex;struct rcu_head rcu_head;struct list_head event_list;spinlock_t event_list_lock;
};
sibling,children和parent三个list_head负责将同一层级的cgroup连接成一颗cgroup树。
subsys是一个指针数组,存储一组指向cgroup_subsys_state的指针。这组指针指向了此cgroup跟各个子系统相关的信息,这个跟css_set中的道理是一样的。
root指向了一个cgroupfs_root的结构,就是cgroup所在的层级对应的结构体。
top_cgroup指向了所在层级的根cgroup,也就是创建层级时自动创建的那个cgroup。
css_set指向一个由struct cg_cgroup_link连成的链表,跟css_set中cg_links一样。
css_set和croup之间的关系如何设计,多对多的关系必须添加一个中间结构来将两者联系起来。
cg_cgroup_link就是这个中间结构
struct cg_cgroup_link {struct list_head cgrp_link_list;struct cgroup *cgrp;struct list_head cg_link_list;struct css_set *cg;};
cgrp_link_list连入到cgroup->css_set指向的链表,cgrp则指向此cg_cgroup_link相关的cgroup。
Cg_link_list则连入到css_set->cg_links指向的链表,cg则指向此cg_cgroup_link相关的css_set。