LINUX入门篇【10】---进程篇【2】---进程状态

前言:

有了上一篇关于进程的初步认识和我们的PCB第一个数据段–标识符的讲解,接下来我们将继续讲解PCB的其他数据段,本篇要讲的是进程状态。

进程状态:

就像我们写贪吃蛇的时候,构建的游戏状态来判定游戏结束的方式一样,进程状态同理也是用来表明一个进程所处的状态的量。
我们常见的关于进程状态的解释图应该是这样的:
在这里插入图片描述
我们会看到,有创建,就绪,阻塞等等一系列其他的进程,但是这些说法都不够具体,下面让我们跳开书本,以一种更加具体的方式去看待进程。

PCB中进程状态到底是什么?

在操作系统中,所谓的进程状态,其实指的就是PCB的一个字段,或者说,就是PCB的一个变量:int status,这个我们在上一篇进程的初步介绍中提到过,如下:

#define NEW 1
#define RUNNING 2
#define BLOCK 3
.......//等等其他各种状态

也就是说,所谓的进程状态的变量,其实就是利用预处理指令将数字定义成一堆状态,通过pcb->status=各种状态量,来控制状态,再利用if语句来对状态进行判断,从而执行对应的语句,比如将PCB放入对应的队列中。
因此,我们总结出:
故所谓的进程状态变化,本质上就是修改状态的整型变量,然后通过if的判断将PCB放入到对应的进程队列当中,按照OS的调度执行。

进程状态的分类:

我们的分类和教材略有不同,我这里重点将其分为3个重要的状态:
1.运行状态
2.阻塞状态
3.挂起状态

接下来,我们将依次去讲解分析这三种重要的状态:

1.运行状态:

1.首先,我们需要弄清楚,什么是运行状态呢?
只要是再运行队列中的进程,其状态都是运行状态,这些进程已经准备好了,可以随时根据OS的指令被调度或者被处理。
2.那么什么是运行队列呢?
在计算机中,CPU是运算速度最快的硬件,每一个CPU在系统层面,都维护着一个运行队列,且注意,每一个CPU只维护一个,因此,有几个CPU,就可以同时维护几条运行队列。其大致的维护方式如下图:
在这里插入图片描述
因此,一个进程在运行状态的时候,一定是其PCB被链接到运行队列里面的时候,我们称这个时候的进程状态为运行状态。

2.阻塞状态:

不论我们写怎样的代码,都或多或少的需要访问计算机中的软硬件资源,也就是说我们或多或少都需要去访问系统中的一些资源。
比如:
我们需要从键盘,磁盘或者网卡等硬件设备中获取资源,当我们使用scanf或者cin的时候,我们的本质是想从键盘中读取一定的数据。
但是,倘若我们的程序开始后,我们没有向键盘输入数据呢?
此时,键盘上的数据是没有的,也就是没有准备就绪,操作系统此时会在第一时间实时掌控我们设备的状态,并且一定第一时间就知道所管理设备的状态变化,并将其反馈给我们的上层用户,因此由于此时进程想要访问的数据迟迟没有就绪,不具备访问条件,因此进程的代码就没法继续向后进行,在这个状态下,就会使进程进入阻塞状态,由前面讲到的运行状态的知识,我们也可以推出,我们所谓的阻塞状态也应该是将对应的PCB链接到某个队列中
对于OS来说,涉及到管理,就必然需要先描述再组织的管理方式,因此,**OS想要管理硬件,也是通过构建关于硬件的属性的结构体来管理的,**其结构体大致如下:

struct dev
{int type;//对应设备的种类,即哪种硬件设备。int status;//设备状态....//其他更多属性PCB*wait_queue//设备的等待队列
};

不同的设备也是通过结构体中的type来重定义整型数据区分的,如下:

#define DISK 1
#define KEYBOARD 2
#define NETCARD 3
.......//让对应的整型数字,都对应一个相应的硬件设备,每一个硬件设备都对应的一个编号用来区分不同设备

