大家好,我是苏貝,本篇博客带大家了解Linux进程(3),如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️
目录
- (A) 运行(R)
- 进程切换
- (B)阻塞(S/D)
- (C)挂起
下图是 操作系统 的5种状态
在Linux中,运行和就绪状态可以合并,用状态R表示。阻塞状态用S/D表示。终止状态用Z/X表示
(A) 运行(R)
下面的内容不是Linux调度的方法,是操作系统教材调度算法的一种。 因为这种调度算法比较简单,所以我们先了解这种
运行进程一定是在CPU里运行的,所以有一个CPU
运行进程时,要将自己的代码和数据从磁盘加载到内存中,操作系统为管理进程,会在内部为每个进程创建对应的内核数据结构task_struct,task_struct能够找到对应的代码和数据。
为了方便操作系统对进程的管理,管理进程变成了对链表(每个结点都是一个进程的task_struct)的增删查改。
新增一个进程就是将新进程的代码和数据加载到内存,再由操作系统提供对应的task_struct,再将task_struct连接到队列中
删除一个进程就是根据pid找到对应的进程,释放掉进程的task_struct和内存里的代码和数据的空间,再将链表重新链接起来
每个CPU都要维护一个运行队列。运行队列本身也是一个结构体
所以进程要运行,首先要将进程放入运行队列当中
往后CPU要调度一个进程,只要找到它对应的运行队列,将队列头部的进程的代码和数据加载到CPU里的寄存器中。严格意义上来讲,只要进程被放入CPU上,进程的状态就是R
但是很多操作系统的教材上会说:只要进程在运行队列中,进程的状态就是R。这里的R表示进程已经准备好了,可以随时被调度了。但在Linux中,并没有already状态(就绪状态),所以我们可以把就绪状态和运行状态压缩成一个状态。但如果实在要将这两个状态分开来的话,那么可以理解为只有在CPU上跑的是运行状态,在运行队列里的就是就绪状态
进程切换
一个进程一旦持有CPU,会一直运行到这个进程结束吗?不会,因为存在时间片轮转调度算法,该算法是基于时间片进行轮转调度的,核心思想是将CPU时间分成固定大小的时间片,每个进程在一个时间片内执行,如果时间片用尽而进程未完成,那么该进程就会被放到就绪队列的末尾,等待下一次轮到它执行。这种方式可以保证每个进程都有机会在CPU上执行,避免了某个进程长时间独占CPU的情况,提高了系统的公平性。
比如进程a是个死循环,时间片是2毫秒,那么2毫秒之后,操作系统会直接将进程a从CPU上剥离下来,重新列入运行队列的尾部,让下一个进程b进行我们的调度
这就是为什么哪怕我们电脑只有一个CPU,我们同时开着QQ,微信和网易云,如果QQ死循环了,微信和网易云还能正常使用的原因
并发:让多个进程以切换的方式进行调度,在一个时间段内同时得以推进代码
如果1个计算机里有2个CPU,那么它们分别维护一个运行队列。也就是说,这2个CPU可以同时调度不同的程序
并行:任何时刻,都同时有多个进程在真的同时运行
现在我的电脑只有一个CPU(CPU内部会有非常多的寄存器,如:eax,eip,CR0~CR4等等),存在进程1和进程2
CPU先调度进程1,此时CPU的寄存器中会保存进程1的临时数据。但是在时间片内,进程1还没有执行完成,要从CPU上剥离,这时CPU会将寄存器内的临时数据存入进程的task_struct中,CPU的寄存器内的临时数据也叫进程的上下文,即将上下文数据存入task_struct中。等到再次被CPU调度时,CPU就会从上一次执行结束的位置开始执行。
再调度进程2,过程同上
进程在切换,最重要的一件事情:上下文数据的保护(CPU将寄存器中的临时文件存入task_struct)和恢复(上下文数据存入寄存器中)
CPU内的寄存器本身是硬件,具有数据的储存能力,CPU的寄存器硬件只有一套。
CPU内部的数据可以有多套,有几个进程,就有几套和该进程对应的上下文数据
拓展:
上面这个简单的C语言代码中,c是局部变量,那是通过什么能将c的值返回呢?寄存器
(B)阻塞(S/D)
在了解阻塞状态之前,先思考一个问题:我们使用C语言里的scanf函数,如果我们不在键盘上输入,那么该进程处于什么状态?
修改.c文件
我们发现,当scanf等待我们键盘输入时,进程状态是S
所以这本质上就是阻塞。这是在等待键盘资源是否就绪,简单来说就是键盘上有没有被用户按下的按键以及按键数据是否交给进程。
我们再来画一下冯诺依曼体系结构
先画出结构的倒数第一二层:硬件和驱动层(每个硬件的厂商都会写自己硬件的驱动程序)
我们知道,操作系统是管理软硬件资源的。管理软件:进程等。那操作系统是怎么管理硬件的呢?依旧是先描述,再组织。下面所写的并非是Linux内核的真正实现,而是它的原理的说明
比如说我们定义一个设备在操作系统的结构体。进程为什么能够等待设备资源,因为设备的结构体有等待队列
每个设备都有自己的struct device,操作系统为了将设备管理起来,将所有设备的struct device用指针联系起来。最终,对设备的管理就变成了对设备链表的增删查改
现在我们要启动testStatus进程,进程里面有scanf函数。该进程要被CPU调度,执行scanf()函数的时候发现要访问键盘资源,而操作系统检测键盘的工作状态发现它并不ok(用户没有在键盘上输入)
所以将该进程从运行队列中剥离出来,放入键盘的等待队列中。比如又有一个进程要等待磁盘资源,那么也要将该进程从运行队列中剥离出来,放入磁盘设备的等待队列中。所以,不是只有CPU才有运行队列,各种设备也有自己的等待队列
此时有2个进程不在运行队列,就不会被调度,因此叫这样的进程为阻塞进程。
等到用户按了键盘上的按键,通过驱动程序会被操作系统最先知道,知道之后,将设备的struct device的工作状态改为ok,再将在设备队列里的进程重新链回到运行队列中(这个过程叫唤醒),等CPU再次调度时,会执行scanf后续代码,将键盘设备的数据读取
阻塞和运行的状态变化,往往伴随着pcb被链入不同的队列中。入队列的不是代码和数据,而是进程的task_struct
(C)挂起
假设只有3个进程,如果此时操作系统内存特别吃紧,且进程处于阻塞态,那么操作系统会将内存中的代码和数据加载到磁盘的swap分区,内存中的代码和数据的空间会被释放,进程任然存在。这个过程叫唤出。
swap分区一般是内存的1-2倍大
如果进程等待的资源就绪了,开始被调度了,即状态由S变成R,那么进程对应的代码和数据会从swap分区加载到内存中。这个过程叫唤入
好了,那么本篇博客就到此结束了,如果你觉得本篇博客对你有些帮助,可以给个大大的赞👍吗,感谢看到这里,我们下篇博客见❤️