信号、Ctrl 组合键与进程状态

0 阅读5分钟

1:什么是信号?进程收到信号会怎么处理?

考察点:信号机制 · 异步中断

信号(signal)= 内核或进程发给另一个进程的"异步通知",本质是软件中断。

进程对信号有三种处理方式:
  1) 默认动作(终止 / 暂停 / 继续 / 忽略 / core dump)
  2) 捕获(自己写 handler 处理,比如"收到 TERM 先把缓存落盘再退出")
  3) 忽略(显式忽略;但 SIGKILL、SIGSTOP 无法被捕获或忽略)
kill -l            # 列出所有信号编号和名字(面试前扫一眼)
kill -l 15         # 15 → TERM

2:kill -15 / kill -9 / kill -2 有什么区别?

kill 默认发 15(SIGTERM),是可捕获的优雅退出,进程可以落盘、关连接;kill -2 是 SIGINT,等同 Ctrl+C,也是优雅中断;kill -9 是 SIGKILL,不可捕获不可忽略,内核直接杀,进程没有任何清理机会。

所以顺序是先 15、等几秒不退出,再上 9。


3:Ctrl+C、Ctrl+Z、Ctrl+D 有什么区别?

考察点:终端控制 · Ctrl+D 不是信号(这是本题的关键)

这题的差距就在:能说出 Ctrl+D 不是信号,面试官立刻觉得你懂。

按键发信号吗信号 / 效果进程状态变化能恢复吗
Ctrl+C是SIGINT(2)前台进程终止(可被捕获改写行为)不能
Ctrl+Z是SIGTSTP(20)前台进程暂停并放到后台能,fg / bg 恢复
Ctrl+D不是信号表示输入结束(EOF):read() 返回 0不影响进程本身—
Ctrl+反斜杠是SIGQUIT(3)终止 + core dump不能
Ctrl+S / Ctrl+Q不是终端流控暂停 / 恢复屏幕输出(程序还在跑)能

Ctrl+D 要说清楚的三层:

1) 它不是信号!它是终端把"输入结束"告诉程序(read() 返回 0,即 EOF)。
2) 在空行按 Ctrl+D → shell 读到 EOF → shell 退出(logout)。
3) 有输入时按 Ctrl+D → 把这行内容先交给程序(不结束输入),再按一次才结束。
   例:cat 里按 Ctrl+D 结束输入;python 交互里按 Ctrl+D 退出解释器。

对比记忆:Ctrl+C 是「我不想让你跑了」,Ctrl+Z 是「你先歇会儿」,Ctrl+D 是「我说完了」。


4:进程有哪几种状态?D 状态是什么意思?

考察点:ps 的 STAT 列 · 卡 IO 的判断

ps aux            # 看 STAT 列
ps -eo pid,stat,comm | grep -E '^ *[0-9]+ (D|Z|T)'
状态含义常见原因
RRunning / Runnable(在跑或在等 CPU)正常
SInterruptible sleep(可中断睡眠)绝大多数进程的常态:等 IO、等事件
DUninterruptible sleep(不可中断睡眠)卡在磁盘/NFS/驱动的 IO 上,不响应信号
TStopped(暂停)收到 SIGSTOP / Ctrl+Z;被调试器停住
ZZombie(僵尸)子进程已退出,父进程没 wait 回收
IIdle 内核线程基本不用管
STAT 里的后缀字母:s = 会话首进程,l = 多线程,+ = 前台进程组,< = 高优先级,N = 低优先级

面试官最爱顺着问:「你刚才说 Ctrl+Z 是挂起 —— 那挂起之后进程是什么状态?」→ T。「怎么恢复?」→ fg %1 / bg %1,或 kill -CONT <pid>


5:kill -9 不是强制杀吗?有没有杀不掉的情况?

考察点:D 状态 · 内核不可中断等待 · 僵尸的本质

这题能答出来,说明你不只会用命令。

情况原因怎么处理
进程处于 D 状态卡在内核的 IO 等待里,信号要等系统调用返回才能处理,而 -9 也不能打断已经启动的不可中断等待等 IO 超时恢复;查是哪块盘/NFS 慢(cat /proc/<pid>/stack、dmesg),严重时只能重启
进程已经是僵尸 Z它已经死了,只剩进程表项,信号无处可发杀父进程,让它被 init/PID 1 收养并回收
是内核线程不是用户态进程,信号不适用别动
权限不足非 root 杀别人的进程 → Operation not permitted用 root 或 sudo
容器里的 PID 1容器内 1 号进程对未注册的信号会忽略(内核为此有特殊处理)用 docker stop(先 TERM 后 KILL)或修容器入口

一句话答法:「-9 杀不掉通常有三种:D 状态卡在不可中断 IO(要等 IO 恢复)、僵尸 Z(要杀父进程)、权限不足或容器 PID 1。」


6:systemctl stop 背后做了什么?

考察点:优雅停止 · TimeoutStopSec

systemctl stop nginx
  → systemd 给主进程发 SIGTERM(默认 KillSignal)
  → 等 TimeoutStopSec(默认 90 秒)让进程自己退
  → 还没退就补一个 SIGKILL

所以「为什么推荐 15 而不是 9」在这里得到闭环:15 给应用时间做收尾,9 是最后的兜底。


7:为什么关掉终端,程序就没了?怎么让它不受影响?

考察点:SIGHUP · 会话与终端 · 四种脱离方式

关闭终端 → 内核给该终端的前台进程组发 SIGHUP → 程序默认被终止

四种免疫方式(按推荐度排):

# 1) 生产推荐:写成 systemd 服务(由 PID 1 托管,和终端无关,还能开机自启)
systemctl enable --now myservice

# 2) nohup:忽略 SIGHUP,输出重定向到 nohup.out
nohup ./run.sh &

# 3) disown:先后台,再从 shell 的任务表里摘掉
./run.sh & disown

# 4) setsid:新会话,彻底脱离终端
setsid ./run.sh
方式一句话
nohup让进程忽略 SIGHUP(关终端不死)
disown把任务从 shell 的任务表里摘掉
setsid新建会话(session),彻底脱离控制终端
systemd最稳:PID 1 托管、可开机自启、可自动重启、日志进 journald

面试加分:「临时调试用 nohup 就够;但生产上我会写成 systemd 服务,这样能开机自启、能设 Restart=on-failure、日志也统一进 journalctl。」