操作系统——进程优先级切换调度与调度算法

目录

1.进程优先级

1.1 基本概念

1.2 查看系统进程

1.3 PRI and NI

1.4  PRI vs NI

1.5  查看进程优先级的命令

1.6 补充概念-竞争、独立、并⾏、并发

2. 进程切换

Linux2.6内核进程O(1)调度队列 


1.进程优先级

1.1 基本概念

(1)cpu资源分配的先后顺序,就是指进程的优先权(priority)。

为什么要有优先级呢?

答:“目标(cpu)资源稀缺,导致要通过优先级来确定谁先谁后的问题”。

优先级与权限概念对比:

优先级:能够得到资源,先后的问题。

权限:是否能得到某种资源。
(2)优先权⾼的进程有优先执⾏权利。配置进程优先权对多任务环境的linux很有⽤,可以改善系统性能。
(3)还可以把进程运⾏到指定的CPU上,这样⼀来,把不重要的进程安排到某个CPU,可以⼤⼤改善系统整体性能。

1.2 查看系统进程

在linux或者unix系统中,⽤ps ‒l命令则会类似输出以下⼏个内容:

我们很容易注意到其中的⼏个重要信息,有下:

(1)UID : 代表执⾏者的⾝份

UID=user ID,也就是使用者id,它通过UID来确认当前访问文件文件的人是拥有者,所属组还是other

那么UID是怎样通过UID来确认访问者的身份的呢?

答:“将当前访问者的UID与文件的拥有者,所属组和other依次比较,如果UID与拥有者匹配,那访问者就是拥有者,如果不是,继续与所属组匹配,匹配成功,访问者就是所属组,如果都匹配失败,那访问者就是other”。Linux系统中,访问任何资源,都是进程在访问,进程就代表用户
(2)PID : 代表这个进程的代号。
(3)PPID :代表这个进程是由哪个进程发展衍⽣⽽来的,亦即⽗进程的代号。
(4)PRI :代表这个进程可被执⾏的优先级,其值越⼩越早被执⾏,也是一个进程的优先级,默认为:80。
(5) NI :进程优先级的修正数据,代表这个进程的nice值,默认为0,进程修改优先级就是通过修改nice值来实现。

1.3 PRI and NI

• PRI也还是⽐较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执⾏的先后顺序,此
值越⼩进程的优先级别越高。

优先级在系统中,也用数字表示。值越低,优先级越高,值越高,优先级越低。


• 那NI呢?就是我们所要说的nice值了,其表示进程可被执⾏的优先级的修正数值。


• PRI值越⼩越快被执⾏,那么加⼊nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice.

进程真实的优先级=PRI(默认)+NI
• 这样,当nice值为负值的时候,那么该程序将会优先级值将变⼩,即其优先级会变⾼,则其越快
被执⾏。


• 所以,调整进程优先级,在Linux下,就是调整进程nice值。


• nice其取值范围是-20⾄19,⼀共40个级别。

基于时间片的分时操作系统,考虑公平性,优先级可能变化,但是变化幅度不能太大

那么为什么要有nice值呢?这个问题后面回答。

1.4  PRI vs NI

(1)需要强调⼀点的是,进程的nice值不是进程的优先级,他们不是⼀个概念,但是进程nice值会影响到进程的优先级变化。

(2)可以理解nice值是进程优先级的修正修正数据。

1.5  查看进程优先级的命令

•top
•进⼊top后按“r”‒>输⼊进程PID‒>输⼊nice值

注意:

• 其他调整优先级的命令:nice,renice
• 系统函数

1.6 补充概念-竞争、独立、并⾏、并发

• 竞争性: 系统进程数⽬众多,⽽CPU资源只有少量,甚⾄1个,所以进程之间是具有竞争属性的。为了⾼效完成任务,更合理竞争相关资源,便具有了优先级。


• 独⽴性: 多进程运⾏,需要独享各种资源,多进程运⾏期间互不⼲扰。

例如我们在计算机上关闭浏览器,不会影响我们另一个正在运行的音乐程序。


• 并⾏: 多个进程在多个CPU下分别,同时进⾏运⾏,这称之为并行。


• 并发: 多个进程在⼀个CPU下采⽤进程切换的方式,在⼀段时间之内,让多个进程都得以推进,称之为并发。

2. 进程切换

死循环进程如何运行

(1)一旦一个进程占有cpu,不会把代码跑完。我们的计算机操作系统绝大多数都是分时操作系统,它给每个正在运行的进程分配一个时间片,一个进程在cpu运行一段时间后换下一个等待运行的进程。

(2)死循环进程,不会让系统崩溃,因为死循环进程不会一直占有cpu资源

寄存器是什么

(1)寄存器是cpu内存放数据的临时空间。存放的是运算器计算好的一些数据。

(2)寄存器!=数据。寄存器是一个空间,这就意味着空间只能有一份,

   CPU上下⽂切换:其实际含义是任务切换, 或者CPU寄存器切换。当多任务内核决定运⾏另外的任务时, 它保存正在运⾏任务的当前状态, 也就是CPU寄存器中的全部内容。这些内容被保存在任务⾃⼰的堆栈中, ⼊栈⼯作完成后就把下⼀个将要运⾏的任务的当前状况从该任务的栈中重新装⼊CPU寄存器,并开始下⼀个任务的运⾏, 这⼀过程就是context switch。

 进程切换最核心的,就是保存和恢复当前进程的硬件上下文数据,即cpu内寄存器的内容

