我们需要先理解操作系统理论中的三个进程状态,再具体到理解具体一款操作系统 Linux
中的七大进程状态会更好
操作系统中三大进程状态
由于篇幅限制,这里可以跳转博客观看:
【Linux】OS进程状态第一章:操作系统理论中的三大进程状态-CSDN博客
同时,本文会涉及到时间片的知识,需要的可以跳转查看:【Linux】并行与并发(含时间片)-CSDN博客
Linux下的七大进程状态
Linux内核源代码中,进程存在这么多种状态
下面的状态在 Linux
系统的 kernel
源代码里定义
c++
/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
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 运行状态:
R 运行状态:进程处在CPU的运行队列中
c++
"R (running)", /* 0 */
c++
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
int main()
{
pid_t id = fork();
if(id == 0)// 子进程需要执行的
{
while(1){
printf("我是子进程, pid = %d\n", getpid());
sleep(1);
}
}
else if(id > 0) // 父进程需要执行的
{
int status = 0;
int ret = waitpid(id, &status, 0);
}
}
S 休眠状态:
休眠状态:即阻塞状态,处在外部设备的等待队列中(这种状态也叫浅睡眠状态)
c++
"S (sleeping)", /* 1 */
例如,我们使用死循环一直 printf 打印语句,同时查询该进程状态
**进程为 S
状态:**在 STAT
这一栏,可以看到该程序 当前状态为 S+
,S 即代表 Sleeping
休眠状态(加号的意思暂时不用管),因为程序循环执行 printf 语句,系统会涉及 IO
操作的系统调用,暂时不会使用CPU。
偶尔的 R
状态 : 同时程序运行起来,你会发现,偶尔会查询出一次 proc 程序
处于 R+
状态,R
即为 Running
(运行状态),说明正处于 CPU
的运行队列中等待被 CPU
执行。
为什么会出现两种状态:
该程序在执行 printf
语句时,操作系统需要进行 IO
操作的系统调用,此时该进程处系统的等待队列中,为阻塞状态
该程序在得到了外设响应(显示器)后,该进程会从该外设的等待队列重新插入到CPU的运行队列中,为运行状态
一次进程运行,进程处在不同队列中,进程就会有不同的状态。
进程在执行printf()
时,会经历从运行状态到睡眠状态的切换,这是因为它需要等待I/O操作的完成。完成之后,它又会被重新调度回到运行状态。
为什么出现 S
的次数要远多于 R
的次数:
因为 printf
和外设进行 IO
交互的时间是较为漫长的,因此 S 的休眠状态时占据主要时间的。
进一步测试:将 printf 语句去除,只有死循环,则一直处于 Running 运行状态
因为系统无需进行 IO
操作
进一步测试:只有 scanf 语句等待输入,则一直处于 Sleeping 休眠状态(等待键盘输入)
D 磁盘休眠状态:
D 磁盘休眠状态:不是磁盘休眠,而是进程在等待磁盘响应时,进程处于休眠状态(阻塞)(这种状态也叫深睡眠状态)
c++
"D (disk sleep)", /* 2 */
D 状态就是进程和磁盘进行IO交互时,进程正在等待磁盘的IO结束
(1)为什么要为磁盘的IO交互设置这样特殊的进程状态呢?
操作系统设计D
状态的主要原因之一是为了保护那些正在进行 关键I/O操作 的进程。
- 如果一个进程正在向磁盘写入大量数据,而这个过程被中断(例如,通过
kill -9
命令强行终止进程),那么已经写入的部分数据可能会损坏或不完整。
因此,对于与磁盘进行 I/O 交互的进程,系统将其置于 D
状态,操作系统不会轻易终止处于 D
状态的进程,保证数据传输稳定与数据不能被丢失。
并非绝对免疫 :虽然操作系统通常不会轻易中止处于D
状态的进程,但并不意味着这些进程完全免疫于系统的干预。操作系统仍然可以在必要时终止这些进程,特别是在系统资源紧张或出现严重错误的情况下。
(2)D
状态的进程能否被 kill -9
命令杀死?
实际上是可以的。SIGKILL
信号(由kill -9
产生)无法被进程捕获或忽略,所以即使是处于D
状态的进程也会被终止。但是,终止进程并不意味着立即终止其 I/O
操作;操作系统会先确保所有未完成的 I/O
操作完成,然后再终止进程。
(3)为什么一般系统中很难查到 D
状态?
通常情况下,系统中的进程与磁盘进行I/O交互的速度较快,因此这些进程通常很快就能完成I/O操作并返回到其他状态(如R
或S
状态)。因此,一般很难观察到进程长时间处于D
状态。
如果查询到某个进程为 D 状态,大概率说明你系统的磁盘出现老化或空间不足情况,磁盘响应读写速度慢,并且磁盘可能需要不停的做空间整合给你的数据输入腾空间这类工作。
T 暂停状态:
c++
"T (stopped)", /* 4 */
命令 kill -l
:查看命令 kill
对应的所有信号
- kill 的 19 号信号 STGSTOP(signal stop):暂停当前进程
- kill 的 18 号信号 STGCONT(signal continue):继续当前进程
(1)代码演示
我们随便写个死循环程序,并运行起来
命令 kill -19 107580
停止掉 PID = 107580
的进程(即我们的死循环程序 test
)
可以发现,信号停止该程序后,该进程的 STAT
状态 由 S+
状态变成 T
停止状态
(至于为什么这里进程会是 S
状态,而不是 R
运行状态,可以看前面讲解过的 [ S
休眠状态])
(2)暂停的作用与意义
暂停该进程目的处理除了有需求需要临时暂停,还有一个作用:继续启动暂停状态进程时,该进程会变成 后台执行进程
后台运行的进程:
- 无法使用 命令
ctrl + c
杀掉该进程, 命令ctrl + c
是用于杀掉前台进程 - 后台进程只能使用命令
kill -9
强制杀掉
(3)前后台进程的概念与表现
概念与区别
前台进程 (Foreground Process)
- 定义:指当前正在与用户交互的进程。
- 特性:
- 占据终端或窗口的焦点,用户必须等待其完成才能执行下一步操作。
- 拥有较高的优先级,通常会优先获取系统资源。
- 阻塞用户输入,直到进程结束或被暂停。
后台进程 (Background Process)
- 定义:在不与用户直接交互的情况下运行的进程。
- 特性:
- 不占用终端或窗口的焦点,用户可以在其运行时执行其他操作。
- 可能会得到较少的系统资源,因为它们不是当前用户关注的焦点。
- 允许用户同时执行多个任务,提高效率。
简单来说,前台进程正在和用户直接进行交互的,后台进程会直接运行而不依赖于与用户的交互,可以将多个任务创建成后台进程,让这些任务自己跑,用户暂时不用理会
让一个进程在后台运行,这种操作我们日常生活使用手机时应该是都有体会到的:打开某个APP,然后不关闭该APP,接着同时切换到另一个APP中,可以发现之前打开的APP还在运行,这就是操作系统把它放到后台运行(当然如果手机运存不够,也是会杀后台的(后台应用进程过多,操作系统会选择杀掉几个进程)),后台进程相当于没消失仍运行,只是不直接和你交互了,而只有一个前台进程和你在交互。
在后台进程运行时,你不影响前台进程和用户的交互,你可以在前台进程中不断进行执行命令,创建新的后台进程等操作
状态表现
前台进程:STAT 进程状态都有 + 号
后台进程:STAT 进程状态 + 号消失
代码演示:
我们通过命令 kill -18 107580
继续运行刚刚停止掉 PID = 107580
的进程(即我们的死循环程序 test
)
可以发现,该进程的 STAT
状态 由 T
状态变成 S
停止状态,并且相比于最初那次启动的 S+
状态,这次少了个 +
号
即表示,从被信号停止,再被信号启动继续运行,该进程就从前台进程变成了后台前台进程。
t 追踪暂停状态:
当进程被追踪,断点停下,进程状态为 t
c++
"t (tracing stop)", /* 8 */
使用 gdb
调试 code
时, gdb
调试进程会启动 code
进程, gdb
进程开始追踪 code
进程(开始调试)
当遇到打断点的地方,code 进程状态变为 追踪暂停状态 t
X 死亡状态:
死亡状态 X 状态只是一个返回状态,你不会在任务列表里看到这个状态。
我们可以通过两种方式来杀死一个进程
(1)命令 kill -9 进程PID
(2)命令 killall 程序名
Z 僵尸状态:
点击跳转博客观看更加详细的文章:【Linux】僵尸进程和孤儿进程-CSDN博客