在这里,你会发现一个PCB*wait_queue,没错,对于每一种硬件设备来说,他们都维护着一条自己的等待队列,和CPU维护的运行队列一样,当我们的PCB由于资源没有准备就绪而无法推进接下来的代码的时候,这个PCB就会被操作系统链接到对应设备的PCB等待队列中,我们称此时的这个进程的状态即为阻塞状态。
因此,我们可以这样总结:在操作系统中,会存在非常多的队列,运行队列,等待队列等,但是我们要明确,不是仅仅在CPU中维护着一条运行队列,在对应的每一个硬件设备中都维护着自己的等待队列

!!!故:我们的进程状态变化的本质:
1.更改PCB.status整型变量的数值
2.将PCB链入到不同的进程队列当中!!!

在上述的讲解中,你会发现:我们所有的状态变化,都只与进程的PCB有关,和进程对应的代码和数据没关系。

将一个进程的PCB由运行队列链接到某个硬件对应的等待队列:该进程阻塞了
将一个进程的PCB由等待队列链接到运行队列:该进程被重新唤醒了
可以用下面的图片来表示进程处于阻塞状态:
在这里插入图片描述
那么,阻塞状态,站在我们用户的视角是怎样呈现的呢?
1.首先是运行的程序会卡住
2.pcb没有在运行队列中,且进程的运行状态不是running,CPU不会调度该进程,该进程的代码无法向下继续推进
因此,这也是为什么我们在同时打开多个软件的时候会卡住的原因,其本质原因就是同时开了多进程导致的
操作系统的进程队列采取的是双向链表链接起来的,故不存在所谓的PCB丢失的问题,OS操作系统也会为其托底,保证PCB不会发生丢失。

挂起状态(阻塞挂起状态)

挂起状态是基于阻塞状态的情形下出现的一种情况:我在这里所说的正是阻塞挂起状态。
如果一个进程当前被阻塞,这就注定在这个进程所等待的资源没有就绪的这段时间内,进程是无法被调用的,但是它的PCB却占据着内存的空间,如果此时的OS的内存的资源已经严重不足的时候,应当如何处理呢?
首先我们需要搞清楚一个问题是,OS直接崩溃和OS中的一个进程中止掉这两个情况哪一个更严重,我想不用我说,我们都知道,OS崩溃造成的后果是更严重的,OS也是这样考虑的,因此,在内存资源严重不足的时候,OS会将阻塞进程对应的代码和数据手动置换到磁盘中,然后释放掉这个PCB空间,从而维持OS的正常运行,此时的这个PCB就是挂起状态。如下图:
在这里插入图片描述

或许你会问,将内存数据进行置换到外设,这时针对所有的阻塞进程的,这就会导致运行速度缓慢,但速度变慢是必然的,我们只要保住了我们的OS正常运行就是最关键的,千万不能让OS停止执行,那样就麻烦更大了
而被置换到磁盘的代码和数据会被存储到磁盘的一个叫swap的分区内,当OS解决了内存不足的问题,又可以正常运行的时候,这些在swap上的数据会被重新加载回到内存中并且重新匹配新的PCB为其调度。注意,我们的swap不要开太大的空间,这回导致操作系统过分依赖磁盘交换节省空间,从而导致程序的运行速度变慢。
我在这里所说的,是挂起的其中一种情况:阻塞挂起,也是最常见的一种情况,而挂起的一系列处理过程,都是由操作系统单独完成的,即寻找阻塞进程,数据置换,内存清理,数据重新加载并重新匹配PCB。

其实,不论教科书上是怎样的名字,我们对应的进程状态实际上都是这三种状态的衍生,比如第一张图片的创建,就绪,衍生其实就是运行状态,阻塞就是阻塞状态,而中止就可以理解为挂起状态,所以我们对于任何一种进程状态,都可以将其分解为三种基本状态或者为三种基本状态的组合。

LINUX中实际的进程状态:

