Linux 操作系统原理 — cgroups 进程资源管理和限制

目录

cgroups

cgroups(control groups)是 Linux Kernel 提供的一种可以限制单个进程或者多个进程所使用资源的机制,可以对 CPU、Memory、I/O 等资源实现精细化的控制,轻量级容器技术 Docker 就使用了 cgroups 提供的资源限制能力来完成操作系统级别的资源控制,继而实现操作系统的虚拟化。

cgroups 的子系统

cgroups 为每种可被控制的资源定义了一个子系统。典型的子系统如下:

  • cpu 子系统:主要限制进程的 CPU 使用率。
  • cpuacct 子系统:可以统计 cgroups 中的进程的 CPU 使用报告。
  • cpuset 子系统:可以为 cgroups 中的进程分配单独的 CPU 或 Memory 节点。
  • memory 子系统:可以限制进程的 Memory 使用量。
  • devices 子系统:可以控制进程能够访问某些设备。
  • blkio 子系统:可以限制进程的块设备 I/O。
  • net_cls 子系统:可以标记 cgroups 中进程的网络数据包,然后可以使用 tc(traffic control) 模块对数据包进行控制。
  • freezer 子系统:可以挂起或者恢复 cgroups 中的进程。
  • ns 子系统:可以使不同 cgroups 下面的进程使用不同的 namespace。

注意,每一个子系统都需要与 Kernel 的其他模块配合来完成资源的控制,比如:

  • 对 CPU 资源的限制是通过 进程调度模块 根据 cpu 子系统的配置来完成的;
  • 对 Memory 资源的限制则是 内存模块 根据 memory 子系统的配置来完成的。
  • 对网络数据包的控制则需要 Traffic Control 模块 根据 net_cls 子系统的配置来完成。

cgroups 的层级结构

内核使用 cgroup Struct(结构体)来表示一个 control group 对若干个 cgroups 子系统的资源限制。

cgroup Struct 可以组织成一颗树的形式,称之为一个 cgroups Hierarchy(层级结构),每个 cgroups Hierarchy 可以 Attach 若干个 cgroups 子系统,cgroups Hierarchy 可以对其 Attach 的 cgroups 子系统进行资源的限制。每一个 cgroups 子系统只能被 Attach 到一个 cgroups Hierarchy 中。

在这里插入图片描述

如上图的 cgroups Hierarchy A、B,每个 Hierarchy 中是一颗树形结构,树的每个节点是一个 cgroup Struct,比如:cpu_cgrp, memory_cgrp。

  1. cgroups Hierarchy A:Attach 了 cpu 子系统和 cpuacct 子系统, 当前 cgroups Hierarchy 中的 cgroup Struct 就可以对 CPU 的资源进行限制,并且对进程的 CPU 使用情况进行统计。
  2. cgroups Hierarchy B:Attach 了 memory 子系统,当前 cgroups Hierarchy 中的 cgroup Struct 就可以对 Memory 的资源进行限制。

并且,在每一个 cgroups Hierarchy 中,每一个节点(cgroup Struct)可以设置对资源不同的限制权重。如上图中 cgrp1 组中的进程可以使用 60% 的 CPU 时间片,而 cgrp2 组中的进程可以使用 20% 的 CPU 时间片。

cgroups 与进程

上面提到了 Linux Kernel 使用 cgroups 子系统对系统的资源进行限制,也提到了 cgroups 子系统需要 Attach 到 cgroups Hierarchy 中来对进程进行资源控制。那么 Kernel 是如何把 Processor(进程)与 cgroups Hierarchy 关联起来的呢?

在创建了 cgroups Hierarchy 中的节点(cgroup Struct)之后,可以把 Processor 加入到某一个节点的控制任务列表中,一个节点的控制列表中的所有 Processor 都会受到当前节点的资源限制。同时某一个 Processor 也可以被加入到不同的 cgroups Hierarchy 的节点中,因为不同的 cgroups Hierarchy 可以负责不同的系统资源。

所以 Processor 和 cgroup Struct 是一个多对多的关系,表示不同的 Processor 被不同的 cgroup Struct 控制从而具有了不同的资源限制。

在这里插入图片描述

上图描述了进程与 cgroups 之间的关系。P 代表一个进程。每一个进程的描述符中有一个指针指向了一个 css_set(cgroups subsystem set)数据结构,指向某一个 css_set 的进程会被加入到该 css_set 的进程链表中。一个进程只能隶属于一个 css_set,一个 css_set 可以包含多个进程,隶属于同一 css_set 的进程受到该 css_set 所关联的资源限制。

