先记住这个答案
SIGTERM 是进程可捕获、可处理的终止信号,默认行为是结束进程,但进程可以注册处理器进行清理后退出;SIGKILL 则被内核直接执行,进程无法拦截或忽略。直接 kill -9 会强制终止,跳过所有善后逻辑,留下临时文件、锁、子进程等残局。因此除非 SIGTERM 无效,否则应优先使用它,确保进程有序退出。
- SIGTERM 默认终止但可捕获处理。
- SIGKILL 强制终止且无法拦截。
- 优先 SIGTERM 保数据一致。
可捕获与不可捕获信号的内核差异
进程对信号的响应分三类:默认动作、捕获处理、忽略。SIGTERM 的默认动作是终止,但 POSIX 允许进程用 signal() 或 sigaction() 注册处理器,在退出前执行释放内存、关闭文件、通知子进程等操作。内核在递送该信号时,若发现进程有处理器,就切换用户态执行处理器;处理器返回后,进程通常继续执行,除非处理器调用了 exit 等终止操作。
SIGKILL 的特殊性在于它既不能被捕获也不能被忽略,内核直接触发 do_exit 强制回收进程。这意味着进程没有机会运行任何清理代码,处于临界区的操作,比如写数据库事务中只写了一半日志、持有锁未释放,都会残留在系统里。后续由外部机制去补偿,例如数据库重放日志或锁超时,但这类补偿并不总是存在。
数据库写入进程挂死不响应:为何先用 SIGTERM
假设一个应用进程在写文件时卡在磁盘 I/O 上,管理接口已无响应。运维用 kill -TERM <pid>,进程收到信号后虽然没有立即处理,但内核会在 I/O 返回后继续执行,信号处理器检测到未完成写操作,先调用 fsync 落盘,再退出。最终文件数据完整,没有残留半截记录。
若一上来用 kill -9,内核直接杀掉进程,文件系统缓冲区里的脏页可能还没写回,导致文件截断。虽然数据库可能靠前写日志恢复,但普通应用没有此机制,数据就丢了。可见,先 SIGTERM 给了进程自行完成关键收尾的机会,尤其是当挂死是因为等待锁或长时间计算时,信号能及时中断正常路径并触发清理。
SIGTERM 不总是有效:何时必须升级到 SIGKILL
SIGTERM 的局限在于进程可能处于不可中断睡眠(D 状态),例如等待磁盘 I/O 或内核锁,此时信号被挂起,处理器无法执行。如果进程陷入死循环但没屏蔽信号,SIGTERM 能递送并调用处理器,但处理器未必会终止进程;若进程显式忽略 SIGTERM 或将其阻塞,它也不会退出。排查时先看进程状态:R 或 S 状态可用 TERM,D 状态则 TERM 通常无效。
生产上常见的做法是:先发 SIGTERM,给 30–60 秒观察;若进程仍未结束,再发 SIGKILL。但这个等待基于业务可容忍的停机时间。对于长时间无法恢复的进程,SIGKILL 后需人工检查遗留的 pid 文件、锁、共享内存段等。另外,向整个进程组发信号时,SIGTERM 允许每个进程各自清理,而 SIGKILL 则群体瞬间消失。
容易答错的地方
- 以为 SIGKILL 更干净利落
- 真相:SIGKILL 直接杀死进程,不执行用户态清理,可能留下孤儿子进程和脏数据。优先用 SIGTERM 才是标准做法。
- 认为 SIGTERM 一定能让进程退出
- 进程可以忽略或阻塞 SIGTERM,且 D 状态无法处理信号。所以 SIGTERM 只是请求退出,不是强制。判断需要结合进程状态和是否注册处理器。
面试官还会怎么问?
怎么确认进程是否支持 SIGTERM 清理?
查看应用文档或源代码,看是否注册信号处理器。运行中可用 cat /proc/PID/status 看 SigCgt 位掩码,判断 15 号信号是否被捕获。
SIGTERM 和 SIGINT 有什么区别?
SIGINT 通常由 Ctrl+C 产生,SIGTERM 是 kill 默认信号。两者都可捕获,但语义不同:SIGINT 用于中断交互,SIGTERM 用于请求终止,有些程序会区分处理。
杀不掉时怎么判断是 D 状态还是屏蔽了信号?
用 ps -o stat 看是否 D;用 cat /proc/PID/status 的 SigBlk 查看阻塞集合。若阻塞了 SIGTERM,可用 SIGKILL 绕过。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。