目录
[1. 进程状态](#1. 进程状态)
[2. 运行状态](#2. 运行状态)
[3. 阻塞状态](#3. 阻塞状态)
[4. 挂起状态](#4. 挂起状态)
[5. Linux中具体的状态](#5. Linux中具体的状态)
前言
在Linux操作系统中,进程状态非常重要,它可以帮助我们了解进程在系统中的运行情况,从而更好地管理和优化系统资源,在Linux系统中,进程可以处于不同的状态,本文我们来聊一聊运行、阻塞、挂起这几状态;
1. 进程状态
在许多教材以及资料当中有许多对进程状态的总结与描述,资料不同它们的描述也各有略同,但总都是一些概念,Linux进程状态具体是什么?本文我们将从底层出发,聊一聊运行、阻塞、挂起这几状态;
温馨提示:如果在本文中某些名称或者内容不太了解,可以先阅读这篇文章:
2. 运行状态
从计算机硬件出发,我们写的代码生成可执行文件都被存储在磁盘当中,想要让程序运行就必须将程序加载到内存当中;
每一个程序(进程)都会有一个属于自己的PCB,通过PCB来进行排队,等待CPU的调度;
为了便于调度管理,操作系统会在内存当中维护一个叫运行队列的结构,所有就绪状态的进程的PCB会被加入到这个队列当中;
CPU在调度执行时就会通过这个运行队列拿到进程的PCB,进而调度执行该进程;
只要进程在这个运行队列当中,它的状态就是运行状态;每个CPU在系统中都会维护一个运行队列;
3. 阻塞状态
了解完运行状态,我们再来聊一聊阻塞状态,阻塞状态是建立在进程被调度执行的基础上;
在CPU执行一个进程时,都可能会或多或少的去访问系统的某些资源,比如:我们使用的scanf,在执行时需要调用键盘(本质就是从键盘中读取数据);
我们不输入,键盘的数据就是没有就绪(进程需要访问的数据没有就绪),此时进程无法继续执行,需要等待数据;
状态又是如何转变的呢?
操作系统和驱动程序它们对硬件进行管理,每个硬件都会有一个属于自己的结构描述,通过指针链接,操作系统通过链表(dev_list)达到对硬件设施的管理;
当进程正在等待某个硬件的资源时,把进程的PCB加入到硬件设备结构描述的等待队列当中,并把PBC的状态设置为阻塞状态;当PCB在这个等待队列中等待数据资源时,这个状态就叫做阻塞状态;
系统资源等待结束之后,操作系统会再次将等待队列中的PCB移入到运行队列当中,并把状态修改为运行状态,等待CPU的执行调度;
4. 挂起状态
了解完阻塞状态,我们来聊一聊挂起状态;挂起全称:阻塞挂起;
挂起是基于阻塞的一种状态;
进程处于阻塞状态时,并且内存不足的情况下就会出现;
针对于这种情况,操作系统会将阻塞进程的代码和数据置换到外设,此时该进程的状态就被称为挂起状态;
阻塞进程的代码和数据一般会存放在磁盘的swap分区,当进程被操作系统调度时,被置换到外设的代码和数据又会重新加载到内存;
拓展补充
一般情况下,swap分区的大小不会太大,大概等于内存的大小,过大的swap分区会导致操作系统过于依赖swap分区,导致效率变低;
5. Linux中具体的状态
说了这么多理论,那么在Linux中进程的状态又是怎样的?
cpp
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 */
};
这是Linux内核源码对进程状态的定义;它其实并没有像阻塞挂起这样的状态;
我们也可以写一个程序来测试一下:
cpp
#include <stdio.h>
#include <unistd.h>
int main()
{
while(1)
{
printf("hello world! \n");
}
return 0;
}
在Linux环境下编译运行,然后使用监控进行观察:
bash
while :; do ps ajx | head -1 && ps ajx | grep myprocess | grep -v grep; sleep 1; done
//myprocess是可执行文件的名称
观察到的情况分为两种,大多数是S+,极少数是R+:
主要是因为调用的printf,在输出时需要调用显示器,大多数的时间都是在等待显示器;我们也可以写一个空的死循环执行来观察;
R状态我们都知道是运行状态,S状态是什么?
S(sleeping)休眠状态,这里的睡眠是浅度睡眠,可以对发送的信号做出响应;
这里的 "+" 号是什么意思?
进程被分为前台进程和后台进程,带+号就表示是前台进程;
- 前台进程:正常使用 ./可执行程序,这种执行方式运行起来就是前台进程,前台进程运行时,无论我们怎么输入指令都无法被执行(Ctrl +c可终止进程)
- 后台进程:./ 可执行程序 & 这种执行方式运行起来就是后台进程,后台进程运行时,我们输入的指令依然可以执行(Ctrl +c 无法终止进程,使用kill + 9 +进程pid 杀死进程)
这里的S状态其实就是上述阻塞状态;
D状态
D(disk sleep)也是休眠状态,它是深度休眠,专门针对磁盘设计的;
当进程需要将较大的数据写入到磁盘当中,在等待磁盘写入时进程的状态就是休眠状态;如果是S状态:
在内存严重不足的情况下,操作系统没办法时会通过杀死进程的方式来节省资源;如果在等待的过程中进程被操作系统杀掉,并且磁盘写入数据失败,那么就会导致数据无法再加载(数据丢失);为了避免这种情况,就可以把等待数据写入的进程状态设为D状态;
D状态无法被杀掉(OS也不行),只能等待执行完毕后状态转换;
注意:
如果用户看到了D状态,说明计算机几乎要挂掉了
T状态
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行
bash
kill -SIGSTOP <进程ID>
使用kill指令可以查看发送给信号的所有类型:
bash
kill -l
为什么要停止?
进程在访问资源时,可能暂时不允许进程访问,这时OS就会将进程的状态设置为stop(T状态)
t状态
t状态也是停止状态,Linux中没有进行区分;
主要出现在程序Debug时,在Debug的时候,遇到断点,进程就暂停,此时就是t状态
在上述的D状态、T状态、t状态其实都是阻塞状态,阻塞可以等待硬件资源也可以等待软件资源
比如:一个进程等待另一个进程,进程的PCB里可以有PCB* wait_queue,比如gdb的进程等待Debug的进程;
X状态
X状态(死亡状态),就是我们常说的终止状态,它是一个瞬时状态,不会在任务列表里看到这个状态
最后就是Z(zombie)僵尸状态,僵尸状态较为复杂,Linux系统中的僵尸进程状态也是一种特殊的进程状态,通常是指一个子进程已经结束运行,但其父进程还未对其进行善后处理(如调用wait()函数)。如果不及时清理僵尸进程,会导致系统资源泄漏,影响系统性能甚至造成系统崩溃;
总结
本文主要从底层出发向大家介绍了运行、阻塞、挂起这几种常见的状态,并且将Linux中具体的状态进行了一系列介绍,可以划分为这几种状态;最后是僵尸状态,僵尸状态的情况较为特殊,如果不及时处理也会造成比较严重的后果,我们下期再来进行详细的介绍,以上便是本文的全部内容,希望可以对你有所帮助,感谢阅读!