1、2.10 Linux调度算法,2.10.1 Linux传统调度算法 2.10.2 Linux2.6调度算法,2.10.1 Linux传统调度算法(1),1 进程调度策略1) policy:SCHED_OTHER普通类任务SCHED_FIFO先进先出实时类任务SCHED_RR轮转法实时类任务2) priority进程静态优先级 3) nice进程可控优先级因子 4) rt_priority实时进程静态优先级 5) counter进程目前时间片配额,也称进程动态优先级,Linux传统调度算法(2),2 动态优先级的产生和变化 当counter递减到0时,运行进程被迫出让CPU;当可运行队列中所有进
2、程的counter值变为0后,表明一轮调度已经结束。 等待态进程的动态优先级通常会逐渐增加,当所有可运行进程的counter都为0时,系统重新计算所有进程的counter,计算公式为: p-counter=(p-counter1)+NICE_TO_TICKS(P-nice)对于就绪态进程来说,因其counter都为0,计算结果就是nice转换过来的时钟滴答数;对于等待态进程就不一样,它们的counter都不为0,计算结束后,等待态进程的动态优先级会大于nice值。,3 Linux进程调度机制,进程调度依据和时机 进程调度任务 进程切换,进程切换时机,(1) 进程被动地放弃CPU,当前进程时间片
3、用完或当一个进程被唤醒,且其优先级高于当前进程的优先级时,通过TIF_NEED_RESCHED位置1,来告诉内核在适当的时刻需要重新调度。 (2) 进程主动放弃CPU,是由于进程执行系统调用,状态发生变化,直接调用schedule( )进入调度,这类系统调用有:yield( )、pause( )、sleep( )、wait( )和exit( )。 (3) 进程执行等待系统调用,如read( )或write( )等,此时进程进入等待队列,系统调用schedule( )进入调度,该函数的执行结果往往是当前进程放弃处理器。,进程切换,上下文切换是从一个可运行进程切换到另一个可运行进程,由contex
4、t_switch()函数处理这项工作,每当一个新的进程被选出来准备投入运行时,schedule()就会调用该函数。 它首先调用switch_mm(),把虚拟主存从上一个进程映射切换到新进程中;再调用switch_to(),从上一个进程的处理器状态切换到新进程的处理器状态。,2.10.2 Linux2.6调度算法,0(1)调度程序-特点是能够保证无论系统负载(进程数目或处理器数目)如何增加,选择合适进程并且给它分配处理器的时间是恒定的,具有的新特性有:支持SMP,每个处理器拥有自己的可运行队列;强化SMP的亲和力,尽量将相关任务分配到一个处理器上连续运行;确保响应时间,及时调度交互式进程;保证公
5、平性,没有进程会处于饥饿状态。,Linux2.6中,进程描述符task_struct含有与调度有关的成员(1),1) policy-进程调度策略,有三种类型:SCHED_NORMAL 非实时进程;SCHED_FIFO 实时进程,采用先进先出的调度算法;SCHED_RR 实时进程,采用轮转法。,Linux2.6中,进程描述符task_struct含有与调度有关的成员(2),2) rt_priority-实时进程的优先级。其值为1000+rt_priority,而MAX_RT_PRIO定义为100,故rt_priority范围为0至99,且不参与优先级计算; 3) static_prio-非实时进
6、程静态优先级。由nice值转换而来,公式为:static_prio=MAX_RT_PRIO+nice-20。,Linux2.6中,进程描述符task_struct含有与调度有关的成员(3),4) sleep_avg-进程平均等待时间。相当于进程等待时间与运行时间的差值,既反映该进程的交互程度,又表示进程需要运行的紧迫程度,该值越大,进程的优先级就越高; 5) prio-进程动态优先级。在进程运行过程中动态计算,主要影响因素为sleep_avg。创建时子进程继承父进程的动态优先级、唤醒等待进程时对它进行优先级修正、时钟中断中重新计算进程优先级并进入相应队列、负载平衡/修改nice值/修改调度策略
7、(setscheduler()都有可能改变进程动态优先级;,Linux2.6中,进程描述符task_struct含有与调度有关的成员(4),6) prio_array_t *array-进程优先级数组。以进程优先级为序号排列; 7) time_slice-进程时间片余额。进程默认时间片与static_prio有关,内核将100至139的优先级映射列800ms至5ms的时间片区间;创建进程时,子、父进程平分父进程的剩余时间片。终止进程时,若子进程如果从未重新分配过时间片,则把剩余时间片归还给父进程;,Linux2.6中,进程描述符task_struct含有与调度有关的成员(5),8) load_
8、weight;-平衡负载用的权重。解决可运行队列出现的负载不均现象; 9)CONFIG_PREEMPT-内核可剥夺编译选项,当该开关开启时,v2.6内核将会在更多内核安全点上检测TIF_NEED_RESCHED位,从而让刚被唤醒的高优先级任务减少延迟而尽快获得处理器运行。,关于need_resched,每个进程都包含need_resched标志,这是因为通过current宏访问进程描述符内的数值要比访问全局变量快,在v2.2以前内核版本中,该标志曾经是一个全局变量; v2.2到v2.4版内核中它放在进程的task_ struct中; v2.6版中,它被移到thread_info结构体里,用特别
9、的标志变量中的一位TIF_NEED_RESCHED来表示。,1 调度算法的数据结构,调度程序中最主要的数据结构是可运行队列(runqueue),它是给定处理器上的就绪进程链表,每个处理器一个,每个就绪进程都归属于一个可运行队列。 它还包含每个处理器的调度信息,所以也是处理器的重要数据结构。,可运行队列(处理器)的优先级数组,一个是活跃的、一个是过期的。 struct prio_array int nr_active; /数组中的进程数unsigned long bitmapBITMAP_SIZE; /优先级位图struct list_head queueMAX_PRIO; /优先级队列;,2
10、进程优先级和时间片,实时进程具有静态优先级,范围为0到99。 非实时任务都有静态优先级nice,该数值变化范围为-20到19,默认值为0,直接对应于100到139的优先级范围,默认值则为120。 进程的动态优先级,它以nice值为基数,再加上-5到+5之间的进程交互性奖励或罚分。 如果进程的交互性非常强,当它时间片用完后,会被再次放置到活跃数组而不是过期数组中。,3 O(1)调度算法(1),O(1)调度算法(2),task_t *prev,*next; runqueue *rq; prio_array_t *array; int idx; prev = current; /保存当前进程 rq
11、= this_rq( ); /当前运行队列 array = rqactive; if (unlikely(!arraynr_active) /运行队列的active变成空 rqactive = rqexpired; /活跃、过期数组切换 rqexpired = array; array = rqactive; rqexpired_timestamp = 0; /进程耗完时间片事件的时间清0 rqbest_expired_prio = MAX_PRIO; /修改过期队列最高优先级 else idx = sched_find_first_bit(arraybitmap); /从active队列选择
12、进程 queue = arrayqueue + idx; next = list_entry(queuenext,task_t,run_list); /任务切换,O(1)调度算法(3),新调度程序减少对循环的依赖,活跃数组内的可执行队列上的进程都还有时间片剩余;过期数组内的可执行队列上的进程都耗尽时间片。 当某进程的时间片用完时,重新计算它的时间片,并被移至过期数组。 数组是通过指针进行访问的,活跃和过期数组之间来回切换所用的时间就是交换指针需要的时间。,4 负载平衡(1),load_balance()函数 找最繁忙的可运行队列,该队列中就绪进程数目最多,如果没有哪个可运行队列中就绪进程数目比
13、当前可运行队列中的就绪进程数目多25或以上,就结束平衡负载处理。 找最繁忙的可运行队列,从中选择一个优先级数组以便抽取就绪进程,最好是过期数组,因为那里面的就绪进程已经有相对较长时间没有运行,很可能不在处理器的高速缓存中。如果过期数组为空,那就只能选活跃数组。,4 负载平衡(2),找到含有进程且优先级最高的链表,把优先级高的就绪进程平均分散开来。 分析找到的所有优先级相同的就绪进程,选择一个不是正在执行,也不会因为处理器相关性而不可移动,且不在高速缓存中的进程。如果有进程满足这些条件,便将其从最繁忙的队列中抽取到当前队列。 只要可运行队列之间仍然不均衡,重复上面步骤,继续从繁忙队列中抽取进程到
14、当前队列,最终会消除不平衡局面。,5 用户抢占和内核抢占,Linux2.6完整地支持内核抢占,只要重新调度是安全的,内核就可在任何时间抢占正在运行的任务。 为了支持内核抢占所作的第一处修改是为进程引入preempt_count计数器,其初值为0,每当使用锁的时候数值加1,释放锁的时候数值减1,当数值为0时,内核就可执行抢占。 内核抢占代码使用自旋锁作为非抢占区域的标记,如果一个自旋锁被持有,内核便不能抢占。 内核代码需要允许或禁止内核抢占,可通过preempt_disable( )和preempt_enable( )实现。,2.11Windows 2003调度算法,处理器调度的对象是线程,也称
15、线程调度。 实现了基于优先级抢先式的多处理器调度系统,系统总是运行优先级最高的就绪线程。 线程可在任何可用处理器上运行,但可限制某线程只能在某处理器上运行,亲合处理器集合允许用户线程通过Win32调度函数选择它偏好的处理器。,时间配额,线程被调度运行时,可运行一个被称为时间配额(quantum)的时间。 时间配额是允许线程连续运行的最大时间长度,随后系统会中断线程运行,判断是否需要降低该线程的优先级,查找是否有其他高优先级或相同优先级的线程等待运行。 由于抢先式调度特征,一个线程的一次调度执行可能并没有用完它的时间配额就被抢先。,线程调度时机,线程调度出现在DPC/线程调度中断优先级,线程调度
16、触发事件有四种: 1)一个线程进入就绪状态,如一个刚创建的新线程或一个刚结束等待状态的线程。 2)一个线程由于时间配额用完而从运行状态转入退出状态或等待状态。 3)一个线程由于调用系统服务而改变优先级或被系统本身改变其优先级。 4)一个正在运行的线程改变了它的亲合处理器集合。,线程优先级,使用32个线程优先级,范围从0到31,分成三部分:实时优先级(优先数31-16):用于通信任务和实时任务。实时优先数线程的优先数不可变,一旦就绪线程的实时优先数比运行线程高,它将抢占处理器运行。 可变优先级(优先数15-1):用于交互式任务。这一层次优先数的线程,可根据执行过程中的具体情况动态地调整优先数,但
17、是15这个优先数是不能被突破的。一个系统线程优先级(优先数0),仅用于对系统中空闲物理页面进行清零的零页线程。,线程优先级示意,Windows 2003线程调度 数据结构,Windows 2003线程调度策略,1)主动切换2)抢先3)时间配额用完4)结束,Windows 2003线程线程 优先级提升,提升线程当前优先级的情况: 1)I/O操作完成。 2)信号量或事件等待结束。 3)前台进程中的线程完成一个等待操作。 4)由于窗口活动而唤醒图形用户接口线程。 5)线程处于就绪状态超过一定时间,但没能进入运行状态(处理器饥饿)。,对称多处理器系统上的线程调度,1)亲合关系 2)线程的首选处理器和第二处理器 3)就绪线程的运行处理器选择 4)为特定的处理器调度线程 5)最高优先级就绪线程可能不处于运行状态,