进程的特性是动态,并发,独立,异步,我们今天来浅显的从并发入手介绍各种知识
系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。
为了高效完成任务,更合理竞争相关资源,便具有了优先级
文章目录
- 1.并发与并行
- 2.进程优先级
- 2.进程的切换
- 3.简单介绍进程的调度
1.并发与并行
与并发相对的还有一个名词是并行。
并发就是多个进程在一个时间段内都被cpu运行过一次,每个进程不是一直占有cpu直到把它的任务完成,而是每隔一段时间就会从cpu上剥离下来。
而并行就是多个cpu同时运行多个进程。
一般市面上的电脑现在都是一颗cpu,不考虑其他,他的进程的运行方式肯定是并发。既然是并发的方式,那在一时刻,运行的进程肯定只有一个。有人会说那我的电脑为什么可以同时听着歌打着游戏还能看看视频呢,我的电脑是多cpu吗?其实大概率不是多cpu,而是进程的运行方式还是并发,只是每个进程在cpu上的运行时间太短,进程切换的太快,以至于用户反应不过来。我们介绍进程状态的时候说处于运行状态的进程都在运行队列中,处于运行队列中的进程也都排好队处于随时被调度的状态。为什么要排队呢?说到底还不是cpu太少了,同一时刻无法运行多个进程。所以排队的本质还是资源的不足。而进程的调度,又跟优先级有关。
2.进程优先级
我们先看一下什么是优先级:
我们使用ps命令的-al选项后可以看到有一参数是PRI,这个就是一个进程的优先级,对于用户来说进程的优先级在60~99,总共四十个等级。Linux中进程默认的优先级都是八十,而优先级也只不过是pcb结构体中的一个int类型的字段而已。
优先级数值越小,对应进程的优先级就越大。
而Linux中优先级是可以被修改的,但是它的的修改不是直接修改,而是通过上图中NI值来间接修改。
调整规则:pri = 旧的pri值 + NI值
上图是通过修改NI值来修改优先级的,有root权限下具体操作是在命令行输入top指令->输入r->输入想要修改的进程id->输入要调整的NI值。还有很多种修改NI值的方法,有兴趣可以自行去探索。
在调整NI值的时候我们发现当NI值调整到一定限度之后例如小于-20或者大于19后进程的优先级都是60和99,前面说过Linux中进程优先级是有范围的,用户所启动的的进程优先级是不能越过这个范围的。
为什么要把优先级限制在一定范围内呢?目的就是为了每个进程都能够被合理的以较为公平的方式调度运行。而出现优先级较低的进程,长时间无法得到cpu资源,这种进程叫做饥饿进程。
2.进程的切换
我们说了,并发是指在一个时间段内运行多个进程。每个进程运行一段时间后不管你的任务完成与否,都要从cpu上剥离下来,而上面说的一段时间是由时钟芯片每个一个特定时间发送电子脉冲给cpu实现的。这个时钟芯片以纳秒为单位,他可能每隔1微秒(具体不清楚)就发射一个电子脉冲。还有一个现象就是我们的电脑长时间关机后比如二十多天,打开后他的时间仍然是对的,但是以年为单位,再次打开我们的电脑,时间就不对了。这也是时钟芯片没电了。
那么进程是如何切换的呢?
我们知道cpu中有各种各样的寄存器,它们有的寄存器用来建立栈帧,初始化栈桢,ebp、esp等等,我们今天还要认识一个寄存器eip,它是存储cpu将要执行的下一句代码的地址。
进程在被调度运行的时候会产生各种临时数据那这些临时数据肯定是要被保存的,它们就被保存在寄存器中,每个进程在cpu寄存器中形成的临时数据叫做硬件的上下文。而每个进程的硬件上下文应该是不一样的,而一颗cpu只有一套寄存器,而它要记录多个进程的临时数据,显然是不可能的,所以寄存器 != 寄存器内容寄存器的内容应该是在pcb中,而pcb是在内存中的。所以进程切走时这些临时数据暂且理解为存储在pcb中。下一个被调度到的进程,就会把它的临时数据放到寄存器上。本质是cpu寄存器中的内容存到内存中。
3.简单介绍进程的调度
上面是Linux2.6.10的源码。
接下来我以我的认知大概讲解一下用户的一个进程它在运行队列里是如何被调度的:首先这种调度方式的效率可以达到O(1)。
运行队列结构体中有两个相同类型的指针它们分别指向两个结构体prio_array,prio_array结构体里有一个位图,还有一个是struct list_head类型的结构体数组queue。struct list_head这个结构体里就是链接进程pcb的指针。
可以看到queue数组的大小是140,而Linux操作系统中实时优先级的范围是0 ~ 99,还有四十个优先级是非实时进程的优先级,这个我们不讨论。我们主要探讨的是用户的60 ~ 99的优先级。我们在命令行启动一个进程的默认优先级是80。
当需要运行的进程被链入到运行队列中的active指向的queue后,会先根据优先级依次调度优先级从大到小的次序调度进程(而对于同等优先级的进程调度次序会有着更加细致的调度方式)。那此时假如还有优先级80的进程要加入运行队列中,而此时queue中的进程有优先级66,80,90的进程,那么它会不会直接加入这个队列中,因为如果这样的规则允许的话,优先级90的进程永远不会被调度到了,所以是不合理的。而是这个进程会被链入到另一个指针expired所指向的结构体中的queue中,等到active中的进程全部被调度完成后,类似于交换两个指针的内容swap(active, expired),从而再次调度曾经链入expired中的进程。
而操作系统是如何知道queue中是否有进程呢?数组就那么大,遍历也可以,但是还是费时间,所以采用了位图的方法来判断queue中有无进程。只需要判断01即可。
所以当一个进程在cpu上运行一段时间后,它会被剥离下来,然后cpu上会上新的被调度的进程实现进程的切换,然后再被链入到expired指向的queue中,继续准备被调度。这种调度的方式就可以形成一个循环。
而因这种调度方式有:Linux支持进程之间进行cpu资源抢占的基于时间片的轮转式抢占式内核。
而其中抢占式的理解可以是当一个进程B进入到running stage的时候(还未被调度),调度器会去检查进程B的优先级,如果进程B的优先级比目前正在执行的进程A的优先级高的话,linux便会抢占(preemptive)进程A,进程A便被打断执行,此时进程B会得到执行。(最后这个对抢占的理解源于链接: linux 内核抢占那些事)。
以上要是有讲述不对的地方,望请指正。