linux o(1)调度

Linux2.6实现O(1)调度,每个CPU都有两个进程队列,采用优先级为基础的调度策略。内核为每个进程计算出一个反映其运行“资格”的权值,然后挑选权值最高的进程投入运行。在运行过程中,当前进程的资格随时间而递减,从而在下一次调度的时候原来资格较低的进程可能就有资格运行了。到所有进程的资格都为零时,就重新计算。

调度程序运行时,要在所有可运行的进程中选择最值得运行的进程。选择进程的依据主要有进程的调度策略(policy)、静态优先级(priority)、动态优先级(counter)、以及实时优先级(rt-priority)四个部分。首先,Linux从整体上区分为实时进程和普通进程,二者调度算法不同,实时进程优先于普通进程运行。进程依照优先级的高低被依次调用,实时优先级级别最高。

从某种意义上讲,所有位于当前队列的任务都将被执行并且都将被移到“过期”队列之中(实时进程则例外,交互性强的进程也可能例外)。当这种事情发生时,情况就会有所变化,队列就会被进行切换,原来的“过期”队列成为当前队列,而空的当前队列也就变成了过期队列。

schedule()函数是完成进程调度的主要函数,并完成进程切换的工作。schedule()用于确定最高优先级进程的代码非常快捷高效,其性能的好坏对系统性能有着直接影响,它在/kernel/sched.c 中的定义如下:

{

...

int idx

...

preempt_disable()

...

idx = sched_find_first_bit( array -> bitmap)

queue = array -> queue + idx

next = list_entry( queue -> next, task_t, run_list)

...

prev = context_switch( rq, prev, next)

...

}

其中,sched_find_first_bit()能快速定位优先级最高的非空就绪进程链表,运行时间和就绪队列中的进程数无关,是实现 O(1)调度算法的一个关键所在。schedule()的执行流程:

首先,调用 pre_empt_disable(),关闭内核抢占,因为此时要对内核的一些重要数据结构进行操作,所以必须将内核抢占关闭;其次,调用 sched_find_first_bit()找到位图中的第1个置1的位,该位正好对应于就绪队列中的最高优先级进程链表;再者,调用context_switch()执行进程切换,选择在最高优先级链表中的第1个进程投入运行;详细过程如图所示:

 

Linux进程调度

图中的网格为140位优先级数组,queue[7]为优先级为7的就绪进程链表。此种算法保证了调度器运行的时间上限,加速了候选进程的定位过程。

时间片的计算方法与时机:

Linux2.4 调度系统在所有就绪进程的时间片都耗完以后在调度器中一次性重新计算,其中重算是用for循环相当耗时。

Linux2.6为每个CPU保留 activeexpired两个优先级数组,active 数组中包含了有剩余时间片的任务,expired数组中包含了所有用完时间片的任务。当一个任务的时间片用完了就会重新计算其时间片,并插入到expired队列中,当 active队列中所有进程用完时间片时,只需交换指向activeexpired队列的指针即可。此交换是实现O(1)算法的核心,由schedule()中以下程序来实现:

array = rq ->active

if (unlikely(!array->nr_active)) {

rq -> active = rq -> expired

rq -> expired = array

array = rq ->active

...

}

Linux进程有140个优先级,前100个分配给实时进程,后40个给普通进程使用。

 Linux2.6 中,仍有三种调度策略:SCHED_OTHERSCHED_FIFO  SCHED_RR

1.3.3.    普通进程

SCHED_ORHER:普通进程,基于动态优先级进行调度,其动态优先级可以理解为调度器为每个进程根据多种因素计算出的权值。

Linux2.6中,优先级prio的计算不再集中在调度器选择next进程时,而是分散在进程状态改变的任何时候,这些时机有:

进程被创建时;

休眠进程被唤醒时;

TASK_INTERRUPTIBLE 状态中被唤醒的进程被调度时;

因时间片耗尽或时间片过长而分段被剥夺 CPU 时;

在这些情况下,内核都会调用 effective_prio()重新计算进程的动态优先prio并根据计算结果调整它在就绪队列中的位置。

struct task_struct{

...

int prio,static_prio

prio 是动态优先级,static_prio 是静态优先级(与最初nice相关)

...

prio_array_t *array

记录当前 CPU 的活跃就绪队列

unsigned long sleep_avg

进程的平均等待时间,取值范围[0MAX_SLEEP_AVG],初值为0

sleep_avg反映了该进程需要运行的紧迫性。进程休眠该值增加,如果进程当前正在运行该值减少。是影响进程优先级最重要的元素。值越大,说明该进程越需要被调度。

...

}

1.3.4.    实时进程

SCHED_FIFO:实时进程,实现一种简单的先进先出的调度算法。

SCHED_RR:实时进程,基于时间片的SCHED_FIFO,实时轮流调度算法。

SCHED_FIFOSCHED_RR的区别是:当进程的调度策略为前者时,当前实时进程将一直占用CPU直至自动退出,除非有更紧迫的、优先级更高的实时进程需要运行时,它才会被抢占CPU;当进程的调度策略为后者时,它与其它优先级相同的实时进程以实时轮流算法去共同使用CPU,用完时间片放到运行队列尾部,注意实时进程并不会放入过期队列中。

虽然在一个CPU内,实时进程的调度方式可以认为是严格优先级的,但是对于SMP系统,每个CPU都有自己的运行队列,实时进程被分配到各CPU队列,高优先级的实时进程并不一定比低优先级的先运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值