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)'
| 状态 | 含义 | 常见原因 |
|---|---|---|
| R | Running / Runnable(在跑或在等 CPU) | 正常 |
| S | Interruptible sleep(可中断睡眠) | 绝大多数进程的常态:等 IO、等事件 |
| D | Uninterruptible sleep(不可中断睡眠) | 卡在磁盘/NFS/驱动的 IO 上,不响应信号 |
| T | Stopped(暂停) | 收到 SIGSTOP / Ctrl+Z;被调试器停住 |
| Z | Zombie(僵尸) | 子进程已退出,父进程没 wait 回收 |
| I | Idle 内核线程 | 基本不用管 |
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。」