云计算百科
云计算领域专业知识百科平台

从理论到实践:操作系统进程状态的核心逻辑与 Linux 实现

前言

在操作系统的世界里,进程的 “生老病死” 并非随机无序,而是被一套精密的状态管理机制所调控。无论是程序从启动到运行的 “活跃期”,因等待资源而进入的 “阻塞态”,还是因内存紧张被暂时换出的 “挂起态”,这些状态的切换背后,藏着操作系统对资源调度、设备管理的底层逻辑。

本文将从理论与实践两个维度拆解进程状态:先从操作系统的通用模型出发,解析 “运行、阻塞、挂起” 三大基础状态的本质 —— 如何通过 PCB(进程控制块)在不同队列间的移动实现状态切换,以及进程调度与设备管理如何通过结构体和队列协同工作;再聚焦 Linux 系统,详细列举其特有的进程状态(如 R、S、D、Z、T 等),结合实例说明每种状态的触发场景、内核逻辑及观察方法。

无论你是想理解操作系统的调度原理,还是想搞懂 Linux 中 “僵尸进程”“不可中断睡眠” 等具体问题,这篇文章都将为你搭建起从抽象理论到具体实现的桥梁,让你对进程状态的认知从 “零散概念” 升华为 “体系化逻辑”。

目录

进程状态(操作系统)

运行&&阻塞&&挂起(概念)

✅ 1、进程调度与设备管理

本质:结构体与队列管理

🧠 2、PCB:进程控制块是调度核心

🏃‍♂️ 3、运行队列 runqueue

💡 4、状态切换的本质:PCB在不同队列之间移动

🖥️ 5、设备管理结构体 device 与等待队列

🔁 6、状态转换流程图

 Linux进程状态

状态列举

详细解释与实例


进程状态(操作系统)

        为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。个进程可以有几个状态(在Linux内核里,进程有时候也叫做任务)。

       进程的状态决定了系统该如何处理进程。在操作系统里,可以用#define,宏定义来定义进程的状态,在task_struct结构体定义整形变量来对应状态。

进程状态就是task_struct中的一个整数。

        

运行&&阻塞&&挂起(概念)

CPU运行一个进程,先找到PCB,通过PCB找到进程对应的代码和数据。一个CPU。有一个调度队列。
调度队列就是进程状态为 ready 或 running 的“候选人名单”,它决定谁可以被 CPU 执行,而调度器就是挑选“下一个上场的选手”的裁判。

运行队列sruct struct*runqueue,里面有一个个PCB结点。所以PCB既在全局链表也在运行队列中。

运行:进程在调度队列中,进程的状态就是running。

阻塞:等待某种设备或者资源就绪。

操作系统对计算机的软硬件资源进行管理。像键盘,显示器,网卡等设备。操作系统像对这些硬件资源进行管理–先描述在组织。一个一个结构体device串联起来。每个device里面有个等待队列。

挂起:CPU,内存资源比较紧时,将进程的代码和数据放到外设磁盘交换分区中,腾出可用资源。(阻塞挂起和就绪挂起)

✅ 1、进程调度与设备管理

本质:结构体与队列管理

我们可以把操作系统的运行抽象为:

“通过维护多个队列和结构体,实现对 CPU、内存、设备等资源的调度与控制”


🧠 2、PCB:进程控制块是调度核心

struct task_struct {
pid_t pid;
long state; // 进程状态:running、sleeping 等
unsigned long flags;
struct mm_struct *mm; // 指向进程地址空间的指针
struct task_struct *parent;
struct list_head run_list; // 运行队列用的链表节点

};

✳ 位置:

  • 所有 PCB 放在一个全局链表中,便于进程查找和管理

  • 同时活跃进程还会被挂入运行队列 runqueue中,供调度器选择运行


🏃‍♂️ 3、运行队列 runqueue

struct runqueue {
struct task_struct *curr; // 当前正在运行的任务
struct list_head queue; // 任务链表
int nr_running; // 正在运行的任务数
};

  • 操作系统调度器从 runqueue 里选出下一个运行进程,赋值给 CPU 执行。

  • 所以 “运行状态” = 在运行队列中 + 被 CPU 选中执行中


💡 4、状态切换的本质:PCB在不同队列之间移动

状态描述数据结构变化
running 占用 CPU 正在运行 PCB 在 runqueue 中,且被调度执行
ready 有资格运行但暂未被调度 PCB 在 runqueue 中等待 CPU
sleeping 等待 I/O(如键盘、网卡等) PCB 被挂到设备的 wait_queue 上
zombie 运行结束但父进程未回收资源 PCB 保留在进程表中
stopped 被暂停 被挂到某个暂停状态队列(如 ptrace)

🖥️ 5、设备管理结构体 device 与等待队列

struct device {
int id;
int status; // 是否就绪
void *data; // 设备私有数据
struct device *next; // 串联所有设备
int type; // 类型:键盘、网卡…
struct task_struct *wait_queue; // 等待此设备的进程队列
};

