Linux - 进程状态
文章目录
- 进程状态
- 进程状态概述
- 基本状态
- 挂起状态(为解决内存等资源限制引入,将部分进程暂时调出内存)
- 核心进程状态(5种基础状态)
- 进程状态查看
- 示例:
- Z(zombie)-僵⼫进程
- 僵⼫进程危害
- 孤儿进程
进程状态
进程状态概述
基本状态
- 创建状态:进程正在被创建,操作系统为其分配资源(如内存、PCB 等),初始化相关信息。
- 就绪状态:进程已具备运行条件,等待 CPU 调度执行。
- 运行状态:进程正在 CPU 上执行指令。
- 阻塞状态:进程因等待某些事件(如 I/O 完成、信号等)而暂停执行,直到事件发生后才能继续运行。
- 结束状态:进程已完成执行或被强制终止,操作系统回收其资源。
挂起状态(为解决内存等资源限制引入,将部分进程暂时调出内存)
- 挂起就绪状态:进程已具备运行条件,但因内存等资源限制被调出内存,等待重新调入内存后才能运行。
- 挂起阻塞状态:进程因等待某些事件而暂停执行,同时被调出内存,等待事件发生并重新调入内存后才能继续运行。
核心进程状态(5种基础状态)
状态标识 | 英文名称 | 中文名称 | 核心含义 |
---|---|---|---|
R | Running/Runnable | 运行 / 可运行态 | 进程正在 CPU 上运行,或已就绪等待 CPU 调度(处于内核的运行队列中) |
S | Interruptible Sleep | 可中断睡眠态 | 进程因等待可中断资源(如 I/O 完成、信号、等待锁释放)而暂停,收到信号后可被唤醒。 |
D | Uninterruptible Sleep | 不可中断睡眠态 | 进程因等待不可中断资源(如磁盘 I/O、硬件操作)而暂停,任何信号都无法唤醒,只能等待资源就绪。 |
T | Stopped/Trace | 停止 / 追踪态 | 进程被暂停(如收到SIGSTOP、SIGTSTP信号),或被调试器(如gdb)追踪(T标识)。 |
Z | Zombie | 僵尸态 | 进程已终止(代码执行完毕),但父进程未调用wait()/waitpid()回收其 PCB(进程控制块),内核仅保留少量信息(PID、退出状态)。 |
下⾯的状态在kernel源代码⾥定义:
static const char *const task_state_array[] = {"R (running)", /*0 */运行态"S (sleeping)", /*1 */可中断睡眠态"D (disk sleep)", /*2 */不可中断睡眠态"T (stopped)", /*4 */停止态"t (tracing stop)", /*8 */追踪态"X (dead)", /*16 */死亡态"Z (zombie)", /*32 */僵尸态
};
进程状态查看
ps aux / ps axj 命令
示例:
while :
dops axj | head -1 # 输出进程列表标题行ps axj | grep 进程名 # 筛选目标进程sleep 1 # 暂停1秒
done
示例:
- 查看R状态
int main()
{int number = 1;while (number++){/* code */}return 0;
}
- 查看S状态
int main()
{int numeber = 0;scanf("%d", &numeber); return 0;
}
- 查看T状态
在运行进程时按ctrl + z可观察到
Z(zombie)-僵⼫进程
- 僵尸状态(Zombies)是⼀个⽐较特殊的状态。当进程退出并且⽗进程没有读取到⼦进程退出的返回代码时就会产⽣僵⼫进程
- 僵尸进程会以终⽌状态保持在进程表中,并且会⼀直在等待⽗进程读取退出状态代码。
- 所以,只要⼦进程退出,⽗进程还在运⾏,但⽗进程没有读取⼦进程状态,⼦进程进⼊Z状态
示例代码:
int main()
{pid_t id = fork();if (id < 0){perror("fork");return 1;}else if (id > 0){ // parentprintf("parent[%d] is sleeping...\n", getpid());sleep(30);}else{printf("child[%d] is begin Z...\n", getpid());sleep(5);exit(EXIT_SUCCESS);}return 0;
}
僵⼫进程危害
- 进程的退出状态必须被维持下去,用于告诉父进程子进程的退出信息,⽗进程如果⼀直不读取,那⼦进程就⼀直处于Z状态
- 维护退出状态本⾝就是要⽤数据维护,也属于进程基本信息,所以保存在task_struct(PCB)。因此,Z状态⼀直不退出,PCB⼀直都要维护
- ⼀个⽗进程多个⼦进程,就是不回收,就会造成内存资源的浪费。因为数据结构对象本⾝就要占⽤内存。
- 内存泄露
孤儿进程
- 若父进程先于子进程退出,子进程会成为 “孤儿进程”。此时,操作系统会将孤儿进程交由 1 号进程(init或systemd,取决于系统版本)接管 —— 即 1 号进程成为孤儿进程的新父进程
- 当孤儿进程后续退出并进入僵尸状态(Z)时,由于其新父进程是 1 号进程,而 1 号进程会周期性地调用wait()系列函数回收所有子进程的资源,因此僵尸状态的孤儿进程会被 1 号进程及时清理,不会长期滞留系统中。
- 这种机制保证了即使原父进程异常退出,其子进程的资源也能被系统妥善回收,避免僵尸进程堆积。
代码示例:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{pid_t id = fork();if (id < 0){perror("fork");return 1;}else if (id == 0){ // childprintf("I am child, pid : %d\n", getpid());sleep(10);}else{ // parentprintf("I am parent, pid: %d\n", getpid());sleep(3);exit(0);}return 0;
}