Linux 2.6 内核进程调度队列
- 运行队列(runqueue)
- 蓝色区域(活动队列)
- queue
- bitmap
- nr_active
- 总结:时间片还没有结束的所有进程都按照优先级放在该队列(活动队列)
- 红色区域(过期队列)
- *active 和 *expired
Linux 内核究竟是如何调度队列的?
运行队列(runqueue)
在 OS 中会存在如下 运行队列,而每一个 CPU 都有一个这样的 运行队列:
蓝色区域(活动队列)
queue
注意此区域有一个叫 queue[140]
的东西,其真正的面目是这个:
task_struct* queue[140];
这是个数组,数组里是 task_struct*
类型的指针,可以存放 140 个进程 PCB
的地址
看似可以放 140 个,但实际上只会使用后 40 个([100, 139]
),好像很巧合,进程优先级就是 40 个级别,对上了?
没错, queue
下标 100 对应优先级 60,下标 139 对应优先级 99([60, 99]
)
那么相同优先级的进程 PCB
(task_struct
)就会以链表的形式链入 queue
对应的下表处,看似是维护了一个运行队列,实际上是维护了 40 个队列(链表)
那么想要找到一个 r
状态进程,只需要找到 运行队列 的 queue
,从优先级最高的 60,也就是下表 100 处开始 遍历取出 即可
bitmap
我们的进程可能压根不会从下标 100 (优先级 60)的位置进行链入,可能都是下标 120 (优先级 80)的位置链入,那 CPU 还需要从下标 100 的位置开始遍历吗?那不是麻烦吗?
而 bitmap[5]
就出来了:
long bitmap[5];
long
在 32 位是 4 字节,32 个 bit
,那 bitmap
数组的 5 个元素就有 5 * 32 = 160 个 bit
而就是用这 160 个 bit
表征 queue
里的 140 个指针是否有链入的进程,若 queue
某一下标有进程,那 bitmap
对应的位置上就是 1,反之则为 0
这样就能快速判断 queue
哪个下标位置上有进程,时间复杂度几近为 O(1)
nr_active
这个表示整个队列里有多少个进程,不用详述
总结:时间片还没有结束的所有进程都按照优先级放在该队列(活动队列)
红色区域(过期队列)
我们看了 蓝色区域 的 queue
等属性,不是这 红色区域 属性怎么和它一样是怎么回事
实际上 运行队列 会出现两套这样的结构;
queue
, bitmap
, nr_active
可以放一起成为一个小的结构体作为 运行队列 里的一个属性
利用这个小的结构体构建一个只有两个这种数据结构元素的数组,分别为: array[0]
, array[1]
而上图 array[0]
表示蓝色区域, array[1]
表示红色区域
- 过期队列 和 活动队列 结构一模一样
- 过期队列 上放置的进程,都是时间片耗尽的进程
- 当 活动队列 上的进程都被处理完毕之后,会对 过期队列 的进程进行时间片重新计算
*active 和 *expired
CPU 在找进程时不是直接访问这个队列的,而是先找 active
指针,active
指针指向 array
数组的哪一个元素就访问哪一个元素里的 queue
,CPU 始终调度 active
指向的队列
而 active
所指的队列是 只出不进 的, expired
所指的队列是 只进不出 的
也就是说新来的进程是只入 expired
队列, active
队列里某个进程被调度的时间片到了,也是直接入 expired
队列,直到 active
队列为空,没有进程被调度时, OS 直接将指针 active
和 expired
的内容交换即可
这就是 O(1)
调度算法,在系统当中查找一个最合适调度的进程的时间复杂度是一个常数,不随着进程增多而导致时间成本增加
active
指针永远指向 活动队列expired
指针永远指向 过期队列- 可是 活动队列 上的进程会越来越少, 过期队列 上的进程会越来越多,因为进程时间片到期时一直都存在的
- 没关系,在合适的时候,只要能够交换
active
指针和expired
指针的内容,就相当于有具有了一批新的 活动进程