Cgroup 冻结器

Cgroup 冻结器对于批处理作业管理系统非常有用,该系统根据系统管理员的需求启动和停止一组任务以调度机器资源。这种程序常用于 HPC 集群,以调度对整个集群的访问。Cgroup 冻结器使用 cgroup 来描述批处理作业管理系统将启动/停止的任务集。它还提供了一种启动和停止构成作业的任务的方法。

Cgroup 冻结器也将有助于检查点(checkpointing)运行中的任务组。冻结器允许检查点代码通过尝试将 cgroup 中的任务强制进入静止状态来获取任务的一致镜像。一旦任务静止,另一个任务就可以遍历 /proc 或调用内核接口来收集有关静止任务的信息。检查点任务可以在发生可恢复错误时稍后重新启动。这也允许通过将收集到的信息复制到另一个节点并在该节点上重新启动任务,从而在集群节点之间迁移检查点任务。

在用户空间中,SIGSTOP 和 SIGCONT 序列并不总是足以停止和恢复任务。我们希望冻结的任务内部可以观察到这两个信号。虽然 SIGSTOP 无法被捕获、阻塞或忽略,但父任务可以通过等待或 ptracing 看到它。SIGCONT 尤其不适用,因为它可以被任务捕获。任何旨在监视 SIGSTOP 和 SIGCONT 的程序都可能因尝试使用 SIGSTOP 和 SIGCONT 停止和恢复任务而出现问题。我们可以使用嵌套的 bash shell 来演示这个问题

$ echo $$
16644
$ bash
$ echo $$
16690

From a second, unrelated bash shell:
$ kill -SIGSTOP 16690
$ kill -SIGCONT 16690

<at this point 16690 exits and causes 16644 to exit too>

发生这种情况是因为 bash 可以观察到这两个信号并选择如何响应它们。

另一个捕获并响应这些信号的程序示例是 gdb。事实上,任何设计用于使用 ptrace 的程序都可能遇到这种停止和恢复任务方法的困扰。

相比之下,cgroup 冻结器使用内核冻结器代码来防止冻结/解冻周期对被冻结的任务可见。这使得上述 bash 示例和 gdb 能够按预期运行。

Cgroup 冻结器是分层的。冻结一个 cgroup 会冻结属于该 cgroup 及其所有子 cgroup 的所有任务。每个 cgroup 都有自己的状态(自状态)和从父级继承的状态(父状态)。当且仅当(iff)两个状态都为 THAWED 时,cgroup 才为 THAWED。

以下 cgroupfs 文件由 cgroup 冻结器创建。

  • freezer.state: 可读写。

    读取时,返回 cgroup 的有效状态——“THAWED”、“FREEZING” 或 “FROZEN”。这是自状态和父状态的组合。如果任何一个处于冻结中,则 cgroup 处于冻结中(FREEZING 或 FROZEN)。

    当属于 cgroup 及其子代的所有任务都变为冻结状态时,处于 FREEZING 状态的 cgroup 会转换为 FROZEN 状态。请注意,当有新任务添加到 cgroup 或其子 cgroup 中时,cgroup 会从 FROZEN 状态恢复到 FREEZING 状态,直到新任务被冻结。

    写入时,设置 cgroup 的自状态。允许两个值——“FROZEN” 和 “THAWED”。如果写入 FROZEN,则 cgroup(如果尚未冻结)将与其所有子 cgroup 一起进入 FREEZING 状态。

    如果写入 THAWED,则 cgroup 的自状态将变为 THAWED。请注意,如果父状态仍在冻结中,则有效状态可能不会变为 THAWED。如果 cgroup 的有效状态变为 THAWED,则所有因该 cgroup 而处于冻结状态的子代也将离开冻结状态。

  • freezer.self_freezing: 只读。

    显示自状态。如果自状态为 THAWED,则为 0;否则为 1。当且仅当(iff)上次写入 freezer.state 的值为“FROZEN”时,此值为 1。

  • freezer.parent_freezing: 只读。

    显示父状态。如果 cgroup 的任何祖先都未冻结,则为 0;否则为 1。

根 cgroup 不可冻结,且上述接口文件不存在。

  • 用法示例

    # mkdir /sys/fs/cgroup/freezer
    # mount -t cgroup -ofreezer freezer /sys/fs/cgroup/freezer
    # mkdir /sys/fs/cgroup/freezer/0
    # echo $some_pid > /sys/fs/cgroup/freezer/0/tasks
    

获取冻结器子系统的状态

# cat /sys/fs/cgroup/freezer/0/freezer.state
THAWED

冻结容器中的所有任务

# echo FROZEN > /sys/fs/cgroup/freezer/0/freezer.state
# cat /sys/fs/cgroup/freezer/0/freezer.state
FREEZING
# cat /sys/fs/cgroup/freezer/0/freezer.state
FROZEN

解冻容器中的所有任务

# echo THAWED > /sys/fs/cgroup/freezer/0/freezer.state
# cat /sys/fs/cgroup/freezer/0/freezer.state
THAWED

这是基本机制,在简单场景下,它应该能正确地处理用户空间任务。

此冻结器实现存在缺陷(参见 提交 76f969e8948d8(“cgroup: cgroup v2 冻结器”))并且推荐使用 cgroup v2 冻结器。