文章目录

引言:进程的生命与状态:动与静的交响曲
每一个进程都拥有自己的生命轨迹,它们在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 PID
即kill -9 1007716
终止 子进程
再次查看进程状态:
僵尸进程如果不被回收,会导致内存泄漏问题和标识符占用问题
📖孤儿进程
孤儿进程是一种特殊的进程状态
- 通过程序创建 父子进程
- 通过指令终止 父进程,此时 子进程 会被
OS
领养 - 子进程 的 父进程 变为
1号进程
- 子进程 就变成了一个
孤儿进程
同样执行上述代码,起初都正常运行
现在我们消灭父进程,观察其执行结果和运行状态
可以看到子进程被1号进程领养
假设 子进程
不被 1号进程
领养
子进程
退出时就会无人回收,成为一只游离的僵尸僵尸进程
有 内存泄漏 的风险- 因此
子进程
会被OS
领养
总结:进程状态的舞蹈
Linux操作系统中的进程状态,不仅仅是抽象的计算机术语,它们是系统中每个进程生命周期的具体体现。正如一场精彩的舞蹈,每个进程都有自己的节奏和步伐,它们或快速跃动,或缓慢舞动,或瞬间消失,而每个转变都与操作系统对资源的调度和管理息息相关。
通过理解进程状态与状态转变的过程,我们能够更好地把握Linux系统的脉搏,掌握进程管理与调度的奥秘。这不仅是操作系统中的技术问题,更是数字世界中的一场优雅的舞蹈。
本篇关于进程状态的介绍就暂告段落啦,希望能对大家的学习产生帮助,欢迎各位佬前来支持斧正!!!