[修改回复]
删除回复
插入表情:
宋体
楷体
幼圆
黑体
隶书
华文行楷
方正舒体
Arial
Arial Black
Arial Narrow
Century Gothic
Comic Sans MS
#0000FF
#8A2BE2
#DEB887
#5F9EA0
#7FFF00
#000000
#D2691E
#FF7F50
#FF0000
#DC143C
#99ccff
字体颜色
#FFF8DC
#00FFFF
#EE82EE
#F5DEB3
#FFFFFF
#F5F5F5
#FFFF00
#9ACD32
使用帮助
三、调度策略 1. 进程优先级 (1) 优先级的计算 前面已经说过,优先级由两部分构成,一是静态优先级static_prio,一是动态优先 级prio。静态优先级在进程创建的时候就被赋值,并且不变(除非用系统调用改变进 程的nice值);而进程的动态优先级则是跟static_prio和sleep_avg有关。对于实时 进程的优先级在创建的时候就确定了,而且一旦确定以后就不再改变,所以下面部分 仅对于非实时进程而言。具体的计算由函数effecitve_prio()(kernel/sched.c)完 成。 函数将进程的sleep_avg映射成范围是-MAX_BONUS/2 ~ MAX_BONUS/2的变量bonus,而 MAX_BONUS是等于 ,可见sleep_avg仅能影响的优先级范围在-5 ~ 5之间。具体的映 射是由以下规则完成的: 那么进程的动态优先级就等于: (当然必须在MAX_RT_PRIO和MAX_PRIO-1之间 )。可见,sleep_avg和bonus是一个线性关系。进程的sleep_avg越大,bonus越大, 从而进程的动态优先级也就越高。 (2) 何时计算优先级 计算进程的动态优先级一般调用两个函数,一个是effective_prio(),一个是 recalc_task_prio()。函数recalc_task_prio ()先要根据进程被唤醒前的状态 (即actived)、interactive_credit等来计算进程的sleep_avg (详见"平均等待时间sleep_avg"一节),在最后调用effective_prio()来计算函数 的动态优先级。总的来说,有以下几种情况需要计算进程的优先级: a. 创建新进程,使用函数effective_prio()(因为此时进程尚未进行调度,没有 sleep_avg和interactive_credit可言); b. 唤醒等待进程时,使用函数recalc_task_prio ()来计算进程动态优先级。 c. 进程用完时间片以后,被重新插入到active array或者expired array的时候需要 重新计算动态优先级,以便将进程插入到队列的相应位置。此时,使用函数 effective_prio(); d. 其他情况,如IDLE进程初始化等时候。 2. 进程时间片 (1) 时间片的计算 进程的时间片time_slice是基于进程静态优先级的,静态优先级越高(值越小),时 间片就越大。计算时间片是同过函数task_timeslice()(kernel/sched.c)来完成的 MAX_BONUS是等于 ,可见sleep_avg仅能影响的优先级范围在-5 ~ 5之间。具体的映 射是由以下规则完成的: 那么进程的动态优先级就等于: (当然必须在MAX_RT_PRIO和MAX_PRIO-1之间 )。可见,sleep_avg和bonus是一个线性关系。进程的sleep_avg越大,bonus越大, 从而进程的动态优先级也就越高。 (2) 何时计算优先级 计算进程的动态优先级一般调用两个函数,一个是effective_prio(),一个是 recalc_task_prio()。函数recalc_task_prio ()先要根据进程被唤醒前的状态 (即actived)、interactive_credit等来计算进程的sleep_avg (详见"平均等待时间sleep_avg"一节),在最后调用effective_prio()来计算函数 的动态优先级。总的来说,有以下几种情况需要计算进程的优先级: a. 创建新进程,使用函数effective_prio()(因为此时进程尚未进行调度,没有 sleep_avg和interactive_credit可言); b. 唤醒等待进程时,使用函数recalc_task_prio ()来计算进程动态优先级。 c. 进程用完时间片以后,被重新插入到active array或者expired array的时候需要 重新计算动态优先级,以便将进程插入到队列的相应位置。此时,使用函数 effective_prio(); d. 其他情况,如IDLE进程初始化等时候。 2. 进程时间片 (1) 时间片的计算 进程的时间片time_slice是基于进程静态优先级的,静态优先级越高(值越小),时 间片就越大。计算时间片是同过函数task_timeslice()(kernel/sched.c)来完成的 。该函数也是使用线性映射的方法,将进程优先级[MAX_RT_PRIO, MAX_PRIO-1]映射 到时间片[MIN_TIMESLICE, MAX_TIMESLICE]范围内。通过优先级来计算时间片的等式 为: timeslice = MIN_TIMESLICE + ((MAX_TIMESLICE - MIN_TIMESLICE) * (MAX_PRIO-1- (p)->static_prio) / (MAX_USER_PRIO-1)) (2) 何时计算时间片 当就绪进程的所有进程的时间片都是0的时候,许多操作系统(包括旧版本的Linux) 是使用下面的循环来给进程队列计算时间片的: for (each task on the system) { recalculate priority; recalculate timeslice } 这样的循环计算会导致以下问题: 循环可能会花很长时间,而且算法的复杂度O(n); 计算过程中必须给进程队列和task_struct上锁,这样可能导致大量的竞争; 因为计算时间不可预计,所以可能给实时进程带来问题; 在Kernel 2.6中时间片的计算是分散的,具体的计算既可以用task_timeslice(),也 可以用其他方法。 a. 进程创建时,将父进程的时间片分一半给子进程,同时父进程的时间片减半。 (详见"sched_fork"一节); b. 进程用完时间片以后,需要重新计算时间片,并将进程插入到相应的运行 队列。(详见"scheduler_tick"一节); c. 进程退出时,根据first_timeslice的值来决定是否将子进程的时间片返 还给父进程。(详见"退出调度"一节)。 可见Kernel2.6通过分散计算时间片的办法很好解决了上面循环计算所带来的几个问题。 3. 平均等待时间sleep_avg 平均等待时间sleep_avg既决定了进程优先级,又影响了进程交互程度的,因此它是 Kernel 2.6调度系统里面很复杂的一块。下面将跟踪调度器中sleep_avg的变化情况。 (1) 进程创建 当一个进程被创建的时候,父进程的sleep_avg要乘以"PARENT_PENALTY / 100",子 进程的sleep_avg要乘以"CHILD_PENALTY / 100",PARENT_PENALTY=100,而 CHILD_PENALTY = 95,可见创建以后子进程的sleep_avg要降低,而父进程则不变。 (2) 进程被唤醒 当一个进程被唤醒以后,acitvate_task()将调用函数recalc_task_prio()来计算进 程的sleep_avg,参数是进程的睡眠时间,从而进一步计算进程的动态优先级。计算 sleep_avg有以下几种可能(当然都需在0 ~ NS_MAX_SLEEP_AVG范围内): a. MAX_SLEEP_AVG - AVG_TIMESLICE 当用户进程(p->mm)不是由UNINTERRUPTIBLE状态唤醒(p->activated != -1),且 睡眠时间大于INTERACTIVE_SLEEP(p),则做此赋值; b. 不变 当用户进程(p->mm)是由UNINTERRUPTIBLE状态唤醒(p->activated == -1),且" 交互程度"不高(!HIGH_CREDIT(p)),如果原来的sleep_avg已经大于INTERACTIVE_SLEEP (p),则不变(对非自愿睡眠的进程进行惩罚); 否则见下面一条; c. INTERACTIVE_SLEEP(p) 如果加上此次的睡眠时间后大于INTERACTIVE_SLEEP(p),则sleep_avg赋值为 INTERACTIVE_SLEEP(p); d. sleep_avg+sleep_time 如果以上条件全都不满足,则直接将本次睡眠时间加到sleep_avg上。 (3) 进程调度过程中 在schedule()过程中,如果发现优先级最高的程序是刚刚从TASK_INTERRUPTIBLE状态 被唤醒的进程(actived>0,参见"actived"的定义),那么将调用recalc_task_prio (),运算过程与(2)相同,所不同的就是调用时的参数sleep_time是进程在就绪队列 的等待时间。如果进程不是被中断唤醒的(actived=1),那么sleep_time还将受到 "(ON_RUNQUEUE_WEIGHT * 128 / 100) / 128"的限制,因为该进程很可能不是交互式 进程。 (4) 进程被剥夺CPU使用权 当进行进程切换的时候,被剥夺CPU使用权的进程的sleep_avg将会被减去进程的运行 时间run_time(这里的run_time对于交互式进程也有奖励的,详见"交互式进程优先 "一节),从而保证调度器的公平性。进程运行的时间越长,sleep_avg就越小(底限 是0),进程的动态优先级也就越低,从而被调度器调度到的机会也就会越小。 (5) 进程退出 当一个进程退出时,如果该进程的sleep_avg比父进程要小(也就是运行时间长), 那么父进程将得到惩罚。具体惩罚的规则为: p->parent->sleep_avg = p->parent->sleep_avg / (EXIT_WEIGHT+1) * EXIT_WEIGHT + p->sleep_avg / (EXIT_WEIGHT + 1); 父进程的sleep_avg将变为原来的1/( EXIT_WEIGHT+1),再加上子进程的sleep_avg的 1/( EXIT_WEIGHT+1),可见子进程运行的越多,父进程得到的惩罚也就越大。这样也 是为了保证调度器的公正性。 4. 交互进程优化 Kernel 2.6为了增加系统在高负载情况下的交互感受,做了以下三点优化。 (1) interactive_credit -- 奖励sleep_avg interactive_credit是设置在task_struct里面用来标记进程的"交互程度"的,它在 进程创建时候被置为0,以后随着不同的情况而增加,减少。 增加 interactive_credit有两处增1的地方,都在函数recalc_task_prio()里面。 a. 进程所拥有的内存区域不为空(p->mm!=NULL),即进程不是内核进程,如果不是从 TASK_UNINTERRUPTIBLE状态中被唤醒的(p->activated!=-1),且等待的时间(包 括在休眠中等待时间和在就绪队列中等待时间)超过了一定限度(sleep_time> INTERACTIVE_SLEEP(p));此时将interactive_credit增1; b. 进程的等待时间大于NS_MAX_SLEEP_AVG了,这种进程很可能是交互进程,所以 interactive_credit增1。 减少 interactive_credit只有一处地方减1,在函数schedule()里面。当进程将要被切换 出CPU的时候,要计算进程的运行时间run_time,并将进程的sleep_avg进行调整,如 果调整后的sleep_avg小于0(说明进程的运行时间大于等待时间),而且该进程的 interactive_credit在HIGH_CREDIT(p)和LOW_CREDIT(p)之间(说明该进程非交互进程), 则将interactive_credit减1作为对进程的惩罚。 从上面的分析可以看出,无论interactive_credit如何增减,它都在-(CREDIT_LIMIT +1) ~ (CREDIT_LIMIT+1)范围内;而且当interactive_credit增大到CREDIT_LIMIT+ 1,即调度器认定该进程为交互进程以后,interactive_credit就不再变化。 调度器采用宏HIGH_CREDIT()来判断一个进程是否是交互进程,如果是,则该进程将 得到以下奖励: a. 当进程被剥夺CPU使用权时,如果发现该进程是交互进程,则将该进程的运行时间 减小,run_time /= (CURRENT_BONUS(prev) ? : 1)。即sleep_avg减去的运行时间比 实际的运行时间要小,从而增加进程的sleep_avg。 b. 交互式进程在就绪队列上等待的时间也将增加到sleep_avg里面,p->sleep_avg += sleep_time;从而增加进程的sleep_avg。 可见,对于交互进程都是奖励sleep_avg的,从而达到提高优先级的目的。对于交互 式进程,调度器并没有在时间片上进行奖励,而是在优先级上进行奖励,是因为交互 式进程通常是运行时间短、睡眠时间长,而且要求响应快,而奖励优先级可以给交互 进程更多的运行机会,因此,调度器对于交互进程的奖励办法是非常公平和科学的。 (2) 平均等待时间sleep_avg -- 奖励动态优先级 在"平均等待时间"一节已做详细介绍。对于交互进程来说,因为它睡眠的时间较长, 所以sleep_avg要大一些。另外,经常处于TASK_INTERRUPTIBLE状态,而且是被中断 唤醒的进程最有可能是交互进程,而这种进程的衡量因素也是sleep_avg。 总之,由于交互进程一般sleep_avg较大,所以调度器通过奖励动态优先级的方式来 使得进程获得更多执行的机会。 (3) TASK_INTERACTIVE() -- 奖励再次被插入active array 这个宏是根据进程的动态优先级和静态优先级来判断该进程的"交互程度"。在进程时 间片用完时,使用这个宏作为一个参考因素来决定是否将进程重新插入active array 。它的定义是: (p)->prio <= (p)->static_prio - DELTA(p) DELTA(p) = (SCALE(TASK_NICE(p), 40, MAX_BONUS) + INTERACTIVE_DELTA) SCALE(v1,v1_max,v2_max) = (v1) * (v2_max) / (v1_max) 可以看出这个宏是将进程的动态优先级和进程的静态优先级做比较,以判断nice值为 n(静态优先级)时,进程p需要多大的动态优先级才能具有"足够的交互性"。从宏的 定义可以看出当进程的nice值大于12时,进程是不可能被认为是具有足够的交互性( 因为nice>12时,DELTA(p)>5,而由于sleep_avg给进程带来的动态优先级上的奖励最 大只有5,所以TASK_INTERACTIVE(p)永假);当进程的nice值为-20时,进程的sleep_avg 必须非常小才可能使得TASK_INTERACTIVE(p)值为假。 从以上分析可以看出,这三种奖励办法一个比一个奖励力度大,奖励条件也一个比一 个苛刻。而且调度器将用户的意愿放在了第一位(因为nice值是可以通过系统调用改 变的),由于用户的意愿而给予的奖励(再次被插入active array)最大,而调度器 所给予的奖励占的比例并不大。
不能为空
不能含有 ` 字符,字数8000以内
(CTRL+ENTER提交)
关闭窗口