cgroup cpu子系统

概述

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_uscpu.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)。

参考

Linux进程调度:完全公平调度器CFS

redhat cfs cpu

《cgroup cpu子系统》上有2条评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据