暗色模式

Linux 信号与后台任务:从 kill 终止到 nohup 与 setsid

技术教程
2026-08-18
8
0
本文要点
  • Linux 进程间通信靠信号(Signal)kill 命令的本质就是向进程发信号,而不是字面上的"杀死"。
  • 最常用的 SIGTERM(15)SIGKILL(9) 区别:前者让程序自行清理退出(可拦截),后者由内核强制终止(无法拦截)。
  • SIGSTOP(19) / SIGCONT(18) 可以在不销毁进程的前提下挂起/恢复任务,对应 kill -STOPkill -CONT
  • & 放到后台的进程会随终端关闭被 SIGHUP 杀掉,nohupsetsid 是两种常见"保活"手段,其原理差异值得掌握。
  • 掌握了信号与后台任务控制,就能稳健地管理长任务、守护进程与定时清理,是 Linux 运维基本功。

为什么需要信号

在 Linux 中,进程之间需要一种轻量级的通知机制来协调、终止或改变行为,这就是信号(Signal)。它不像消息队列那样传递大量数据,而是一个简单的"数字代号",内核据此触发进程的某个默认动作。无论是你用 Ctrl+C 中断前台程序、用 kill 结束一个进程,还是系统关闭时通知守护进程收尾,背后都是信号在工作。

理解信号,是理解"如何安全地终止、挂起、恢复一个进程"的第一步,也是日常运维里排障和守护长任务的关键。

认识最常见的信号

运行 kill -l 可以看到系统支持的全部信号列表。其中有几个在运维中最常打交道:

信号数值默认动作能否拦截
SIGHUP1终止进程
SIGINT2终止进程(对应 Ctrl+C)
SIGKILL9立即终止进程不能
SIGTERM15优雅终止进程
SIGCONT18继续被挂起的进程不能
SIGSTOP19暂停(挂起)进程不能
SIGTSTP20暂停进程(对应 Ctrl+Z)

其中需要重点区分的是 SIGTERMSIGKILLSIGTERM(15) 是"请你自己收尾退出",程序有机会保存数据、释放资源,甚至主动忽略;而 SIGKILL(9) 由内核直接强制杀掉进程,任何程序都无法拦截,适合处理"已经卡死、无论如何都不退出"的进程。

用 kill 发送信号:优雅终止

kill 命令的名字容易让人误解,它的真正作用是"向指定进程发送信号"。默认发送的是 SIGTERM(15),也就是"请优雅退出"。

sleep 300 &
echo "PID=$!"
kill -15 $PID

下面这张截图展示了完整的流程:先启动一个 300 秒的 sleep 长任务,拿到它的 PID,然后发送 SIGTERM(15),进程随即退出:

优雅终止一个后台进程

注意:Ctrl+C 对应的是 SIGINT(2),只能中断前台任务;对后台任务则必须用 kill 配合 PID 或作业号来发信号。

kill 既可以用信号数值,也可以用信号名称,二者等价:

kill -15 1234    # 用数值
kill -TERM 1234  # 用名称
kill 1234        # 省略时默认就是 SIGTERM

当进程对 SIGTERM 无响应时,才考虑升级为 SIGKILL(9)

kill -9 1234

挂起与恢复:SIGSTOP 与 SIGCONT

有时候我们并不是要终止进程,而是想让它暂时停下来,稍后再继续。这正是 SIGSTOPSIGCONT 的用途。SIGSTOP(19) 会把进程挂起(不占用 CPU),SIGCONT(18) 再把它唤醒。

ps 输出的 STAT 列中,被挂起的进程状态会从 S(Sleeping,睡眠)变成 T(Stopped,已停止),恢复后又回到 S

sleep 300 &
PID=$!
kill -STOP $PID   # 挂起,状态变 T
kill -CONT $PID   # 恢复,状态变回 S

下图演示了整个过程,可以清楚看到状态在 S → T → S 之间切换:

挂起与恢复进程状态变化

Ctrl+Z 对应 SIGTSTP(20),同样是把前台进程挂起,但它可以被捕获。SIGSTOP 则和 SIGKILL 一样,是内核强制执行的,无法被程序拦截。

后台任务:&、nohup 与 setsid

& 把命令放到后台,是一件再常见不过的事。但后台任务有个"坑":当所在的终端会话关闭时,shell 会向它发送 SIGHUP(1),进程默认被终止。这就是为什么明明后台跑着任务,关掉终端后任务就"没了"。

要解决这个问题,常用两种手段:nohupsetsid

nohup:忽略 SIGHUP

nohup 的意思是 "no hangup",它让命令忽略 SIGHUP 信号。配合 & 放到后台,并把输出重定向到文件(避免占用终端),就能保证即使退出终端任务也在继续:

nohup sleep 120 >/tmp/nohup_demo.log 2>&1 &

setsid:开启全新会话

setsid 则是让命令脱离当前会话,开启一个新的会话。由于进程一旦脱离原会话,终端关闭时的 SIGHUP 就再也找不到它了,因此同样能达到"保活"效果,而且更彻底。

下面这张截图对比了三种启动方式的进程属性差异。重点关注 PGID(进程组)、SESS(会话)和 STAT 列:

普通后台与 nohup、setsid 对比

观察输出可以得出清晰结论:

  • 普通后台sleep &):PGID=SESS=1649799,与父 shell 同属一个会话,终端关闭会被 SIGHUP 杀掉。
  • nohupPGID=SESS=1649799,会话没变,只是忽略 SIGHUP,所以它仍然"绑定"在原会话里。
  • setsidPGID=SESS=1649802,相比父进程的 1649799 是新的一组,STAT 显示为 Sss 表示会话首进程),完全独立的会话,终端关闭对它毫无影响。
从长期守护任务的角度看,setsidnohup 更彻底;而如果只是随手在交互终端里避免一次挂断,用 nohup 就足够轻量。

快速结束同类进程:pkill 与 killall

当需要一次性结束所有符合特征的进程时,逐个查 PID 太麻烦,可以用 pkill 按名称匹配,或用 killall 按精确名称结束:

pkill -x sleep      # 结束所有名为 sleep 的进程(-x 精确匹配)
killall sleep       # 按进程名结束所有 sleep
pkill -f "node app.js"   # 按完整命令行模糊匹配

pkillpgrep 是一对好搭档:先用 pgrep 确认要匹配哪些 PID,再用 pkill 精准清理,能有效避免误杀。

常见坑与提示

  • kill 默认发 SIGTERM,不是立即杀:对卡死的进程要先用 TERM,实在无效再 KILL,避免数据损坏。
  • 区分后台与前台信号Ctrl+C/Ctrl+Z 只对前台任务有效,后台任务必须用 kill/killall/pkill
  • nohup 不改变会话:它只是忽略 SIGHUP。要彻底脱离终端会话,应使用 setsid
  • SIGKILLSIGSTOP 无法拦截:这是内核层强制操作,程序写再多清理代码也无济于事。
  • kill -l 的列表随架构略有差异:实时信号(SIGRTMIN 起)数量在不同系统上可能不同,但 1~31 的通用信号在所有主流 Linux 上一致。

小结

信号是 Linux 进程控制的核心语言。掌握 SIGTERM/SIGKILL 实现优雅或强制的终止,SIGSTOP/SIGCONT 实现挂起与恢复,再配合 nohupsetsid 等后台保活手段,你就能稳健地管理长任务与守护进程。这些技能在日常运维、脚本编写和服务部署中几乎每天都会用到。

参考资料

发表评论

暂无评论,快来抢沙发吧!