深入理解 Linux 优雅停机 的工作机制
前几天远程重启家里那台跑了十几个 Docker 容器的 N100 小主机,reboot 敲下去,SSH 断开了,然后就一直连不上,等了五六分钟机器才回来。翻了一眼日志才发现,关机的时候有个服务卡了整整 90 秒超时。这事让我意识到,虽然 shutdown 这条命令敲了这么多年,关机背后到底发生了什么,我还真没认真看过。索性把最近梳理的东西记下来。
先搞清楚“优雅”优雅在哪
所谓优雅停机,核心就一句话:给正在运行的进程一个收尾的机会。内核给进程发的信号是 SIGTERM,意思是“该下班了,把手上的活干完,数据落盘,连接关掉,然后自己退出”。和它对应的 SIGKILL 就完全不是一回事了,进程根本收不到这个信号,内核直接把进程抹掉,没写完的数据就永远留在内存里了。
所以完整的优雅停机流程其实就是:先发 SIGTERM,等一段时间,还没死的再 SIGKILL 补刀。听起来简单,但所有的坑几乎都藏在“等多久”和“等的时候发生了什么”里。
systemd 接手后到底干了什么
现在主流发行版关机都是 systemd 说了算。你执行 systemctl poweroff(或者按一下机箱电源键,logind 收到 ACPI 事件后走的也是同一套流程),systemd 会切到 shutdown.target,然后按依赖关系,把一个个服务按顺序停下来。
每个服务停止时,systemd 先给它的主进程发 SIGTERM,然后盯着,等 TimeoutStopSec(默认 90 秒)的时间。超时还没退,就发 SIGKILL 补刀。这里有个很容易忽略的细节:systemd 判断服务停没停,看的不是主进程,而是整个 cgroup 有没有清空。也就是说,哪怕你的主进程已经体面退出了,但它 fork 出来的某个子进程还赖在里面,这个服务就得一直等到超时。
想查自己机器关机都卡在哪,可以翻上一次启动的日志:
journalctl -b -1
搜一下 A stop job is running for,后面跟着的服务名就是嫌疑人,括号里还有它耗了多久。
你的机器为什么关机那么慢
结合我自己踩过的坑,常见嫌疑人大概这几个。
一是压根不理 SIGTERM 的程序。有些程序压根没注册信号处理,有些注册了但收尾动作太重(比如退出前要同步几个 G 的数据),90 秒耗完很正常。如果某个服务就是要磨蹭,可以单独给它放宽或者收紧超时:
[Service]
TimeoutStopSec=20s
二是各种文件系统挂载。挂过 NFS 的机器应该有体会,网络断了之后 umount 能卡到天荒地老,进程进到 D 状态之后连 SIGKILL 都杀不动,只能干等或者硬断电。
三是 Docker。docker stop 默认只给容器 10 秒钟,比 systemd 还要急。更隐蔽的坑是:如果你的主程序直接以 PID 1 跑在容器里,而它没注册 SIGTERM 处理器,内核对 PID 1 是有特殊照顾的——未处理的信号直接被忽略,SIGTERM 发过去跟没发一样。解决办法要么加个 --init 参数让 tini 当 PID 1,要么自己把信号处理写上。我那台小主机卡 90 秒的服务,查到最后就是这个问题。
自己写服务时的两条底线
轮到自己写程序的时候,事情其实很简单,就两条:注册 SIGTERM,收到之后停止接新活,把手上处理完,刷盘,退出。
拿 Python 随手举个例子:
import signal, time
running = True
def handle_term(sig, frame):
global running
running = False
signal.signal(signal.SIGTERM, lambda s, f: on_term())
while running:
handle_one_task()
flush_data() # 刷缓冲
close_connection() # 关连接
write_checkpoint() # 记录状态
要注意收尾逻辑本身别写得太重。systemd 给你 90 秒,前面排队的停止动作还会分掉一部分,真正留给你刷盘的时间可能就几十秒。重要的数据应该平时就落盘,退出时只做最后一点扫尾,别把两年的数据同步都堆在临终那一刻。
最后内核干了什么
所有进程都停干净了,故事还没完。systemd 最后会把自己换成 systemd-shutdown 这个精简程序继续收尾:卸载剩余的文件系统、把根文件系统重新挂成只读、执行同步、最后通过 ACPI 触发真正的断电。所以在最外层兜底的其实是内核,systemd 只负责前面的“礼貌环节”。
搞明白这一整套流程之后,很多以前似懂非懂的东西就串起来了:kill -9 为什么是万不得已,容器里信号为什么经常“发不进去”,关机慢的时候该去翻哪段日志。优雅停机这件事,说到底就是一层一层往下传的那句“请把手头的事做完”——每一层都守规矩,机器才能干净利落地停下来。
评论
还没有评论。
发表评论
提交后评论将经过自动审核,审核通过后公开展示。