概述
cgroup 全名是 control groups,在 linux 上负责对进程的一系列资源进行管控。比如 CPU,Memory,Huge Pages 等。cgroup 下通过子系统(subsystem)来划分模块,每种资源都通过一个子系统来实现。
cgroup 通过文件系统的方式对外提供调用,并可以用层级的方式进行组合。这种层级通过文件系统目录的方式进行呈现。比如在 cgroup cpu 目录下创建子目录,就相当于在根 cpu cgroup 下创建了一个子 cgroup。并且子 cgroup 会继承父 cgroup 的限制。
cgroup 目前有两个版本:v1 和 v2,并且两个版本的设计差异较大。但是理念类似,因此即使版本不同,也可以一样来理解。下面会以 cgroup v1 cpu 子系统进行讲解。
cpu 子系统的使用
cgroup 描述起来一直是一个比较抽象的概念。下面用一个简单的例子来帮助认识 cgroup 是如何工作的。
首先在机器上启动一个 stress 进程,分配一个 cpu,然后查看该进程 cpu 占用情况:
$ stress -c 1
$ pidstat -p 480164 1
Linux 4.14.81.bm.26-amd64 (n251-254-159) 06/01/2021 _x86_64_ (8 CPU)
02:36:56 PM UID PID %usr %system %guest %CPU CPU Command
02:36:57 PM 1001 480164 100.00 0.00 0.00 100.00 6 stress
可以看到,stress 进程已经占用了 1 cpu。现在我们创建一个名叫 stress 的 cgroup 来限制 cpu:
$ cd /sys/fs/cgroup/cpu
$ mkdir stress && cd stress
# 将 pid 写入到 cgroup.procs 中,就等同于将这个进程移到该 cgroup 中
$ echo 480164 > cgroup.procs
$ echo 100000 > cpu.cfs_period_us
$ echo 50000 > cpu.cfs_quota_us
# 再看看当前的 CPU 占用
$ pidstat -p 480164 1
Linux 4.14.81.bm.26-amd64 (n251-254-159) 06/04/2021 _x86_64_ (8 CPU)
05:17:49 AM UID PID %usr %system %guest %CPU CPU Command
05:17:50 AM 1001 480164 50.00 0.00 0.00 50.00 6 stress
上述操作通过配置 cpu.cfs_period_us
和 cpu.cfs_quota_us
参数达到了限制进程使用 CPU 的目的。
cgroup 还提供了一个 cpu.shares
参数,当 CPU 资源繁忙时,这个参数可以配置进程使用 CPU 的权重。下面我们在 cpu 为 1 的虚拟机演示。 在 cgroup 下创建两个子 cgroup 来展示这个参数的效果。
$ cd /sys/fs/cgroup/cpu,cpuacct
$ mkdir stress1 && cd stress1
$ stress -c 1
$ echo 3475127 > cgroup.procs
$ echo 1024 > cpu.shares
此时 PID 3475127 的 stress 进程 CPU 占用率接近 100%。在新的终端中执行以下命令:
$ mkdir stress2 && cd stress2
$ stress -c 1
$ echo 3479833 > cgroup.procs
此时两个 stress 进程的 CPU 占用大致相等,接近 50%。因为 stress2 cgroup 中没有设置 cpu.shares,所以取默认值为 1024。现在设置 stress2 cgroup 的 cpu.shares 参数:
$ echo 512 > cpu.shares
# 使用 top 查看
PID USER PR NI VIRT RES SHR S %CPU %MEM
3475127 root 20 0 7948 96 0 R 65.1 0.0
3479833 root 20 0 7948 92 0 R 32.2 0.0
stress1 中的进程 CPU 占用率大概是 stress2 中的两倍。这是因为 stress1 中 cpu.shares 的值是 stress2 中的两倍。当然上述情况必须在 CPU 资源不够时,cpu.shares 才会起作用。如果这是一个 2 cpu 的虚拟机,那么 stress1 和 stress2 都会占用 100%。
参数说明
上述出现了一些 cpu 的参数,这里统一解释一下:
- cpu.cfs_period_us: 重新分配 CPU 资源的时间周期长度,单位是 us。cfs 是 linux 进程调度器的一种,全称为完全公平调度器。因此这个参数只针对使用 cfs 调度的进程。
-
cpu.cfs_quota_us: 进程在设置的时间周期长度内,可以使用的 CPU 时间上限。结合 cpu.cfs_period_us 就可以限制一个进程可以使用的总 CPU 时间了。计算方式为
(cpu.cfs_quota_us / cpu.cfs_period_us)*count(cpu)
。这个参数只针对使用 cfs 调度的进程。 -
cpu.shares: 这个参数只有在 CPU 资源忙时才生效,它可以用来设置进程使用的 CPU 权重。上面的例子中,虚拟机只有 1 CPU,进程 1,2 都会占用一个 CPU,因此根据设置进程 1 的 cpu.shares 为 1024,进程 2 的 cpu.shares 为 512,就可以将 2/3 的 cpu 分配给进程 1,1/3 的 cpu 分配给进程 2 了。
除了上述例子中的几个参数,cgroup cpu 子系统还提供了以下的参数:
- cpu.rt_period_us: 重新分配 CPU 资源的时间周期长度。 针对使用了实时调度器的进程
- cpu.rt_runtime_us: 进程在设置的时间周期长度内,可以使用的 CPU 时间上限。这个和上面说的 cfs 的两个参数类似。
- cpu.nr_periods: 这是一个统计参数。用来表示已经过去的 cpu 周期数(使用 cpu.cfs_period_us 来指定)
- cpu.nr_throttled: cgroup 中进程被限制的次数(因为这些进程用完了分配的 cpu 时间)。
- cpu.throttled_time: cgroup 中进程被限制的总时间(单位是 ns)。