Linux学习之路 -- 进程篇 -- PCB介绍1 -- 进程状态

前面已经介绍了进程标识符及其进程创建的相关知识,下面我将介绍进程PCB中进程状态的相关知识。

目录

一、进程排队

二、普遍意义的进程状态

<1>运行状态

<2>阻塞状态

<3>挂起状态

三、在Linux中的进程状态


一、进程排队

在学习进程状态之前我们先要简单了解一下进程排队的概念,在理解进程排队之前,我们需要知道的是,进程不是一直在运行的,进程放在CPU上,也是不会一直运行的。这点其实非常好理解,比如我们在编译器上写了一个死循环程序,编译运行后我们仍能使用其他的程序。这是因为在计算机中存在时间片的概念,也就是一个进程只能在CPU上运行一段时间,用完了就换下一个。

回到问题本身,为什么进程会排队?原因是进程在等待某种资源(可能是软件,也可能是硬件),举个例子:我们运行一个带scanf函数的程序时,控制台光标在不断闪烁的过程,其实就是在等待键盘上的资源。这里我们排队是用PCB进行排队,而不是程序本身进行排队。

那我们该如何排队呢,这里一般是使用队列的形式来进行排队,当然也有其他的一些数据结构,只不过以队列(双向链表)为主(这里一般一个task_struct可以被链入多个数据结构中)。这里我们可以使用结构体listnode来连接各个进程的task_struct,不过这里的连接方式和普通的链表有些许差异

通过prev和next指针就可以遍历各个进程。

解决了上述问题后我们会发现一个问题,每当我们执行到指定进程中时,我们得到的链表节点地址并不是结构的首元素地址,但我们需要的就是首元素地址,只用通过首元素地址进行运算,我们才能获取task_struct 结构体中的信息。所以解决首元素的地址就是我们的问题。在一般我们访问一个结构体指针时,我们通常都是通过偏移量来访问结构体中特定的成员。所以我们需要计算出该节点相对于首元素的偏移量,就可以得知task_struct的结构体的首元素地址。

那么一个task_struct里面有listnode结构体呢? 答案是有很多个,每个task_struct里面都可能有很多个的listnode 以表示不同的队列(其他数据结构),也就是上文所说的一个进程可以被连入多个数据结构当中,所以我们在排队时,只要修改链表节点就可以了。这种方式易于被操作系统管理。

二、普遍意义的进程状态

进程状态,顾名思义就是用来描述一个进程的运行情况,它决定了进程的后续动作。在Linux操作系统中,这个状态本质上就是一个整型变量,在task_struct中的一个整型变量(status)。我们一般用一些宏来代替这些数字表示进程的运行状态。

<1>运行状态

在Linux操作系统中,CPU往往会维护一个运行队列,如果一个进程已经准备好要要被运行了,那这个进程的task_struct就被链入这个运行队列之中。我们一般用R这个大写字母对其进行表示,当然如果一个进程正在CPU上运行,那么这个进程肯定是运行状态。

<2>阻塞状态

前面我们说过,操作系统是管理软硬件资源的软件。那我们该如何管理硬件资源呢?先描述,再组织,我们可以搭建一个结构体对硬件进行描述(包括设备的操作方法,类型,状态等等).同样使用上面的例子scanf函数的例子,当我们使用scanf函数时,需要从键盘中读取数据,当我们的进程在进行等待硬件(软件)资源时,如果资源没有就绪,我们的进程task_struct 只能将自己设置成阻塞状态,并且把自己的PCB链入等待资源提供的等待队列。

图一.

图二.(下面的程序就处于一种阻塞状态,进程在等待键盘上的资源)

图三.

其实上述的状态的改变,就是进程的PCB被操作系统(OS)链入不同的队列之中。

<3>挂起状态

(这里我们主要简单地介绍一下阻塞挂起)

挂起状态的一个重要前提:计算机资源已经严重不足了。

