进程生命周期
Linux是多任务操作系统,系统中的每个进程能够分时复用CPU时间片,通过有效的进程调度策略实现多任务并行执行。进程在被CPU调度运行,等待CPU资源分配以及等待外部事件时会处于不同的状态。进程状态如下:
- 创建状态:创建新进程;
- 就绪状态:进程获取可以运作所有资源及准备相关条件;
- 执行状态:进程正在CPU中执行操作;
- 阻塞状态:进程因等待某些资源而被跳出CPU;
- 终止状态:进程消亡;
[新建] → [就绪] ↔ [运行] → [终止]↑ ↓└── [阻塞] ←─┘
linux内核中进程状态
内核进程状态定义如下。
/* Used in tsk->state: */
#define TASK_RUNNING 0x0000 // 运行或就绪
#define TASK_INTERRUPTIBLE 0x0001 // 可中断睡眠,也叫轻度睡眠,可被信号或资源就绪唤醒
#define TASK_UNINTERRUPTIBLE 0x0002 // 深度睡眠,仅由资源就绪唤醒(不可被信号中断)
#define TASK_KILLABLE (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE) // 0x0102,中度睡眠,能被kill信号打断
#define __TASK_STOPPED 0x0004
#define __TASK_TRACED 0x0008
/* Used in tsk->exit_state: */
#define EXIT_DEAD 0x0010 // 进程完全终止(资源已回收)
#define EXIT_ZOMBIE 0x0020 // 进程已终止,但父进程未回收其资源(wait() 未调用)
#define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD)
进程状态查询方法
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">ps</font>**
/**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">top</font>**
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">cat /proc/<PID>/status</font>**
<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">ps</font>
/<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">top</font>
显示的进程状态
符号 | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">ps</font>** /**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">top</font>** 状态 | 对应内核状态 | 场景描述 |
---|---|---|---|
R | Running | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">TASK_RUNNING</font>** | 进程正在运行或就绪(等待CPU调度)。 |
S | Sleeping | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">TASK_INTERRUPTIBLE</font>** | 可中断睡眠 |
D | Uninterruptible | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">TASK_UNINTERRUPTIBLE</font>** | 不可中断睡眠 |
T | Stopped | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">__TASK_STOPPED</font>** | 进程被暂停(如收到**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SIGSTOP</font>** 、**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SIGTSTP</font>** 信号)。 |
t | Traced | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">__TASK_TRACED</font>** | 进程被调试器(如gdb)追踪(通常在断点处暂停)。 |
Z | Zombie | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">EXIT_ZOMBIE</font>** | 僵尸进程(已终止但父进程未回收资源)。 |
X | Dead | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">EXIT_DEAD</font>** (极少在 **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">ps</font>** 中看到) | 进程完全终止(资源已回收,通常瞬间状态)。 |
I | Idle | (内核线程的特殊标记) | 内核空闲线程。 |
僵尸进程
进程已终止执行,但其父进程尚未调用 **wait()**
系统调用来回收它的资源(主要是退出状态信息)。 这样的进程称为僵尸进程。
进程状态为 Z
(Zombie)。
进程已经执行完成,但是没有释放pid,task_struct等资源。
系统pid资源是有限的,大量僵尸进程会耗尽系统pid。
下面是僵尸进程测试代码。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main() {pid_t pid = fork();if (pid == 0) {printf("子进程退出\n");exit(0); // 子进程终止} else {sleep(60); // 父进程没有调用 wait()}return 0;
}
结果如下。
root@VM:~$ ps aux | grep zom
root 10106 0.0 0.0 2364 576 pts/4 S+ 17:20 0:00 ./zombie_process_test
root 10107 0.0 0.0 0 0 pts/4 Z+ 17:20 0:00 [zombie_process_] <defunct>
孤儿进程
进程的父进程已经提前退出,而该进程仍然在运行。 这种进程称为孤儿进程。
孤儿进程执行完后会被init进程回收。一般不会产生危害。
下面是孤儿进城的测试代码。
#include <stdio.h>
#include <unistd.h>int main() {pid_t pid = fork();if (pid == 0) {sleep(60); // 子进程继续运行printf("我是子进程,父进程已经退出\n");} else {printf("父进程退出\n");exit(0); // 父进程终止}return 0;
}
参考资料
- Professional Linux Kernel Architecture,Wolfgang Mauerer
- Linux内核深度解析,余华兵
- Linux设备驱动开发详解,宋宝华
- linux kernel 4.12