其中 M×N Linkage 表示 css_set 可以与 cgroup Struct 进行多对多的关联。但是 cgroups 的实现不允许 css_set 同时关联同一个 cgroups Hierarchy 下的多个 cgroup Struct。 这是因为 cgroups 对同一种资源不允许有多个限制配置。一个 css_set 关联多个 cgroups Hierarchy 的节点时,表明需要对当前 css_set 下的进程进行多种资源的控制。而一个 cgroups 节点关联多个 css_set 时,表明多个 css_set 下的进程列表受到同一份资源的相同限制。

cgroups 与文件系统

Linux 使用了多种数据结构在内核中实现了 cgroups 的配置,关联了 Processor 和 cgroup Struct,那么 Linux 又是如何让用户态的进程使用到 cgroups 的功能呢?

Linux File System 实现为 VFS(Virtual File System)。VFS 能够把底层不同类型的文件系统 Provider 的细节隐藏起来,给用户态进程提供一个统一的文件系统 API 接口。cgroups 也是通过 VFS 把功能暴露给用户态的,cgroups 与 VFS 之间的衔接部分称之为 cgroups File System。

基于 VFS 实现的文件系统都必须实现 VFS 通用文件模型定义的对象,cgroups File System 亦如此,cgroups 通过实现 VFS 的通用文件系统模型,把维护 cgroups Hierarchy 的细节,隐藏在 cgroups File System 的这些实现函数中。

cgroups 的使用

挂载 cgroups File System

使用 cgroups 之前首先要挂载 cgroups File System,指令:

mount -t cgroup -o subsystems name /cgroup/name
  • subsystems 表示需要挂载的 cgroups 子系统。
  • /cgroup/name 表示挂载点。

如上所述,这条命令在内核中创建了一个 cgroups Hierarchy。

比如挂载 cpuset、cpu、cpuacct、memory 这 4 个 cgroupssubsystem 到 /cgroup/cpu_and_mem 目录下的话,就执行:

mount -t cgroup -o remount,cpu,cpuset,memory cpu_and_mem /cgroup/cpu_and_mem

在 CentOS 中,yum install libcgroup 安装了 cgroups 模块之后,在 /etc/cgconfig.conf 配置文件中会自动生成 cgroups subsystem 的挂载点:

mount {
    cpuset  = /cgroup/cpuset;
    cpu = /cgroup/cpu;
    cpuacct = /cgroup/cpuacct;
    memory  = /cgroup/memory;
    devices = /cgroup/devices;
    freezer = /cgroup/freezer;
    net_cls = /cgroup/net_cls;
    blkio   = /cgroup/blkio;
}

上面的每一条配置都等价于展开的 mount 命令。

创建子节点

挂载某个 cgroups subsystem 到挂载点之后,就可以通过在挂载点下面建立文件夹或者使用 cgcreate 命令的方法创建 cgroups Hierarchy 中的节点(cgroup Struct)了。比如通过命令:

cgcreate -t sankuai:sankuai -g cpu:test

就可以在 cpu subsystem 下建立一个名为 test 的 cgroup Struct 了。结果如下所示:

$ ls
cgroup.event_control  cgroup.procs  cpu.cfs_period_us  cpu.cfs_quota_us  cpu.rt_period_us   cpu.rt_runtime_us  cpu.shares  cpu.stat  lxc  notify_on_release  release_agent  tasks  test

然后可以通过写入需要的值到 test 下面的不同文件,来配置需要限制的资源。每个 subsystem 下面都可以进行多种不同的配置,需要配置的参数各不相同,详细的参数设置需要参考 man cgroups 手册。例如:使用 cgset 命令也可以设置 cgroups 子系统的参数,指令为:

cgset -r parameter=value path_to_cgroup

当需要删除某一个 cgroups 节点的时候,可以使用 cgdelete 命令,比如要删除上述的 test 节点,可以使用指令:

cgdelete -r cpu:test

将进程加入子节点

把进程加入到 cgroups 子节点也有多种方法,可以直接把 pid 写入到子节点下面的 task 文件中。也可以通过 cgclassify 添加进程,指令为:

cgclassify -g subsystems:path_to_cgroup pidlist

也可以直接使用 cgexec 在某一个 cgroups 下启动进程,指令为:

gexec -g subsystems:path_to_cgroup command arguments

猜你喜欢

转载自blog.csdn.net/Jmilk/article/details/108894380