虽然理论上,我们谈到了基本的三种状态,但在LINUX的系统的实际环境下,还是衍生出了各种不同的进程状态:
1.前台状态"+"
2.后台状态
3.浅度休眠状态“S”
4.深度休眠状态“D”
5.暂停状态”T"
6.调试追踪暂停状态“t"
7.死亡状态”X"
8.僵尸状态“Z”
9.孤儿状态
接下来,我们将分别去学习这几种状态:

1.前台进程:

在这里插入图片描述
如图所示,我们的STAT即代表进程状态,这种带有+号的即为前台进程状态,这种是不需要去关注后面的字母的,有了+号就代表是前台状态。
前台状态的特点是:
进程一直不断的向前走,一个系统中前台进程有且只有一个,只有用^c才能中止。

2.后台进程:

在这里插入图片描述
这种不带有+号的即为后台进程,想要后台执行一个进程,只需要在可执行程序后面加上&号即可,后台进程在运行是可以输入其他指令和代码,且^c是无法中止的,用kill -9即可中止进程或者等待程序自己结束。

3.S休眠进程(sleeping):

它的本质就是阻塞状态,或者是死循环的状态,在LINUX中,这种休眠的进程被称为浅度睡眠,浅度睡眠的特点是会对外部信号做出响应。
比如,你可以直接利用kill指令来杀死进程,如下:
在这里插入图片描述
这里的S便代表一个休眠进程,我们是可以通过kill将其杀死的。

4.D深度休眠进程(disk sleep):

这是一种专门针对磁盘IO拷贝设计的一种进程状态,在这条进程没有执行完之前,OS是无权杀掉这个进程的,哪怕内存空间不够,因为有的时候这个进程可能涉及到将重要的数据放入到文件中,数据传输需要时间,如果这时进程被杀死,对应的数据就会丢失,如果是很重要的数据,就会造成巨大的损失,因此,深度休眠正是为此而生,它保证了数据不会发生丢失。
不过,一旦到了连深度休眠的进程都想要删除的地步,这个OS也基本处于崩溃边缘了,这个时候除非拔网线,否则OS就要崩溃了,但拔网线同样会导致数据丢失,因此,深度休眠基本是见不到的。

5.T暂停进程(Stopped):

T进程是需要我们通过信号让进程响应才可以触发的,我之前提到过信号的查询kill -l
如下:
在这里插入图片描述
我们已经认识了9号信号SIGKILL,今天让我们再认识两个信号:18号SIGCONT 19号SIGSTOP
18号信号是进程重启信号,它可以让一个被暂停的进程重新运行
19号信号是进程暂停信号,它可以让一个进程暂停下来编程暂停进程:T进程
如下:
在这里插入图片描述
如图所示,pid为9989的进程变成了暂停进程,停止了运行,现在我们可以让其复原,只需要输入对应的kill -18信号即可。
由此我们引发了一个问题:我们为什么要暂停进程呢?
在进程访问软件资源的时候,可能由于权限问题,暂时不让进程进行访问,此时就会将进程设为T状态

6.t追踪暂停进程(tracing stop):

追踪暂停用于我们调试的时候,在我们设置断点后,再debug模式下,我们的代码遇到断点就会自动进入暂停进程状态,之后再运行则会重启进程,如下:
在这里插入图片描述
在这里插入图片描述
如上图,这便是我在7处设置了断点,因此当我run的时候,会自动运行到断点处进入暂停状态,此时进程的状态就是t状态,而后我再一次run,则进程又变成了运行状态。
因此我们可以总结:
本质上T/t也算阻塞状态,等待软件准备就绪,它也会创建一个关于软件的PCB*wait_queue进程,,而我们调试gdb也是一款软件,因此调试的时候也会打开一个进程gdb,gdb进程后面还会接着一个我们自己的可执行程序,当gdb进程没有执行完的时候,就会将可执行程序进程PCB链接到wait_queue中,等到debug执行完后再回到运行队列中,因此进程也会等待进程。

7.X死亡状态(dead):死亡状态是一种顺时状态

即PCB被OS释放回收后,这个进程就变成了X状态,即瞬间死亡状态。