小问题:

(1)当前进程要把自己的硬件上下文数据保存起来,保存到了哪里呢?

答:"保存到了当前进程的tesk_struct中”。

Linux2.6内核进程O(1)调度队列 

⼀个CPU拥有⼀个runqueue

如果有多个CPU就要考虑进程个数的负载均衡问题。

优先级

• 普通优先级:100〜139(我们都是普通的优先级,想想nice值的取值范围,可与之对应!)
• 实时优先级:0〜99(不关心)

活动队列

• 时间⽚还没有结束的所有进程都按照优先级放在该队列
• nr_active: 总共有多少个运⾏状态的进程
• queue[140]: ⼀个元素就是⼀个进程队列,相同优先级的进程按照FIFO规则进⾏排队调度,所以,
数组下标就是优先级!
• 从该结构中,选择⼀个最合适的进程,过程是怎么的呢?
1. 从0下表开始遍历queue[140]
2. 找到第⼀个⾮空队列,该队列必定为优先级最⾼的队列
3. 拿到选中队列的第⼀个进程,开始运⾏,调度完成!
4. 遍历queue[140]时间复杂度是常数!但还是太低效了!
• bitmap[5]:⼀共140个优先级,⼀共140个进程队列,为了提⾼查找⾮空队列的效率,就可以⽤
5*32个⽐特位表⽰队列是否为空,这样,便可以⼤⼤提⾼查找效率!

在Linux系统中,一个cpu有一个进程调度队列:

在队列中,有一个名为queue[140]的变量,它的全称为:struct task_struct* queue[140]。没错,它是一个指针数组,而它内部存储的是进程的优先级。那这么说,进程的优先级有140个吗?我们在上文不是说只有四十个吗?其实这两个说法都对,进程的优先级确实有140个,但是其中有100个是实时优先级,不被我们算在里面,剩下的40个分时优先级才应该是我们关注的对象。

在queue[140]数组中剩下的40个位置中,每一个都是task_struct* 类型,未来我们有多个优先级相同的进程,就会被依次链接到代表优先级为60的那个下标的位置中,按照先进先出的顺序依次执行,系统运行进程只需要依次查找这个数组,如果为空,就找下一个,不为空,就先执行链表的第一个、第二个...

总结:先看优先级,优先级相同则遵守先进先出规则。 

调度器如何快速地挑选一个进程呢?

   在队列中,还有一个bitmap[5]的数组,在它内部存着5个无符号整型,一个无符号整型是32位,5个就是160位,有20个不用,剩下的140位与进程的140个优先级一一对应,比特位的内容要么为0,要么为一,表示的是是否存在进程

 

 这样调度器运行进程分为两步:

(1)挑队列。在bitmap里1-5下标中查找,为0则无进程,为1则有进程,再到对应下标的比特位查找,0为无,1为有。

(2)挑进程。在对应的优先级中查找进程。

在数组中,还设置了一个变量:

nr_active,有进程就将它的状态设为1,如果没有进程状态就为0。这样操作系统要调度进程,先看nr_active是否为0,如果为0就没有进程,不再往下查找,如果为一,再去bitmap中查看单个进程的优先级下标。

过期队列

• 过期队列和活动队列结构⼀模⼀样

• 过期队列上放置的进程,都是时间⽚耗尽的进程
• 当活动队列上的进程都被处理完毕之后,对过期队列的进程进⾏时间⽚重新计算

 active指针和expired指针

• active指针永远指向活动队列
• expired指针永远指向过期队列
• 可是活动队列上的进程会越来越少,过期队列上的进程会越来越多,因为进程时间⽚到期时⼀直
都存在的。
• 没关系,在合适的时候,只要能够交换active指针和expired指针的内容,就相当于有具有了⼀批
新的活动进程!

问题:有一个优先级为60与一个优先级为99的进程,它们一开始都在自己的队列位置,优先级为60的进程先执行完,但是它如果被放回优先级为60的那个链表中,优先级为90的那个进程就一直无法被执行,显然这个问题被解决了,那么这个问题是如何被解决的呢?

答:系统内部定义了一个结构体:

有一个struct requeue_elem类型的数组:

数组中包含两个变量:

一个变量代表一个调度队列,active与expired,待执行的进程始终被active指向,执行完的进程会被链接到expired中指定的优先级位置中,这样active指向的队列中进程会越来越少,expired指向的队列中的进程越来越多,当active指向的队列没有进程时,交换这两个指针指向的队列:

swap(&active,&expired);

然后继续执行上述操作,这就是时间复杂度为O(1)的进程调度队列算法。

总结

在系统当中查找⼀个最合适调度的进程的时间复杂度是⼀个常数,不随着进程增多⽽导致时间成
本增加,我们称之为进程调度O(1)算法!

本章完。 

猜你喜欢

转载自blog.csdn.net/qq_58761784/article/details/144050519
今日推荐