LWN: cgroup 新增 kill 功能!
关注了就能看到更多这么棒的文章哦~
A "kill" button for control groups
By Jonathan Corbet
May 3, 2021
DeepL assisted translation
https://lwn.net/Articles/855049/
内核的 cgroup 机制(control group)是用来划分进程并为每个进程提供资源保证(和限制)而存在的。在一个正确配置好的 cgroup 中运行的进程不能剥夺在其他 group 中运行的进程所预留的资源(CPU 时间、内存、I/O 带宽等),并且同样不受其他进程的干扰。除了少数例外情况以外,cgroup 不会直接对进程采取行动,而 Christian Brauner 的 cgroup.kill 这组 patch set 就为了成为这些例外之一。
在目前的内核中,对 cgroup 内的进程采取行动的一种方式是通过 "freezer",也就是用来暂停(或恢复)这个 cgroup 中所包含的进程。不过,除此之外,很少有 cgroup 的开关会直接影响到进程的状态。Brauner 的 patch set 在每个 non-root 的 cgroup 中增加了一个控制文件,名为 "kill"。它的效果正如它的名字那样。向该文件写入 "1" 会将导致该 group 中的所有进程立即死亡(更正确的说法是它会立即触发向每个进程发送一个 SIGKILL 信号)。如果 cgroup 里包含了其他的 cgroup,那也会一起被消灭。等操作完成,该组通常会处于一个完全被清除干净的状态。
当然,这种行为也有几个例外。kill 操作的定义,是对一个进程起作用。如果该进程包含许多线程,那么它们都将被 kill。但是,如果相关的 cgroup 是在thread mode下,也就是允许一个进程的多个线程分布在多个 cgroup 中,这就可能导致不在目标 cgroup 中的线程也会被意外杀死。因此,如果试图对以 thread mode 运行的 cgroup 进行 kill 操作,将会直接失败。
同样,kill 操作也不会杀死内核线程,因为这可能导致一些不好的结果。对一个包含内核线程的 cgroup 中可以写入 kill,但内核线程本身继续保持存活。因此在这种情况下,这个 cgroup 在调用结束时不会是空的。
Brauner 列举了这个功能的一些潜在用途。最明显的用途之一是容器(container)管理。如果决定关闭一个容器,那么这个 kill 操作是一种快速而直接的方式。Systemd 已经将各个 service 都按照 cgroup 来划分管理了起来,在需要时,它可以使用这个操作作为一种更简单的方式来停止一项 service。同样地,用户空间的 out-of-memory 管理也可以使用这个操作,在需要时直接让整个 cgroup 消失。kill 操作也可以是抵御 fork-bomb 的一种有效的手段,当调用了 kill 操作时,在 cgroup 里设置一个标志,防止创建新的进程,这样就能有效组织正在进行 fork 的进程。
另一方面,这个功能可以被认为是给系统中的每个 cgroup 配备了一个大大的红色按钮,上面写着 "DO NOT PUSH THIS"。如果错误写入 kill 节点有可能会对运行中的系统造成巨大的损害。对于人们的这种担忧,简单的回答就是:"那就别这么做",但不难想象,会有一些人希望在对这个文件加一些防护。
目前的 patch 是通过向目标 cgroup 中的每个进程发送 SIGKILL 信号来生效的。没有任何关于不能发送其他信号的限制,这个功能今后似乎也可能在有些情况下会有好处。kill() 系统调用可以发送任何信号,最终可能也有需要让 cgroup.kill 文件也允许其他信号。
到目前为止,对这组 patch 还没有很多评论,也许部分原因是它还没有传播到 cgroup 邮件列表之外。对于代码实现来说,可能没有什么可以挑毛病的,因为代码实现相当直接,所以如果要说有什么会导致这项工作暂时无法合入的话,那也一定是跟这个功能本身有关。不过,似乎使用场景非常明显,所以这个 kill 开关在不久的将来确实可能成为 cgroup 的一个功能。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~