进程状态:Linux的幕后指挥管理,穿越操作系统进程的静与动

发布于:2025-03-29 ⋅ 阅读:(28) ⋅ 点赞:(0)


在这里插入图片描述

引言:进程的生命与状态:动与静的交响曲

每一个进程都拥有自己的生命轨迹,它们在CPU的调度下,按照某种规律从一个状态转变到另一个状态。这些状态是进程在操作系统内存空间中的各种“存在形式”,是它们生命周期的不同表现,承载着操作系统对每个进程的管理与调度。

在Linux这个庞大而复杂的宇宙中,每个进程如同宇宙中的星辰,承载着数据流动与信息处理的重任。而它们的生命周期,却不仅仅是简单的“开始”与“结束”。在这无尽的天际,进程的状态不断变化,如同风中的细沙,或稳静、或急躁、或急促、或安逸。今天,我们将一同探索,深入理解Linux操作系统中进程状态的神秘面纱。

一、操作系统中的进程状态概述

操作系统中的进程状态是进程在生命周期中可能处于的不同状态。这些状态帮助操作系统识别进程的运行情况,并在不同状态间进行合理的资源分配

操作系统中的经典进程状态包括就绪运行阻塞终止

1.1 经典的进程状态模型

典型的操作系统中,进程状态可以分为以下几种:
在这里插入图片描述
在这种经典模型中,进程会在不同状态之间流转。具体来说:

就绪 -> 运行:CPU调度器分配CPU给就绪状态的进程。
运行 -> 阻塞:进程等待I/O或其他资源时进入阻塞状态。
阻塞 -> 就绪:等待的条件满足后,进程重新进入就绪队列,等待CPU调度。
运行 -> 终止:进程执行完毕或异常终止,进入终止状态。

1.2 进程状态转换图

在这里插入图片描述
该图展示了一个经典进程状态的转换流程。箭头表示进程状态转换的可能路径。

二、Linux操作系统中的进程状态

Linux操作系统在经典的进程状态基础上进行了一系列扩展,允许内核更细粒度地控制进程,尤其是当系统资源紧张或多任务并发性很高时。Linux内核中的进程状态可以使用ps命令或读取/proc文件系统来查看进程的状态信息。

2.1 Linux进程状态的分类

在这里插入图片描述
在进程的task_struct结构体中,state字段用来表示进程的当前状态。根据内核中的定义,不同状态的进程会被挂载在不同的等待队列上,以实现细粒度的调度与控制。

而在我们 Linux 中,新建就绪运行都可以看作 运行 R 这一个状态,所以比较清晰

在这里插入图片描述

2.2 各状态的详细解释

  • TASK_RUNNING:进程正在运行或准备运行。它可以被调度器分配到CPU执行。TASK_RUNNING的进程始终是就绪队列的一部分。

  • TASK_INTERRUPTIBLE:进程处于可中断等待状态,等待某一条件满足。进程在此状态下可以被信号唤醒。(关于信号的知识我们在后面会讲)

  • TASK_UNINTERRUPTIBLE:进程处于不可中断的等待状态。这通常用于等待特定资源(例如设备I/O操作完成)。进程在此状态下不会被信号打断,即便外部发送信号也不会响应。

  • TASK_STOPPED:进程被暂停,通常是由于接收到SIGSTOP信号或调试器干预,等待继续或恢复信号。

  • TASK_TRACED:进程被调试工具(如gdb)跟踪和控制。此状态下的进程会暂停,直到调试器进一步控制。

  • EXIT_DEAD:进程终止后进入清理阶段,等待系统回收资源

  • EXIT_ZOMBIE:进程已结束,系统未回收其资源。僵尸进程会保留在系统中,直到其父进程调用wait()系统调用收集它的退出状态。

  • TASK_DEAD:表示进程已彻底结束,系统已回收其所有资源。

2.3 Linux进程状态表

在这里插入图片描述

2.4 使用ps查看进程状态

在Linux系统中,可以通过ps命令查看进程的状态:

ps -aux

在这里插入图片描述
ps命令会显示每个进程的详细信息,其中状态列标记着每个进程的状态。状态的含义如下:

  • R : 运行或就绪状态
  • S:可终止等待
  • D: 不可终止等待
  • Z: 僵尸进程,等待回收
  • X:终止死亡进程
  • T: 停止状态

我们也可以通过ps命令查看某个指定的进程的信息:

ps axj | grep 进程名字

三、进程状态具体分析

🖋️运行 R

首先来看看第一种状态 R
以我们以往的认知来说,一个程序在运行就表示该 进程 处于 运行 状态,那么事实真的如此吗?

先来看看下面这段代码:

#include<iostream>
using namespace std;

#include<unistd.h>
#include<sys/types.h>

int main()
{
  while(1)
  {
    cout << "I'm a process, my PID is:" << getpid() << endl;
    sleep(1);
  }
  return 0;
}

当前makefile文件为:

myProcess:test.cpp
	g++ -o myProcess test.cpp

.PHONY:catPI
catPI:
	ps ajx | head -1 && ps ajx | grep myProcess | grep -v grep 

.PHONY:clean
clean:
	rm -r myProcess

通过 make catPI 指令调用 Makefile 中提前设定好的指令,查看当前进程信息
在这里插入图片描述
可以看到当前的进程状态为 睡眠 S+
注: + 表示当前进程在前台运行中