8.!!!Z状态(zombie):僵尸进程:

何为僵尸进程呢?根据字面的理解,我们可以理解为半死不活的状态。
它的概念是:
进程被中止,但是它对应的PCB的信息还没有被采集完毕的状态。
如下:
在这里插入图片描述
此时的mybin对应的便是僵尸进程。
想要真正认识僵尸进程,我们需要重新理解一下进程:
首先进程-内核PCB+进程对应的代码和数据,在内存中,这两者都要占用内存空间。而进程退出的核心工作之一:将PCB和自己的数据和代码释放掉。
那首先我们创建一个进程的目的是为了帮助我们完成某些任务,那如何知道进程完成的好坏呢?我们就需要反馈的数据和信息,进程在退出的时候就需要一些退出信息来表明自己的任务完成如何,然后由父进程来查看收集这个信息从而反馈给上层。
因此,当进程退出的时候,退出的信息会被写入到它对应的退出进程的PCB中,而代码和数据是可以允许被释放的,但是这个PCB不允许被立刻释放,必须等到信息被收集完才能释放,从而让父进程知道这个子进程退出的情况和退出原因。
因此,一个进程退出了,但是还没有被父进程读取数据,则OS必须来维护这个退出进程的PCB结构直到数据被读取,在父进程读取之前,这个进程没有彻底退出,但是又不能被调度,这个进程就是僵尸进程,僵尸进程的数据被父进程读取后,才会由Z状态变为X状态,之后这个进程才会被彻底释放掉,这样我们对僵尸进程的概念才更加清楚了

那如果父进程一直不回收退出的子进程,则这个PCB就会一直维持Z状态并且一直存在么?
让我们来看下面的代码:
在这里插入图片描述
退出一个进程,我们要使用exit(状态参数)来退出,它的头文件为<stdlib.h>
倘若父进程一直都在执行,而子进程执行了几次就被回收了,那么父进程一直执行自己的代码,不会回收子进程,这个子进程的PCB就会一直为僵尸进程么?
结果如下:
在这里插入图片描述
是的,父进程一直在执行,同时子进程的PCB也会变成僵尸状态,我们直到PCB是会占用内存空间,因此,如果不及时释放我们的僵尸进程,就会造成内存泄漏的问题,就像我们动态开辟如果不及时free的话,也会造成堆区内存的泄漏,最后导致程序卡顿甚至最后崩溃,而僵尸进程更是会导致内核级别的内存泄露问题。
在这里插入图片描述
此时我们会发现,我们的僵尸进程为defunct,即为失效的意思
!!!!僵尸进程的危害:
若一个父进程创建了很多个子进程,但就是不回收,就会造成内存资源的浪费,因为数据结构对象本身就要占用内存,所以会造成内存泄漏的问题,我们后面会介绍如何解决这个问题。

9.孤儿进程:

倘若存在一个父子进程,父进程已经退出了,父进程会被bash直接回收,但是这种回收是一级管一级的,也就是说bash可以回收父进程,但子进程必须有父进程回收,bash是不会回收子进程的,bash会及时回收父进程,只要父进程要退出bash就会及时回收,因此bash不会出现僵尸进程的问题,但此时的子进程的父进程退出了,子进程没人回收,就成为了孤儿进程,对于孤儿进程,他们统一会被1号进程,你可以理解为操作系统本身领养托管,system/initd。从而防止子进程在进程退出后变成僵尸进程无人回收造成严重的内存泄漏问题。
如下:
在这里插入图片描述
在这段程序执行的过程中,我主动kill掉父进程,则子进程正常运行,但显示如下:
在这里插入图片描述
没错,你会发现他的父进程的ppid变成了1,说明此时这个子进程成为了孤儿进程被系统托管了,直到进程结束直接被系统释放掉。

总结:

以上便是我们的PCB第二个重要的数据段:进程状态,之后我们就会讲解下一个数据段:进程优先级,其实进程状态的本质就是PCB被链接到不同的队列中,希望大家可以再进一步理解一下进程状态,并且最好可以同我们使用电脑时的一些实际情况联系起来,从而获得更好的理解。5

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/174507.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录算法训练营第六十天|84. 柱状图中最大的矩形

84. 柱状图中最大的矩形 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 示例 1: 输入&#xff1a;heights [2,1,5,6,2,3] 输出&#xff1a;10 解释…

Linux——使用命令查看文件和文件夹数量

目录 一、相关命令参数解析二、查看当前目录下的文件数量2.1 包含子目录中的文件2.2 不包含子目录中的目录 三、查看当前目录下的文件夹个数3.1 不包含目录中的目录3.2 包含目录中的目录 四、查看当前文件夹下叫某某的文件的数量实例 五、总结 一、相关命令参数解析 "&qu…

java方法引用

MethodReference 前言特定类任意方法引用函数接口特定类调用 前言 JDK1.8之后提供是Lambda&#xff0c;它可以让开发者自定义函数接口中抽象方法的实现&#xff0c;方法引用可以让开发者直接引用已存在的方法。 方法引用的形式: 类::静态方法 类::特定类任意方法 对象::实例方…

MyBatis 操作数据库(入门)

一&#xff1a;MyBatis概念 (1)MyBatis &#x1f497;MyBatis是一款优秀的持久层框架&#xff0c;用于简化JDBC的开发 (2)持久层 1.持久层 &#x1f49c;持久层&#xff1a;持久化操作的层&#xff0c;通常指数据访问层(dao)&#xff0c;是用来操作数据库的 2.持久层的规范 ①…

竞赛选题 题目:基于卷积神经网络的手写字符识别 - 深度学习

文章目录 0 前言1 简介2 LeNet-5 模型的介绍2.1 结构解析2.2 C1层2.3 S2层S2层和C3层连接 2.4 F6与C5层 3 写数字识别算法模型的构建3.1 输入层设计3.2 激活函数的选取3.3 卷积层设计3.4 降采样层3.5 输出层设计 4 网络模型的总体结构5 部分实现代码6 在线手写识别7 最后 0 前言…

前缀和——1314. 矩阵区域和

文章目录 &#x1f3a4;1. 题目&#x1f3a4;2. 算法原理&#x1f3a4;3. 代码实现 &#x1f3a4;1. 题目 题目链接&#xff1a;1314. 矩阵区域和 - 力扣&#xff08;LeetCode&#xff09; 给你一个 m x n 的矩阵 mat 和一个整数 k &#xff0c;请你返回一个矩阵 answer &#…

[极客大挑战2023] Crypto/PWN/Reverse

这个网站真辛苦&#xff0c;每次都要回到all&#xff0c;屏幕随时卡。界面有待进步老远。也不提示结束&#xff0c;结果现在才听说结束了&#xff0c;才开始记录一下。 还跟往常一样&#xff0c;WM不作&#xff0c;其它也AK不了&#xff0c;总是差点。 Crypto SignIn 53594…

C语言错误处理之<errno.h>与<error.h>

目录 前言 错误号处理方式 errno.h头文件 常见的宏 error.h头文件 参数解释&#xff1a; 关于的”__attribute__“解释&#xff1a; 关于“属性”的解释&#xff1a; 实例一&#xff1a; 实例二&#xff1a; error.h与errno.h的区别 补充内容&#xff1a; 前言 …

6款AI工具网站,赶紧收藏,以备不时之需

1、海鲸AI-支持AI对话、AI文档解析、AI绘画 https://www.atalk-ai.com 海鲸AI是一个AI应用网站&#xff0c;同时支持PC和移动端&#xff0c;它在一个页面上提供了多种模型&#xff08;GPT3&#xff0c;GPT4&#xff0c;文心一言&#xff0c;通义千问&#xff0c;智谱AI&#…

【Vue】记事本

上一篇&#xff1a;Vue的指令 https://blog.csdn.net/m0_67930426/article/details/134599378?spm1001.2014.3001.5501 本篇所需指令&#xff1a; v- for v-model v-on v-show 目录 删除功能 添加功能 统计功能 清空功能 v-show 删除功能 <!DOCTYPE html> …

