我们知道进程会有属于自己的PCB,便于操作系统的管理,而PCB结构体里面还有进程状态参数,类似于用一个变量标识对应的进程状态,就相当于将每个进程状态编号,而PCB中有一个变量存储当前进程状态所对应的编号,也就表明当前进程对应的具体状态,好让操作系统看到具体的状态,并对其更好地进行操作。所以总而言之进程状态的本质其实就是PCB内部的一个整形变量,不同的整型值对应着不同的进程状态
而对于普适的操作系统而言进程状态图是这样的:
操作系统层面的进程状态
运行状态
我们知道每个进程都有自己所对应的PCB,当进程运行的时候,为了更好地进行进程管理调度,会为每一个CPU创建一个运行队列,也就是一个结构体类型,内部会将每一个需要运行的进程所对应的PCB列入到运行队列当中去,此时该进程PCB中标志进程变量的值就可以设为运行状态对应的数值。所以运行状态就是处于CPU运行队列当中的进程。但是也要注意,处于运行状态的进程并不是证明该进程就一定在被CPU运行。
阻塞状态
当一个进程被运行时,该进程可能会访问对应的外设,例如硬盘、键盘...例如我们的C++代码当中调用cin函数时,肯定需要访问键盘,进程想要运行需要从键盘中拿数据,但是如果此时不在键盘上输入任何数据的话,此时就可以说该程序状态是阻塞状态。
同样像管理进程一样,操作系统为了管理好硬件资源,也会为硬件设备创建对应的结构体,而每个结构体都标识着对应的硬件,而每一个硬件数据信息都会存在该结构体当中,便于像操作系统管理进程一样管理硬件设备。而每一个硬件设备对应的结构体当中还有一个PCB*的类型wait_queue的变量,是一个等待队列。会将所有访问硬件不成功的进程链入到对应的等待队列当中。所以上面的例子当中,该进程需要的访问键盘硬件不成功,会将该进程的PCB链入到键盘的等待队列当中,并将该进程PCB中标志进程变量的值改成阻塞状态对应的数值。直到硬件被成功访问之后再将进程pcb列回到运行队列中。
阻塞状态不仅仅只有等待硬件资源这一种,实际上一个进程执行时需要访问另一个进程时,而另一个进程未响应的话,这一个进程也会处于阻塞状态。或者一个进程访问软件资源,等待软件资源就绪时也会处于阻塞状态。
挂起状态
当我们的一个进程发生阻塞时,进程就会等待某种资源,而此时进程的代码和数据依旧是存在内存当中的,占据着内存空间。但是如果此时恰巧内存空间不足的话,为了避免操作系统崩溃的可能,操作系统就会将所有阻塞状态的进程所对应的代码和数据从内存中拿出来交换到磁盘当中,此时就释放了一部分资源供给操作系统使用。此时该进程就属于挂起状态。
其实我们的磁盘当中有一个swap分区,大小一般等于内存大小。而这部分空间就是专门给操作系统进行内存与外存数据交换的。也就是将上面提到的阻塞状态进程的代码和数据进行交换到这磁盘的swap分区的,当该进程再次能被操作系统调度的时候,那么被置换出去的代码和数据又将会置入到操作系统的内存当中。
Linux下的进程状态
其实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状态
R状态其实就是操作系统上的运行状态,但是在一般查看进程状态时是很难看到R状态的。
此时进程的代码仅仅是执行死循环,啥也没干,也就是说该进程始终在CPU的运行队列中。
S状态
S全称sleep,也就是休眠状态,相当于操作系统上的阻塞状态。
此时该进程执行时处在S状态,其实就是表明该进程此时在等待队列中,因为该进程需要执行printf函数,需要访问显示器这个硬件设备,但是显示器并不一定资源就绪,所以查看该进程时就处于休眠状态。其实本质上是因为硬件的执行效率远远低于CPU的执行效率,而访问硬件速度相较而言是十分缓慢的,CPU的运行是十分快的。所以查看进程状态时,几乎绝大部分时间都是在等待硬件资源就绪,只有极少的时间是在运行代码。
注:查看进程状态时,后面几乎都有个+:其实这种进程叫做前台进程也就是进程运行时命令行是无法执行其他命令,也就是不能运行其他进程
没有+:该进程称为后台进程,也就是说是在后台运行的,所以说命令行当中还可以执行其他命令,运行其他的进程(执行后台进程需要在命令行中输入该进程运行指令时,后面加上&符)后台进程无法通过Ctrl c终止进程,想要终止只能强行杀掉该进程:kill -9 +进程pid
D状态
D状态其实也是休眠状态,但是属于深度休眠,相较于S状态会对外部信号做出响应而言,而D状态不会对外部信号做出响应。而disk其实就是磁盘的意思,也就是磁盘休眠。
操作系统的挂起状态我们已经了解过,就是当内存空间不足时,操作系统会将阻塞状态进程的代码和数据进行提出交换到磁盘上去。但如果恰巧在此时,操作系统的空间还是不足的话,那么该进程的PCB就极可能会被操作系统给kill掉。
假如说,当一个进程需要向硬盘中写入数据,在磁盘写入数据的过程中该进程始终处于阻塞状态,也就S状态,而此时操作系统中内存爆满的话,该进程就会被删掉,那么此时磁盘读取数据也恰巧失败的话,将信息反馈给进程,可进程也被删除了,无响应,那么磁盘为了不影响其他进程的访问,就也会将进程的数据清除,而此时数据就全部丢失。所以就有了D状态,处于此状态的进程不会被操作系统删掉也不能被用户删掉,只能等待进程自行恢复或者强行关电源。
T状态
T状态就是暂停的意思。可以通过 kill-l 查看进程信息的详细列表
其实编号后面就代表着该编号所执行的操作。 SIG就是signal(信号)的缩写,使用该指令时后面要跟上进程pid,所以可以kill -19 +pid或者kill -SIGSTOP +pid暂停进程,而且暂停以后该进程就自动变成后台进程了。想要恢复该进程的话可以使用18号指令CON(continue)
t状态
tracing stop追踪暂停状态,进程处于此状态表示该进程正在被追踪。
当我们的代码在debug(-g)下时,可以使用gdb+可执行程序调试代码,此时使用b打断点,然后再r(在第一个断点处停止),此时该进程的状态就是t状态
X状态
x状态就是进程的死亡状态,表示该进程运行结束了,所以说此时该进程的PCB和代码数据都被操作系统回收了。
Z状态
Z状态就是僵尸状态。当一个进程执行完该进程需要执行的操作之后,该进程会向自己的父进程或者操作系统(OS)返回任务执行的一些退出信息。
当一个进程属于运行状态时,在运行队列上让CPU调度执行,该进程执行结束之后,进程代码和数据会被操作系统释放,但是该进程对应的PCB并不会立即跟着释放掉,而是会由操作系统将退出信息和各种状态代码写入到该进程对应的PCB上,等待该进程的父进程或者是操作系统读取进程PCB中的退出信息,得知子进程的退出原因,此时该进程就属于是Z状态(僵尸状态)。之后PCB的退出信息被读取之后,进程的状态就会从Z状态转变成X状态,紧接着PCB也就被释放掉了。
所以总结一下:僵尸状态就是在进程退出等待父进程或者操作系统来读取退出状态代码,然后释放PCB的一种状态。
以上即为进程状态的解释。
其实linux下的进程状态是可以和操作系统层面的进程状态对应起来的,linux下的(X)死亡状态和(Z)僵尸状态就相当于是操作系统层面上的死亡状态;而linux上的(R)运行状态就相当于操作系统的就绪和运行状态;linux上的(S)浅度休眠状态和(D)深度休眠状态就相当于操作系统的阻塞状态,等待某种软硬件资源;linux上的(T)暂停状态和(t)追踪暂停状态也相当于操作系统的阻塞状态,等待某种条件指令 ;而对于挂起状态是无法体现的,一般都操作系统自行决定的。