进程状态

1995 字
10 分钟
进程状态

1. 进程状态理论模型#

1.1 基本进程状态#

进程状态在操作系统中体现为 task_struct 结构体中的一个整数标志。理论模型通常包含以下状态:

状态描述触发条件
运行状态 (Running)进程正在CPU上执行被调度器选中
就绪状态 (Ready)进程已准备就绪,等待CPU时间片等待调度
阻塞状态 (Blocked)进程等待某事件(如I/O完成)发起I/O请求或等待资源
结束状态 (Terminated)进程已结束运行进程执行完毕或被终止

1.2 挂起状态#

当系统资源(尤其是内存)不足时,进程可能被交换到磁盘:

状态描述触发条件
就绪挂起进程在磁盘中,换入内存即可运行内存不足,被换出
阻塞挂起进程在磁盘中等待事件内存不足且进程阻塞

2. 进程状态的实际实现#

2.1 运行队列与等待队列#

  • 运行队列 (runqueue):CPU维护一个调度队列,包含所有处于**就绪(R)**状态的进程。调度器从这个队列中选择进程执行。

  • 等待队列 (wait_queue):每个设备(如键盘、磁盘、网卡)都有自己的等待队列,包含所有**阻塞(S/D)**在该设备上的进程。

运行队列 (runqueue)
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ task_struct │◄─►│ task_struct │◄─►│ task_struct │
│ (状态: R) │ │ (状态: R) │ │ (状态: R) │
└─────────────┘ └─────────────┘ └─────────────┘
磁盘等待队列 (disk wait_queue)
┌─────────────┐ ┌─────────────┐
│ task_struct │◄─►│ task_struct │
│ (状态: D) │ │ (状态: D) │
└─────────────┘ └─────────────┘
│ │
└──────────────────┘
▼
磁盘设备结构
struct device

2.2 状态变化的本质#

进程状态的变化,本质上就是进程在不同的队列(运行队列、设备等待队列)中进行流动。这最终归结为对数据结构的增删查改操作。

  • 运行 → 阻塞:进程从运行队列移除,加入某个设备的等待队列。

  • 阻塞 → 就绪:当设备就绪(如磁盘I/O完成),进程从设备等待队列移除,加入运行队列。

2.3 挂起与交换分区#

  • 当内存严重不足时,操作系统会将部分进程的数据和代码换出到磁盘的交换分区(swap)。

  • 被换出的进程处于挂起状态,其PCB可能保留在内存或也被换出。

  • 当需要时,操作系统再将进程换入内存。

3. Linux进程状态定义#

Linux内核中,进程状态用数组 task_state_array 表示:

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 - 僵尸状态 */
};
  1. R (running):

    • 进程正在运行或在运行队列中等待运行。

    • 注意:在Linux中,处于运行队列中等待调度的进程也显示为R状态。

  2. S (sleeping) - 可中断休眠:

    • 进程在等待某事件完成(如等待键盘输入、等待网络数据)。

    • 可以被信号中断(唤醒),是常见的阻塞状态。

  3. D (disk sleep) - 不可中断休眠:

    • 通常发生在等待磁盘I/O时。

    • 不可被信号中断,是保护磁盘数据完整性的关键机制。

    • 模拟方法:使用 dd 命令进行大量磁盘写入。

Terminal window
dd if=/dev/zero of=~/test.txt bs=4096 count=100000
  1. T (stopped) - 停止状态:

    • 进程被暂停,通常由信号 SIGSTOP (19) 引起。

    • 可通过信号 SIGCONT (18) 继续运行。

  2. t (tracing stop):

    • 进程被调试器暂停(如gdb设置断点)。

    • 是T状态的一种特殊形式。

  3. Z (zombie) - 僵尸状态:

    • 进程已终止,但其父进程尚未读取到子进程的退出状态信息。

    • 目的:为了让父进程能够获取子进程的退出信息(退出码、退出信号等)。

    • 危害:如果父进程一直不回收(不调用 wait()),僵尸进程会一直存在,导致内核资源(PCB)泄漏。虽然进程的代码和数据已被释放,但其 task_struct 结构体仍占用内核内存。

    • 常驻内存的进程如果产生僵尸且不被回收,危害更大。

  4. X (dead) - 死亡状态:

    • 进程最终状态,资源已完全回收。此状态瞬间存在,用户通常无法观察到。

