CGroups的全称叫做 control groups,是由Google首创,并被合入Linux内核的一种资源限制机制。使用CGroups可以将一系列任务极其子任务整个到资源划分等级不同的组内,从而管理系统资源。通俗讲,CGroups可以限制和记录任务组所使用的物理资源(包括CPU、Memory、IO等)。
CGroups对外以伪文件系统的方式提供API,用户态程序可以操作对应的文件以达到对CGroups的控制,其对于操作的单元可以细粒度到线程级别。需要注意的是,当父任务创建完子任务后,子任务依然处于父任务的CGroups组中。
CGroups主要提供了以下四大功能:
CGroups提供了非常强大的资源控制能力,当然,其相关概念也比较多,本节主要介绍CGroups涉及的概念,以及基本含义。本节对后边的内容相当重要,一定要熟悉相关概念。
表示CGroups中的一个任务,该任务可以是线程,也可以是进程。
CGroups中对于某种资源的调度控制器,例如CPU子系统,控制CPU时间片的分配;mem子系统,控制cgroup对于内存的使用量等。在Linux中,子系统主要有以下9种组成(不同内核版本看到的种类数可能不同):
在CGroups中,所有的控制都是以控制组为单位实现的,表示将某种或某些资源按照某种控制标准进行划分而成的任务组,包含一个或多个subsystem。一个task可以加入某个cgroup,也可以从某个cgroup迁移到另一个cgroup。
由一系列cgroup以树状结构排列而成,每个层级通过绑定对应的子系统进行资源控制。层级中的cgroup节点可以包含零个或多个自己诶点,子节点继承父节点挂载的子系统。
cgroups的主要由以下四大规则进行组织
同一层级可以附加一个或多个子系统,例如一个层级中,可以附加cpu子系统,也可以附加mem子系统。
当且仅当目标层级只有一个子系统时,一个子系统可以附加到多个层级上。例如:层级A上拥有CPU子系统,层级B上拥有mem子系统时,CPU子系统无法再添加到层级B上;如果层级B上没有mem子系统时,CPU子系统可以附加于层级B上。
系统新建一个层级时,该系统上所有任务默认加入该新层级的初始化cgroup中,这个cgroup被称为root cgroup。对于创建的每个层级,任务只能存在于其中一个cgroup中,即一个任务不能存在于同一个层级的不同cgroup中,但一个任务可以存在于不同层级中的多个cgroup中。
任务在fork、clone自身时创建的子任务默认与父任务在同一个cgroup中,但子任务允许被移动到不同cgroup中。
本节利用限制cpu使用率的例子,理解CGroups的简单使用。本节主要使用Linux命令行对文件进行操作,在程序中,我们可以使用程序提供的文件操作函数来对CGroup进行操作。
通过 mount | grep cgroup 命令查看cgroup的挂载
第一行显示的是cgroup的文件系统类型为tmpfs(内存中的临时文件系统);第二行表示systemd对cgroup的支持,systemd可以通过相关命令来控制进程;其余各行就是cgroups对应的子系统挂载目录。
通过 mount -t cgroup 命令挂载cgroup后,即可看到对应的子系统挂载目录
在操作限制CPU的实验之前,我们先编写一个消耗CPU高的程序,用来实验。
#include
int main(int argc, char *argv[]) {
while(1){
for(int i = 0 ;i<100;i++);
}
return EXIT_SUCCESS;
}
编译运行上述程序,并使用top命令查看其CPU占比
从图中可以看到,不一会CPU占用就飙升到了100%。我们先看下对应进程所属的cgroup(输入 cat /proc/[pid]/cgroup即可)
我们看到,对应的cgroup都是 / 。此时,我们进入cpu子系统的cgroup目录,创建新的cgroup名为cg1
进入cg1 cgroup后,我们ls后看到有很多文件,这里重点介绍两个我们限制cpu使用时用到的文件,其他文件大家有兴趣可以自行查阅相关资料。
对于cpu.cfs_period_us和cpu.cfs_quota_us之间的关系再多做一点解释:
cpu.cfs_quota_us / cpu.cfs_period_us = 需要的CPU核数
例如:
通过上边的理论支撑,我们如果要将上述程序的CPU使用率限制在50%,则需要分三步操作:
执行完上述操作后,我们可以看到,CPU的使用率已经被限制在了50%左右。
在日常的使用中,除了上述 cpu.cfs_quota_us 和 cpu.cfs_period_us 外,还有一个非常重要的文件是 cpu.shares,他是用来设置cpu子系统对于不同cgroup之间的cpu分配比例
例如,A cgroup 设置需要 4 个 CPU (cpu.cfs_quota_us/cpu.cfs_period_us = 4 ),B cgroup 也设置需要4个CPU,假设当前宿主机有16核CPU,则A和B都可以跑满4个CPU,即当CPU资源充足时,各自都可跑满设置的CPU核数。但是在CPU资源紧张的情况下,这个时候需要通过 cpu.shares 设置的比例来判断CPU分配的个数。例如还是上边的例子,假设此时宿主机只有4个CPU,A的cpu.shares为1024,B的cpu.shares 为 3072,他俩的比值为1:3,这个时候如果A满负荷运行,B不占用CPU或休眠,则A依然可以获得4个CPU。但是,如果此时A和B都需要满负荷运行,则A只能获得1个CPU,B可以获得3个CPU。
cpu.shares在生产环境最广泛的应用就是用来解决宿主机CPU资源紧张时优先保障核心业务或在线业务,主动降级非核心业务或离线业务。例如:我们可以将核心业务的cpu.shares设置为10240,将离线业务的cpu.shares设置为1024,则在资源紧张情况下,可以最大程度保障核心服务拿到CPU时间片的概率。
CGroups是内核提供的一种资源限制机制,对用户态程序以伪文件系统的方式提供操作API,虽然概念繁多,但是我们通过对于cpu限制的实操,简单了解了CGroups的使用方法,也算是入门了CGroups这门技术。