内核编程十三:进程状态详解
进程如同数字世界中的生命体,诞生时被系统母体赋予初始资源,在CPU的脉搏中呼吸,于内存的疆域里生长。它睁开线程之眼观察世界,伸出系统调用之手与环境互动,时而如幼童般单纯执行指令,时而如哲人般陷入阻塞沉思。当时间片如春雨洒落便舒展枝桠,遭遇死锁风暴则蜷缩休眠。最终在exit的凋零中归还所有记忆,化作进程列表里一道转瞬即逝的微光,完成从二进制胚胎到硅基消亡的完整轮回。
内核定义的进程状态
在 include/linux/sched.h 文件中对进程的状态进行了具体的定义,如下所示:
/* Used in tsk->state: */
#define TASK_RUNNING 0x0000
#define TASK_INTERRUPTIBLE 0x0001
#define TASK_UNINTERRUPTIBLE 0x0002
#define __TASK_STOPPED 0x0004
#define __TASK_TRACED 0x0008
/* Used in tsk->exit_state: */
#define EXIT_DEAD 0x0010
#define EXIT_ZOMBIE 0x0020
#define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD)
/* Used in tsk->state again: */
#define TASK_PARKED 0x0040
#define TASK_DEAD 0x0080
#define TASK_WAKEKILL 0x0100
#define TASK_WAKING 0x0200
#define TASK_NOLOAD 0x0400
#define TASK_NEW 0x0800
/* RT specific auxilliary flag to mark RT lock waiters */
#define TASK_RTLOCK_WAIT 0x1000
#define TASK_STATE_MAX 0x2000
这些状态可以分为以下三类 :
1. 运行与睡眠状态
状态 | 值(十六进制) | 描述 |
---|---|---|
TASK_RUNNING | 0x0000 | 进程正在运行(CPU 执行)或就绪(在运行队列中等待调度)。 |
TASK_INTERRUPTIBLE | 0x0001 | 可中断睡眠:进程因等待资源(如 I/O、信号量)被阻塞,但可被信号或事件唤醒。 |
TASK_UNINTERRUPTIBLE | 0x0002 | 不可中断睡眠:进程因关键操作(如磁盘 I/O)被阻塞,不可被信号唤醒。需谨慎使用。 |
__TASK_STOPPED | 0x0004 | 进程被信号(如 SIGSTOP 、SIGTSTP )暂停,直到收到 SIGCONT 恢复。 |
__TASK_TRACED | 0x0008 | 进程被调试器(如 ptrace )跟踪,通常与 STOPPED 联合出现。 |
TASK_PARKED | 0x0040 | 进程主动休眠(如内核线程),需通过特定唤醒机制恢复。 |
TASK_DEAD | 0x0080 | 进程正在结束,资源尚未完全释放(与 EXIT_DEAD 配合使用)。 |
TASK_WAKEKILL | 0x0100 | 附加标志:允许致命信号(如 SIGKILL )唤醒处于 INTERRUPTIBLE/UNINTERRUPTIBLE 的进程。 |
TASK_WAKING | 0x0200 | 临时状态:进程正在从睡眠状态转移到运行队列。 |
状态转换
-
运行 → 睡眠:因等待资源(如
TASK_INTERRUPTIBLE
)或主动暂停(如__TASK_STOPPED
)。 -
睡眠 → 运行:被事件唤醒(如 I/O 完成)或收到信号(如
SIGCONT
)。
不可中断睡眠(UNINTERRUPTIBLE)
常见于磁盘 I/O 或内核关键路径,无法通过 kill
终止,需解决底层阻塞问题。
调试相关状态
__TASK_TRACED
和 __TASK_STOPPED
用于调试器(如 gdb
)或 shell 作业控制(如 jobs
)。
2. 退出状态
状态 | 值(十六进制) | 描述 | |
---|---|---|---|
EXIT_ZOMBIE | 0x0020 | 僵尸进程:进程已终止,但父进程尚未通过 wait() 回收其资源(如 PID、退出状态)。 | |
EXIT_DEAD | 0x0010 | 彻底退出:进程资源已完全释放,是退出过程的最终状态。 | |
EXIT_TRACE | 0x0030 | 被调试进程的退出状态(`ZOMBIE | DEAD`)。 |
退出流程:进程终止后先变为 EXIT_ZOMBIE
,父进程回收后转为 EXIT_DEAD
。
僵尸进程:若父进程未调用 wait()
,子进程会长期保持 EXIT_ZOMBIE
,占用内核资源。
3. 其他辅助状态
状态 | 描述 |
---|---|
TASK_NOLOAD | 进程不参与系统负载计算(如某些内核线程)。 |
TASK_NEW | 进程刚创建,尚未完全初始化。 |
TASK_RTLOCK_WAIT | 进程正在等待实时(RT)互斥锁(用于实时调度策略)。 |
结构体中状态的定义
struct task_struct
中的 unsigned int __state
成员是用来标记进程状态的核心字段,它存储了进程的当前状态标志(如 TASK_RUNNING
、TASK_INTERRUPTIBLE
等)
struct task_struct {
...
unsigned int __state;
...
}
打印进程状态(内核态)
通过 current 宏打印当前进程的状态,再通过dmesg进行查看
struct task_struct *current_task = current;
printk(KERN_INFO "Current process state: %d\n", current_task->__state);
用户态进程状态
ps
通过解析 /proc/<pid>/status
或 /proc/<pid>/stat
获取进程的状态,显示为单字母缩写,仅反映内核状态的子集:
ps 状态 | 含义 | 对应的内核状态 |
---|---|---|
R | Running (运行/就绪) | TASK_RUNNING |
S | Interruptible Sleep | TASK_INTERRUPTIBLE (可中断睡眠) |
D | Uninterruptible Sleep | TASK_UNINTERRUPTIBLE (不可中断睡眠,通常因等待磁盘 I/O 或内核锁) |
Z | Zombie | EXIT_ZOMBIE |
T | Stopped | __TASK_STOPPED (如通过 SIGSTOP 暂停) |
t | Tracing stop | __TASK_TRACED (被调试器跟踪,如 gdb ) |
X | Dead (已退出) | EXIT_DEAD (极少在 ps 中看到,因进程已完全退出) |
I | Idle (空闲线程) | 内核线程的特殊状态(无直接对应标志,可能结合 TASK_RUNNING 和 TASK_NOLOAD ) |
P / W | Parked / Waking | TASK_PARKED 或 TASK_WAKING (某些内核版本支持) |
内核态 VS 用户态
维度 | 内核状态 | ps 状态 |
---|---|---|
表示方式 | 位掩码(多状态组合) | 单字母缩写(互斥状态) |
粒度 | 细粒度(支持复合状态) | 粗粒度(简化映射) |
退出状态 | 明确区分 EXIT_ZOMBIE 和 EXIT_DEAD | 仅显示 Z (僵尸)或 X (彻底退出极少见) |
调试状态 | 单独标记 __TASK_TRACED | 显示为 t (与 T 区分) |
实时状态 | 支持 TASK_RTLOCK_WAIT 等扩展标志 | 无直接对应 |
用户友好性 | 面向内核开发者 | 面向系统管理员/开发者 |
-
内核状态:丰富、细粒度,用于调度和资源管理。
-
ps
状态:简化、用户友好,覆盖常见场景。 -
关键区别:
ps
隐藏了复合状态和部分扩展标志(如TASK_WAKING
),但足以满足日常监控需求。
示例分析
场景 1:进程因磁盘 I/O 阻塞
-
内核状态:
__state = TASK_UNINTERRUPTIBLE | TASK_NOLOAD
(不可中断睡眠 + 不计入负载) -
ps
显示:
D
(仅体现不可中断睡眠)
场景 2:被调试的暂停进程
-
内核状态:
__state = __TASK_STOPPED | __TASK_TRACED
-
ps
显示:
t
(优先显示TRACED
,而非T
)
场景 3:僵尸进程
-
内核状态:
exit_state = EXIT_ZOMBIE
,__state = TASK_DEAD
-
ps
显示:
Z