进程 难道没有运行吗?

运行了,但我们 很难捕捉到 对于 CPU 来说,将这么简单的一句话输出到屏幕上是一件很小的事,可能几毫秒就完成了 而其他大多数时间,进程
都在外设等待队列中 排队 当我们将打印语句和睡眠语句屏蔽后,进程 不用在等待队列中 排队, CPU 就一直在处理死循环,此时可以观察到 运行R 状态

在这里插入图片描述

🖋️睡眠 S

睡眠 S 的本质就是 进程阻塞表示此时进程因等待某种资源而暂停运行

睡眠 S 又称为可中断休眠,当 进程 等待时间过长时,我们可以手动将其关闭,应用卡死后强制关闭也是这个道理

在这里插入图片描述
还有一种方式终止进程:kill

  • kill -9 PID 终止进程,当进程在后台运行时(状态不加 +),我们是无法通过 ctrl+c 终止的,但kill指令可以终止

🖋️休眠 D

还存在一种特殊睡眠状态 休眠 D休眠 又被称为不可中断休眠,顾名思义,休眠 D 状态下的 进程 是无法终止的,kill 指令和 OS都无能为力,只能默默等待 进程阻塞 结束,拿到资源了,进程 才会停止 休眠 D 状态

终止 休眠 D 进程的一个方法就是切断电源,此时进程是结束了,但整个系统也结束了

🖋️暂停 T

我们还可以使 进程 进入 暂停 T 状态

  • kill -19 PID 暂停进程
  • kill -18 PID 恢复进程

在输入暂停指令后,进程进进入暂停状态

在这里插入图片描述
在这里插入图片描述
我们可以通过 kill -18 PID 使 进程 恢复运行,恢复后的 进程 在后台运行

可以看到,恢复后进程继续执行,并且状态由T变为S
在这里插入图片描述
注意: 进程 在后台运行时,是无法通过 ctrl+c·指令终止的,只能通过 kill -9 PID 终止

在 gdb 中调试代码时,打断点实际上就是 使 进程 在指定行暂停运行,此时 进程 处于 追踪暂停状态 t

在这里插入图片描述
在这里插入图片描述

🖋️死亡 X

当进程被终止后,就处于 死亡 X 状态

死亡状态是无法在任务列表中观察到的,死亡 X 状态只是一个返回状态

🖋️僵尸 Z

与死亡状态相对应的还有一个 僵尸 Z 状态

  • 通俗来说,僵尸状态 是给 父进程 准备的
  • 当 子进程 被终止后,会先维持一个 僵尸 状态,方便 父进程 来读取到 子进程 的- 退出结果,然后再将 子进程 回收
  • 单纯的在 bash 环境下终止 子进程,是观察不到 僵尸状态 的,因为 bash 会执行回收机制,将 僵尸 回收
  • 我们可以利用 fork() 函数自己创建 父子进程 关系,观察到这一现象

代码示例如下:

#include<iostream>
using namespace std;

#include<unistd.h>
#include<sys/types.h>

int main()
{
  pid_t ret = fork();
  if(ret == 0)
  {
    while(1)
    {
      cout << "I'm son process, my PID: " << getpid() << " PPID: " << getppid() << endl;
      sleep(1);
    }
  }
  else if(ret > 0)
  {
    while(1)
    {
      cout << "I'm father process, my PID: " << getpid() << " PPID: " << getppid() << endl;
      sleep(1);
    }
  }
  else
  {
    while(1)
    {
      cout << "Make son process fail!" << endl;
      sleep(1);
    }
  }

  return 0;
}


此时进程开始执行
在这里插入图片描述
此时父子进程都处于常规睡眠状态
在这里插入图片描述
此时输入指令 kill -9 PIDkill -9 1007716终止 子进程
再次查看进程状态:
在这里插入图片描述
僵尸进程如果不被回收,会导致内存泄漏问题和标识符占用问题

📖孤儿进程

孤儿进程是一种特殊的进程状态

  • 通过程序创建 父子进程
  • 通过指令终止 父进程,此时 子进程 会被OS领养
  • 子进程 的 父进程 变为 1号进程
  • 子进程 就变成了一个 孤儿进程

同样执行上述代码,起初都正常运行
在这里插入图片描述
现在我们消灭父进程,观察其执行结果和运行状态
在这里插入图片描述
可以看到子进程被1号进程领养
在这里插入图片描述
假设 子进程 不被 1号进程 领养

  • 子进程 退出时就会无人回收,成为一只游离的僵尸
  • 僵尸进程 有 内存泄漏 的风险
  • 因此 子进程 会被OS领养

总结:进程状态的舞蹈

Linux操作系统中的进程状态,不仅仅是抽象的计算机术语,它们是系统中每个进程生命周期的具体体现。正如一场精彩的舞蹈,每个进程都有自己的节奏和步伐,它们或快速跃动,或缓慢舞动,或瞬间消失,而每个转变都与操作系统对资源的调度和管理息息相关。

通过理解进程状态与状态转变的过程,我们能够更好地把握Linux系统的脉搏,掌握进程管理与调度的奥秘这不仅是操作系统中的技术问题,更是数字世界中的一场优雅的舞蹈。

本篇关于进程状态的介绍就暂告段落啦,希望能对大家的学习产生帮助,欢迎各位佬前来支持斧正!!!

在这里插入图片描述