1. 优先级是什么
优先级就是进程得到某种资源的先后顺序。优先级与权限的区别:
- 优先级:能获得资源,拥有资源的先后顺序。
- 权限:能不能获得资源。
1.1 为什么存在优先级
因为资源少,进程多。所以需要优先级来确定谁先拿资源,谁后拿资源。
2. Linux中的优先级
2.1 查看优先级
运行一个进程后使用ps命令来查看,有以下信息:
- UID:代表执行者的身份,也就是这个进程是由哪个用户创建的。
- PID:前面文章讲过,进程代号。
- PPID:父进程代号。
- PRI:进程的优先级,值越小越早被执行。
- NI:当前进程的nice值。

2.2 PRI and NI
NI 就是所说的nice 值,表示进程可被执行的优先级的修正数值。加入nice值后使PRI 变为:<math xmlns="http://www.w3.org/1998/Math/MathML"> P R I ( n e w ) = P R I ( o l d ) + n i c e PRI(new) = PRI(old) + nice </math>PRI(new)=PRI(old)+nice。
当nice值为负数,该进程优先级值就会变小,即优先级变高,则越快被执行。所以在Linux下,调整优先级就是调整nice值。
需要强调的是,进程的nice值与进程优先级不是一个概念,但是会影响到进程的优先级变化,可以理解nice值是进程优先级的修正数据。
2.3 更改nice值
使用top命令,进入后按 "r"->输入进程PID->输入nice值(注意:修改nice值需要root用户)。
查看当前进程
top命令
进入后按 r,输入当前进程PID
输入要修改的nice值(-10)
修改后
再次修改nice值为10(省略了中间过程,因为中途重启了一次进程导致PID不同)
再次修改后发现PID与NI 没有变回80和0 ,而是90和10,因为:

所以即使再次修改nice值为-15,我们所看到的就会是PRI:65 ,NI:-15。
其他修改nice值的命令:nice,renice。
2.4 nice值的范围
nice值的取值范围为 -20~19(一共40个级别) 。所以Linux的进程优先级也是有范围的:60~99(也是40个级别)。
那么为什么要有范围呢?
- 因为我们现在使用的操作系统大多数都是分时操作系统,要保证进程之间尽量的公平公正,所以优先级可以变但是要在可控的范围内变,如果优先级可以随意调整的话,那么就可能出现有的进程永远不会被调度的情况,这种情况也叫做饥饿问题。
3. 竞争、独立、并行、并发
- 竞争性:系统进程数目众多,而cpu资源只有少量,所以进程之间具有竞争属性,为了高效完成任务并且更合理的竞争资源,便有了优先级。
- 独立性:多进程运行间独享各种资源,期间互不干扰。
- 并行:多个进程在多个CPU下分别同时运行,称之为并行。
- 并发:多个进程在同一个CPU下采用进程切换的方式,在一段时间内让多个进程推进,称之为并发。
4. 进程切换
CPU内寄存器就是一套存储空间,寄存器只有一套,属于CPU本身,但是寄存器内部的数据可以有很多。
当前我们使用的操作系统都是分时操作系统,每个进程都有它自己的 "时间片(可以理解为一个计数器)",当时间片到达后,进程就被操作系统从CPU中剥离,进而执行下一个进程。
当进程A暂时被切换出去时,进程A会带走并保存自己在寄存器内的上下文数据,以便下次再调度时候按照之前的逻辑继续运行。
5. 进程调度
一个CPU,一个运行队列(runqueue),在运行队列中存在一个queue[140]
,它的类型是struct list_head
,更好理解一点可以理解成为struct task_struct*
,也就是一个指针数组。
图片来源于网络
当我们创建出一个进程后,会根据它的优先级插入到指定位置,这样当CPU调度进程的时候,实际上就是按顺序调度,但是初始创建的进程优先级都是80,所以假如运行队列中只有queue[120]
的位置存在大量进程,其他位置没有进程呢?岂不是还需要花时间遍历。
从图片中可以看到queue
上方还有一个bitmap[5]
(位图),每个比特位代表了queue
中对应位置是否存在进程,所以当需要调度进程时就查这一个位图,从而提升效率。
上面还有两个指针,分别是*active
和*expired
,*active
指针就指向了活跃进程这个结构,*expired
指向了过期进程的结构,所以:
- CPU调度的时候,直接通过
*active
找到对应的queue[140]
,新增进程、或者时间片到了的进程,被从CPU上剥离,链入到过期进程的队列中。 - 当活跃进程中所有进程都被执行完毕后,交换
*active
和*expired
两个指针指向的内容,从而能再次执行。

这时就可以知道为什么要有nice值了:当前进程在活跃进程时如果调整优先级还要进行移动操作,有了nice值后,只需要当前进程时间片到达后,加上nice值,按照调整后的优先级直接链入到过期进程中,等待下一轮再被调度时,就会直接按照调整后的优先级来调度。
那么在这种调度算法下会存在饥饿问题吗? 很明显不存在这种问题,因为在局部内(也就是每一轮调度)都会将当前所有进程调度完一遍后,再调度下一轮,即使某一个进程优先级特别高,但也要等上一轮调度完才能被调度,这就做到了局部上根据优先级调度,整体上不会造成饥饿问题。 这就是为什么要使用两个队列的原因。