kill 默认不是"杀死":进程信号与 nice 优先级实战
kill -9 是很多人的肌肉记忆——进程不听话,直接 -9 送走。但 kill 这个命令名其实起得有误导性:它默认发的信号根本不是”杀死”,而是”请求退出”。这两者的区别,决定了进程退出前有没有机会保存数据、释放资源。
这是 Linux 命令系列的一篇,承接 Linux 常见命令与工具地图;服务器排查那篇 讲了用 ps/top 找进程,这篇讲找到之后怎么正确地”管”它。
信号:内核给进程递的通知条
信号(signal)是内核发给进程的一种异步通知。大部分信号进程可以选择怎么响应——捕获它执行自定义逻辑、忽略它、或者按默认行为处理;只有极少数信号(比如 SIGKILL)是强制的,进程没有任何选择余地。
1 | kill -l |
kill 命令的作用就是给指定 PID 的进程发信号——发什么信号由参数决定,不指定就发默认的。
SIGTERM(15):kill 的默认信号,请求优雅退出
1 | kill 12345 |
绝大多数正经写的服务都注册了 SIGTERM 处理逻辑(Go 里的 signal.Notify、容器里的优雅停机钩子都是响应它),收到后会体面地收尾。这也是 systemctl stop、docker stop 停服务时先发的信号。
SIGKILL(9):最后手段,不是首选
1 | kill -9 12345 |
-9 的问题不在”杀不死”,恰恰在”杀得太死”:进程的清理逻辑一行都不会执行。数据库进程被 -9 可能留下损坏的数据文件,持锁的进程被 -9 可能让别的进程永远等锁。正确的顺序是:
1 | kill 12345 |
把 -9 当默认选项,等于放弃了所有进程善后的机会——它应该是 SIGTERM 无效之后的最后手段。
SIGHUP(1):重载配置的约定俗成
SIGHUP 历史上表示”终端挂断”(hangup,拨号上网时代的遗产),现在大量守护进程把它重新定义成”重新加载配置文件,但不重启”:
1 | kill -HUP $(cat /var/run/nginx.pid) |
不是所有程序都遵守这个约定,用之前查一下目标服务的文档;但见到 kill -HUP 时要知道,它的意图大概率是”重载配置”,不是杀进程。
按场景补全:还有几个迟早会用到的信号
前面 TERM/KILL/HUP 是服务进程最常打交道的三个,但信号还分布在别的几类常见场景里,按场景归类一次,以后碰到能直接搜到。
终端交互:Ctrl-C / Ctrl-\ / Ctrl-Z 发的都是信号
1 | # 前台跑一个程序时按下的这几个组合键,本质都是终端驱动发信号给前台进程组: |
kill -INT 12345 和终端按 Ctrl-C 发的是同一个信号,只是来源不同(终端驱动 vs 手动 kill)。很多程序对 SIGINT 和 SIGTERM 的处理逻辑是一样的(收到任何一个都触发优雅退出),这也是为什么很多语言的信号捕获写法会把这两个一起注册。
进程恢复:SIGSTOP 和 SIGCONT
1 | kill -STOP 12345 |
fg/bg 命令背后做的就是发 SIGCONT;SIGSTOP 常用来临时冻结一个占用资源的进程做诊断,而不用直接杀掉它,诊断完 SIGCONT 恢复运行。
自定义运行时行为:SIGUSR1 / SIGUSR2
1 | kill -USR1 $(cat /var/run/nginx.pid) |
见到某个服务的文档里提到”发 SIGUSR1 触发 xxx”,说明这是这个程序自己约定的行为,跟 nginx 的用法未必是一回事,用之前查该程序自己的文档。
子进程与管道:SIGCHLD、SIGPIPE 不是手动发的
1 | # SIGCHLD:子进程退出或状态改变时,内核自动发给父进程, |
这两个不是靠 kill 手动发的,是内核在特定事件下自动触发,但排查”进程为什么莫名其妙退出了”这类问题时,经常需要认出它们。
pkill / pgrep:按名字找和杀,跳过查 PID
pgrep 按进程名查 PID,pkill 按进程名直接发信号——省掉 ps aux | grep xxx 再手动复制 PID 的三步操作:
1 | pgrep -l nginx |
pkill -f 是双刃剑:模式写宽了会把不相干的进程一起杀掉。安全的习惯是先跑一遍同参数的 pgrep -fl,看清楚列表再动手。
nice / renice:CPU 调度优先级
nice 值决定进程在 CPU 竞争中的优先级,范围 -20 到 19——数值越低优先级越高,这个方向和直觉相反:可以理解成”nice 值越高,进程越’客气’,越愿意把 CPU 让给别人”。普通用户只能调高 nice 值(降低优先级),设负值需要 root。
1 | nice -n 19 tar -czf backup.tar.gz /data |
常见场景对应的信号速查表
| 场景 | 信号(编号) | 作用 |
|---|---|---|
| 请求优雅退出 | SIGTERM(15) | kill 默认信号,进程可捕获自己清理后退出 |
| 强制终结 | SIGKILL(9) | 内核直接终结,不可捕获不可忽略,无清理机会,最后手段 |
| 重载配置 | SIGHUP(1) | 多数守护进程约定为”重读配置不重启”(如 nginx -s reload) |
| 终端按 Ctrl-C | SIGINT(2) | 请求中断,默认终止,可捕获自己处理,语义常和 SIGTERM 一起注册 |
| 终端按 Ctrl-\ | SIGQUIT(3) | 请求退出并生成 core dump,比 Ctrl-C 更”重” |
| 终端按 Ctrl-Z | SIGTSTP(20) | 请求挂起到后台,不是终止,配合 fg/bg 恢复 |
| 强制暂停 | SIGSTOP(19) | 不可捕获不可忽略地冻结进程,常用于诊断 |
| 恢复运行 | SIGCONT(18) | 唤醒被 SIGSTOP 或 Ctrl-Z 暂停的进程,fg/bg 背后就是它 |
| 自定义运行时行为 | SIGUSR1 / SIGUSR2(10 / 12) | 内核不规定语义,由程序自己约定(nginx 用 SIGUSR1 切割日志) |
| 子进程状态变化(内核自动发) | SIGCHLD(17) | 子进程退出/停止时发给父进程,触发 wait() 回收,避免僵尸进程 |
| 写入已断开的管道/socket(内核自动发) | SIGPIPE(13) | 默认终止当前进程,网络服务常显式忽略它 |
| 只探测不发送 | kill -0 |
不发实际信号,只检测进程是否存在 |
| 列出全部信号 | kill -l |
查看系统支持的完整信号列表 |
| 命令 | 作用 |
|---|---|
pgrep -l 名字 |
按进程名查 PID |
pkill 名字 |
按进程名发 SIGTERM |
pkill -f 模式 |
按完整命令行匹配,先用 pgrep -fl 确认再杀 |
nice -n N 命令 |
以指定优先级启动,-20 最高、19 最低,越低越优先 |
renice -n N -p PID |
调整运行中进程的优先级,负值需要 root |
写到这里
kill 家族的正确打开方式:默认 SIGTERM 给进程体面退出的机会,几秒后还赖着不走再 -9;重载配置认准 SIGHUP;批量操作用 pkill 但先 pgrep 确认目标。至于 nice,记住”值越低越优先”这个反直觉设定,剩下的就是两条命令的事。