一、Lua基础

文章目录 一、Lua是什么二、Lua特性&#xff08;一&#xff09;轻量级&#xff08;二&#xff09;可扩展&#xff08;三&#xff09;其它特性 三、Lua安装四、Lua应用 看到评论说&#xff0c;C让我见识了语言的严谨与缜密&#xff0c;lua让我见识到了语言的精巧与创新&#xff…

Linux:通过VMWare,定制化Linux系统

一、原理图 二、新增磁盘&#xff08;对应上图sdb盘&#xff09; 三、挂载磁盘 主要是四步&#xff1a;查看磁盘&#xff0c;分区磁盘&#xff0c;格式化磁盘&#xff0c;挂载磁盘 1、查看磁盘 2、分区磁盘 3、格式化磁盘 4、挂载磁盘 创建两个备用目录&#xff0c;用于磁盘…

【Kotlin】引入与基础语法

文章目录 Kotlin的特性Kotlin优势Kotlin的安卓项目变量变量保存了指向对象的引用优先使用val来避免副作用 后端变量Backing Fields延迟初始化 Kotlin的特性 它更加易表现&#xff1a;这是它最重要的优点之一。你可以编写少得多的代码。Kotlin是一种兼容Java的语言Kotlin比Java…

1、windows10系统下Qt5.12.0与卸载

一、安装包下载 1、Qt社区下载 https://download.qt.io/archive/qt/5.12/5.12.10/qt-opensource-windows-x86-5.12.10.exe 2、百度网盘下载 链接&#xff1a;百度网盘 请输入提取码 3、Qt官网下载&#xff1a; Try Qt | 开发应用程序和嵌入式系统 | Qt 二、安装提示 下…

nodejs+vue+elementui网上家电家用电器数码商城购物网站 多商家

基于vue.js的恒捷网上家电商城系统根据实际情况分为前后台两部分&#xff0c;前台部分主要是让用户购物使用的&#xff0c;包括用户的注册登录&#xff0c;查看公告&#xff0c;查看和搜索商品信息&#xff0c;根据分类定位不同类型的商品&#xff0c;将喜欢的商品加入购物车&a…

【html+css】表单元素

目录 表单元素 展示图 简约写法&#xff1a; 完美写法 表单元素 输入框 单选框 复选框 下拉框 按钮 展示图 简约写法&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><t…

华大基因在合规管理、高质量发展方面将迈上新的台阶

今年6月&#xff0c;华大基因顺利通过了国际领先标准、测试及认证机构BSI的严格审核&#xff0c;获得GB/T 35770-2022 / ISO 37301:2021合规管理体系认证证书&#xff0c;成为行业内率先获此国际认证的企业。 ISO 37301合规管理体系认证是国际通用的合规管理体系认证标准&…

Mysql之局域网内不同ip互登陆mysql

1 navicat修改mysql表中user> host改为% 2 重新加载mysql服务 3登陆mysql -h 192.168.x.xxx&#xff08;计算机ip&#xff09; -P 3306 -uroot -p123456&#xff08;密码&#xff09;

每日一练2023.11.26——打印沙漏【PTA】

题目链接&#xff1a;L1-002 打印沙漏 题目要求&#xff1a; 本题要求你写个程序把给定的符号打印成沙漏的形状。例如给定17个“*”&#xff0c;要求按下列格式打印 ************ *****所谓“沙漏形状”&#xff0c;是指每行输出奇数个符号&#xff1b;各行符号中心对齐&am…

小程序如何进行版本回退

当商家决定回退小程序版本时&#xff0c;可能是因为新版本出现了一些问题或者不符合预期&#xff0c;需要恢复到之前的稳定版本。下面具体介绍怎么回退小程序的版本。 在小程序管理员后台->版本设置处&#xff0c;点击版本回退。确认后&#xff0c;小程序会回退到上一次的版…