3.1 孤儿进程#

  • 孤儿进程 (Orphan Process):在父子进程关系中,如果父进程先于子进程退出,子进程将成为孤儿进程。

  • 处理机制:操作系统会将孤儿进程**“领养”,通常由init进程(PID=1)** 或特定系统进程接管。

  • 为什么需要领养?

    1. 防止僵尸进程累积:如果父进程退出且子进程未被领养,子进程终止后将无人回收,成为永久的僵尸进程,导致系统资源泄漏。

    2. 维护进程树结构:确保所有进程都有父进程,便于统一管理。

  • 特性与影响

    • 变为后台进程:孤儿进程通常会成为后台进程,在终端关闭后仍可能继续运行。

    • 创建后台进程的方法:在命令后添加 & 符号,如 ./myprocess &。

4. Linux内核数据结构设计#

4.1 设备管理的“先描述,再组织”#

操作系统管理各种硬件设备(键盘、磁盘等)同样遵循“先描述,再组织”原则:

struct device {
int id; // 设备标识
int vendor; // 厂商信息
int type; // 设备类型
struct task_struct *wait_queue; // 等待该设备的进程队列
// ... 其他属性
};

所有设备通过链表组织,便于管理。

4.2 内核通用链表实现#

Linux内核使用一种巧妙的通用双向链表设计,实现数据结构的复用:

// 内核通用链表节点(只包含前后指针)
struct list_head {
struct list_head *next, *prev;
};
// 将链表节点嵌入到具体数据结构中
struct task_struct {
// 进程的各种属性...
int pid;
int status;
// ...
struct list_head tasks; // 链接到进程链表
};

优势:

  1. 类型无关:list_head 不包含具体数据类型,可嵌入任何结构体。

  2. 代码复用:一套链表操作函数(增、删、遍历)可用于所有嵌入 list_head 的结构体。

  3. 通过偏移量获取父结构:通过 list_head 指针,可以计算出其所在外层结构体(如 task_struct)的地址。

// 示例:从list_head指针获取包含它的task_struct指针
#define container_of(ptr, type, member) \
((type *)((char *)(ptr) - offsetof(type, member)))

5. 进程控制块的缓存管理#

5.1 Slab分配器#

内核使用Slab分配器管理 task_struct 等内核对象的分配与释放:

  • 目的:提高频繁创建和销毁进程时的性能。

  • 机制:维护一个 task_struct 对象的缓存池。

  • 优势:

    1. 避免频繁向物理内存申请释放带来的碎片和性能开销。

    2. 快速重用已释放的 task_struct 对象。

Slab缓存池
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ task_struct │ │ task_struct │ │ task_struct │
│ (空闲) │ │ (空闲) │ │ (空闲) │
└─────────────┘ └─────────────┘ └─────────────┘

6. 总结#

  1. 进程状态是进程生命周期中的关键属性,Linux通过具体状态(R、S、D、T、Z等)管理进程行为。

  2. 状态转换的本质是队列间的移动,进程在运行队列、设备等待队列间流转,这体现了“先描述,再组织”的管理思想。

  3. 僵尸进程(Z状态) 是已终止但未被父进程回收的进程,会导致内核资源泄漏,必须通过父进程调用 wait() 系列函数回收。

  4. 深度睡眠(D状态) 是不可中断的,主要保护磁盘I/O操作,确保数据完整性。

  5. Linux内核通过通用链表和Slab分配器等高效数据结构与内存管理机制,实现进程管理的性能优化。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Profile Image of the Author
HandlerOne
おかえり
公告
本站已整理 138 篇技术笔记,并按 8 条主线排成学习路线(141 站);站内搜索与桌宠对话均已上线。
分类
标签
最新动态
站点统计
文章
138
分类
12
标签
42
总字数
597,044
站点信息
构建平台
腾讯云 · Nginx
博客版本
v6.16.8
文章目录