设备访问流程:

  • 进程访问设备但设备未就绪

  • 系统将当前 PCB 加入设备的 wait_queue

  • 将进程状态设为 sleeping

  • 当设备就绪,唤醒 wait_queue 上的进程 → 重新进入 runqueue


  • 🔁 6、状态转换流程图

    +———-+ block (I/O wait) +————+
    | running | ———————–> | sleeping |
    +———-+ +————+
    |
    | preempt/yield
    v
    +———-+ wakeup +————+
    | ready | <———————- | waiting |
    +———-+ +————+
    |
    | schedule()
    v
    +———-+
    | running |
    +———-+

    ✅ 操作系统本质上是:

    “通过结构体组织资源,通过队列调度进程,通过状态切换实现多任务并发。”

    进程状态的变化表现之一就是要在不同的队列中进行流动,本质就是就是数据结构的增删查改。把PCB在两个队列中来回串。

     Linux进程状态

    状态列举

    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 */
    };

    R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么运行中要么在运行队列里。

    S睡眠状态(sleeping): 意味着进程在等待事件完成(这⾥的睡眠有时候也叫做可中断睡眠 (interruptible sleep))。

    D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个 状态的进程通常会等待IO的结束。

    T停⽌状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停⽌(T)进程。这个被暂停的 进程可以通过发送 SIGCONT 信号让进程继续运⾏。

    X死亡状态(dead):这个状态只是⼀个返回状态,你不会在任务列表⾥看到这个状态。

     

    详细解释与实例

    有+是进程是在前台出现,在命令行。没有+是在后台进行的。

     while :; do ps axj | head -n 1; ps axj | grep -v grep | grep code; sleep 1; done

     grep -v grep 是一个常用的过滤技巧,用于排除包含 grep 自身的进程。

     

    ./code &(在后面加&) 

    等待键盘输入,S阻塞sleeping   可中断休眠,浅度睡眠,可以杀掉

    T,t暂停例子

    debug下,gdb对程序断点调试,t进行追踪

     ctrl Z 暂停程序

     

     D 深度休眠,代表进程正在等待某个 I/O 操作(如磁盘读写、网络请求、外设交互等)完成,且在此期间无法被中断或杀死。该状态不做演示,一般都是高IO操作,若出现D,就要检查磁盘等设备了。

    Z僵尸进程,为了获取退出信息。

    在Linux系统里,所有的进程是某个进程的子进程,要么是bash的,要么是自己的,创建子进程的目的,是为了让子进程完成某种事情的。父进程就需要知道完成的结果的相关信息,子进程退出不能释放所有资源,会保留最小化的 PCB 信息,退出的信息暂时维持住,在子进程退出之后,父进程获取信息之前,就要有一个状态就是Z状态。

    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    int main(){
    pid_t id=fork();
    if(id<0){
    perror("fork");
    }
    else if(id==0){
    int count=5;
    while(count–){
    printf("我是一个子进程,pid=%d,count:%d.\\n",getpid(),count);
    sleep(1);
    }
    }

    else{
    while(1){
    printf("我是一个父进程,pid=%d\\n",getpid());
    sleep(1);
    }
    }

    return 0;
    }

    当子进程在 5 秒后退出时,由于父进程没有调用wait()或waitpid()来回收子进程的退出状态,子进程会变成僵尸进程(Z 状态)。

    如果父进程一直不管,不回收,不获取子进程的退出信息,那么Z一直存在,会引起内存泄漏问题。

     进程退出了,内存泄漏问题还在不在?自己代码引起的内存泄漏不存在了。

    什么样的进程具有内存泄漏问题是比较麻烦的?常驻内存的进程 

    结束语

    进程状态的管理,本质上是操作系统对 “有限资源” 与 “无限需求” 的动态平衡艺术。从理论层面的 “运行、阻塞、挂起” 模型,到 Linux 系统中精细的状态划分(如 D 状态对 I/O 原子性的保护、Z 状态对父子进程通信的支持),每一种状态的设计都承载着特定的功能目标 —— 既要保证进程高效运行,又要避免资源竞争与数据不一致。

    理解进程状态,不仅是掌握 “查看状态”“分析问题” 的实用技能,更能帮我们透过现象看本质:操作系统如何通过 PCB 和队列 “操控” 进程的生命周期?不同状态的转换如何体现 “调度优先” 与 “资源依赖” 的权衡?这些思考,将为我们深入学习进程调度算法、内存管理、设备驱动等知识打下坚实基础。

    如果你在实践中遇到了特殊的进程状态问题(如长期 D 状态的排查、僵尸进程的清理),不妨回头再梳理这些理论逻辑 —— 答案往往就藏在状态转换的底层原理中。欢迎在评论区分享你的实践案例,让我们一起在 “理论指导实践,实践反哺理解” 的循环中,深化对操作系统的认知。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 从理论到实践:操作系统进程状态的核心逻辑与 Linux 实现
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!