当我们系统资源不足时,处于阻塞状态的进程如果被系统认为短期内不被调度,那么系统就会把该进程在内存上的相关数据交换到磁盘上(这里有一块专门的区域,swap分区),注意:task_struct 还在内存之中。在需要使用时,系统会把这个进程的资源重新载入到内存当中,这种方式可以腾出内存空间,这里不止是进程,还有其他的资源。虽然这种方式需要从外设中读数据,这会让系统变慢,但是总比系统整个挂掉要好。

三、在Linux中的进程状态

前面介绍了那么多的理论知识,下面举一些具体的例子,帮助各位理解在Linux中进程状态,以及如何查看的问题。

这里我们使用一段while循环程序,再观察该进程的运行状态。

运行状态

在ps命令中,有一行STAT,这一行表示的就是进程的状态,这里我们会发现,这里的进程处于一种S状态,也就是休眠状态(+号等会介绍),等同于前面的阻塞状态。可明明我们的程序在运行啊,为什么这里会显示出现进程休眠的状态呢?这是因为我们的while语句里面只有一条printf语句,cpu的执行速度是非常快的,一条语句很快就被执行完了,执行完后,进程就处于休眠状态。哪怕我们把sleep语句去除,这里的状态大概率还是S,因为printf语句是要访问外设的(比如显示器),访问外设时,我们并不能保证相应的外设资源已经准备好了,虽然当我们看到屏幕上不断刷新语句,其实大部分时间,进程还是没被执行,或者被执行了,但是时间片用完了,重新休眠等待下次调用。

这里我们只要把while里面的语句全部注释掉,就能让STAT显示为R状态,因为它不访问外设,执行速度很快,不需要等待资源。

此时处于运行状态。

下面介绍一下S后面的“+”,一般我们运行的进程都可以用ctrl + c停止运行(可以通过键盘操作杀死),这种进程我们一般叫前台进程,如果我们在执行程序时在结尾加 & 符号,就可以把一个进程变成后台进程,这时候就不能用ctrl+C终止进程了,我们必需要通过发送信号来杀死进程。

可以看见,运行进程的状态由S+变为S了。

此时用ctrl+C终止进程,发现没有作用。

下面我们发送一个信号,杀死该进程。

此时,我们可以看见,进程就被终止了,这个信号大家可以暂时先不关注。

在Linux操作系统里,S状态也称为休眠状态,当然S状态是可以被打断的,称为可中断睡眠。而D状态又称为不可中断的睡眠状态,这个状态一般在读取磁盘设备时,进程可能会处于的状态,因为如果在读取设备过程中,如果需要等待一些资源的话,进程会处于一种休眠状态。当我们的系统内存出现不足时,操作系统就可能会结束这个休眠进程。这时候一些数据就可能会丢失,如果把该进程设置为D,系统才不会结束这个进程。一般进程处于这种状态的时间非常短暂,如果出现长时间处于D状态的进程,系统基本也就快挂了。

下面介绍一下T状态,这里的T状态其实是一种暂停状态。我们可以通过给一个进程发送信号来实现T状态。(19是一个信号,这个信号能暂停进程,这里不具体介绍,kill -l  命令可以查看各种信号)

如果我们要继续进程,可以用kill -18 +进程号,18也是一个信号编号,表示继续进程的意思。

此时,我们发现,虽然进程回复了,但是我们不能ctrl+c 结束。这是因为一旦暂停后,进程会转到后台。只能通过信号杀死。

除了有T状态,还有一种t状态,这两种其实都是一种暂停状态,只不过t是在被追踪的一种状态。我们用gdb调试代码时,进程就处于一种t状态。暂停状态一般也可以看成时阻塞状态。进程在执行一些不好的动作时,系统就可能直接暂停这个进程。

还有两个进程Z和X状态,这两个状态比较特殊,后面单独讲。

以上就是全部内容,希望我的文章对你有些许帮助,文中如有不对之处,还望各位大佬指正,谢谢!!!!!!

  • 20
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值