4.1.2 进程生命周期与状态转换
进程生命周期描述了进程从创建到退出的完整路径,核心在于状态转换及其触发条件。理解这些状态有助于定位运行异常、资源占用及僵尸/孤儿问题。
1. 进程的主要状态#
- 新建(New):进程被创建但尚未进入就绪队列。
- 就绪(Ready):进程具备运行条件,等待 CPU 调度。
- 运行(Running):进程占用 CPU 正在执行。
- 阻塞/睡眠(Blocked/Sleeping):等待事件或资源(I/O、锁、信号等)。
- 停止(Stopped):被信号暂停(如
SIGSTOP、SIGTSTP)。 - 退出(Terminated/Zombie):进程执行完成或被终止,进入僵尸等待回收。
2. 状态转换路径与触发条件#
- 新建 → 就绪:
fork()/clone()完成,内核初始化完成。 - 就绪 → 运行:被调度器分配到 CPU。
- 运行 → 就绪:时间片耗尽或更高优先级进程抢占。
- 运行 → 阻塞:等待 I/O、互斥锁、条件变量或资源。
- 阻塞 → 就绪:I/O 完成、锁释放或信号到达。
- 运行 → 停止:收到停止信号或调试器挂起。
- 停止 → 就绪:收到
SIGCONT继续执行。 - 运行 → 退出:正常
exit()或异常终止(SIGKILL)。
3. 命令示例与状态观测#
以下示例可直接在 Linux 终端执行,用于观察状态变化:
# 1) 创建一个子进程并让其进入睡眠(S)
sleep 300 &
echo "sleep pid=$!"
# 2) 查看进程状态(STAT 字段)
ps -o pid,ppid,stat,cmd -p $!
# 3) 发送停止信号,观察 T 状态
kill -STOP $!
ps -o pid,ppid,stat,cmd -p $!
# 4) 继续运行,观察返回 S
kill -CONT $!
ps -o pid,ppid,stat,cmd -p $!
# 5) 强制结束,观察是否产生 Z(需父进程不回收)
kill -9 $!
ps -o pid,ppid,stat,cmd | grep Z
命令说明:
- ps -o pid,ppid,stat,cmd -p PID:按指定字段输出;stat 含状态码。
- kill -STOP/-CONT:停止/恢复进程。
- kill -9:强制终止进程(SIGKILL),可能产生僵尸。
4. 僵尸与孤儿示例与排错#
4.1 僵尸进程复现与定位#
// /tmp/zombie.c
#include <unistd.h>
#include <stdlib.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
_exit(0); // 子进程立即退出
} else {
sleep(300); // 父进程不 wait,产生僵尸
}
return 0;
}
# 编译与运行(需要 gcc)
gcc /tmp/zombie.c -o /tmp/zombie
/tmp/zombie &
# 查找 Z 状态
ps -eo pid,ppid,stat,cmd | grep Z
排错步骤:
1. ps -eo pid,ppid,stat,cmd | awk '$3 ~ /Z/ {print}' 定位僵尸 PID。
2. pstree -p <PPID> 查看父子关系。
3. 处理方式:重启父进程或让父进程正确 wait()。
4.2 孤儿进程验证#
# 开一个新终端,在其中运行
( sleep 600 & echo "child=$!" ) && exit
# 回到原终端,查看是否被 init/systemd 接管
ps -o pid,ppid,cmd -p <child_pid>
# 预期:PPID 变为 1
5. 运维排查要点与命令解释#
# 观察系统中 D 状态进程(不可中断睡眠)
ps -eo pid,ppid,stat,cmd | awk '$3 ~ /D/ {print}'
# 结合 top 定位高负载阻塞
top -H -p <PID> # -H 展示线程级别
# 追踪父子树
pstree -p | less
解释:
- D 状态常见于磁盘/网络 I/O 卡顿。
- top -H 能查看线程级别状态,适合定位阻塞线程。
6. 最佳实践#
- 服务程序中正确处理
SIGCHLD并回收子进程。 - 对外部依赖设置超时,避免长时间阻塞。
- 高并发场景使用进程池/线程池减少频繁 fork/exit。
7. 练习题#
- 观察一个
sleep进程从S到T再回S的状态变化,并记录stat。 - 编写或运行
zombie.c,用ps与pstree定位僵尸。 - 使用
top -H -p观察一个多线程程序的线程状态差异。