linux内核CFS进度调治战略

在空虚模型中vruntime决定了经过被调整的前后相继顺序,在一步一个足迹模型中央调节制被调整的前后相继顺序的参数是由函数entity_key决定的。 
 
static inline s64 entity_key(struct cfs_rq *cfs_rq, struct
sched_entity *se)
{
    return se->vruntime – cfs_rq->min_vruntime;
}
enqueue_task_fair—->enqueue_entity—->__enqueue_entity—->entity_key决定插入就绪队列的岗位。

一、概述

日常性进度分为叁19个级次,每一种阶段对应一个权重值,权重值用一个整数来标示。权重值定义在数组prio_to_weight[40]中;普通进度的权重值最大为88761,最小为15。暗许景况下,普通进度的权重值为1024(由NICE_0_LOAD钦赐)。weight是由进程的静态优先级static_prio决定的,静态优先级越高(static_prio值越小)weight值越大。普通进度的暗许nice值为0,即暗中同意静态优先级为120,它的weight值为prio_to_weight[20],即1024。因此NICE_0_LOAD的值就
为1024。

首先简要介绍一下根本的布署思路,
CFS思路非常easy。正是基于种种进程的权重分配施行时间(权重怎么来的前边再说)。
经过的施行时间总括公式为:
分红给进程的实践时间 = 调节周期 * 进程权重 / 全体经过权重之和  
(公式1)
调解周期非凡好驾驭。便是将全部高居TASK_RUNNING态进程都调治一次的时光,
差一些大同小异也就是O(1)调治算法中实践队列和过期队列切换一回的时日
(作者对O(1)调节算法看得不是格外熟,如有错误还望各位大虾提出)。
举个样例。比如仅独有多少个进度A, B,权重分别为1和2,
调节周期设为30ms,那么分配给A的CPU时间为
30ms * (1/(1+2)) = 10ms
而B的CPU时间为
 
30ms * (2/(1+2)) = 20ms
这正是说在这30ms中A将实行10ms。B将实施20ms。
公平怎么展示吗?它们的施行时间并不等同阿?
实则公平是体前段时间其余多个量上边。叫做virtual
runtime(vruntime)。它记录着进度已经施行的日子,
只是并非平素记录,而是要依靠进度的权重将实施时间放大只怕减少五个比重。
大家来看下从实际奉行时间到vruntime的折算公式
vruntime = 实际实行时间 * 1024 / 进度权重。 (公式2)
为了不把我们搞晕。这里本凡直接写1024。实际上它约等于nice为0的长河的权重,代码中是NICE_0_LOAD。
也正是说。全体历程都是nice为0的进度的权重1024作为标准。总计本身的vruntime加多快度。
还以下面AB多个进程为例。B的权重是A的2倍,那么B的vruntime增加快度只有有A的四分之二。

vruntime行走速度:
   
系统明确:暗中同意权重值(1024)对应的进程的vruntime行走时间与事实上运转时刻runtime是1:1的涉嫌。由于vruntime的走动速度和权重值成反比,那么别的进度的vruntime行走速度都通过以下三个参数总计获得:1、该进度的权重值2、暗中认可进度的权重值。
    举个例子权重为3096的历程的vruntime行走速度为:1024/3096 * (wall
clock)。
    “真实机械钟速度”即为runtime(即 wall clock)行走的进程。

前日我们把公式第22中学的实际推行时间用公式1来替换。可以获取那样贰个结实:
vruntime = (调治周期 * 进度权重 /
全体历程总权重) * 1024 / 进度权重=调治周期 * 1024 / 全体进度总权重
见到哪些形容未有?没错,即使经过的权重区别,可是它们的vruntime增速应该是平等的(这里所说的增速一样,是从宏观上来看的。从上一篇作品能够看出来。而在上一篇小说中说vruntime的增量不一样,是从公式深入分析获得的,算是局地解析,在公式第22中学,假使实际施行时间都是一致。非常肯定权重小的增高的多。权根本的增进的小,小编个人感觉便是虚拟石英钟的存在。转变了考虑。才有了这些CFS,事实上仍旧基于权重来决定三个经过在三个调用周期内施行了多久,但是虚构机械钟决定了怎么调治这些历程,那正是思量),与权重毫无干系。
好,既然全体进程的vruntime增速宏观上看应该是平等时候推动的。
那正是说就可见用那几个vruntime来抉择实行的进度。哪个人的vruntime值很小就印证它曾经占用cpu的岁月相当的短,
遭到了“有所偏向”对待,由此下二个施行进程正是它。

   
进度实践施行时期周期性调治器周期性地运行,其承受更新一些辅车相依数据,并不承担进度之间的切换:
   
timer_tick()—->update_process_times—->schedule_tick()
   
schedule_tick—->task_tick_fair—->entity_tick()—->update_curr()
    update_curr()函数完成相关数据的换代。
        update_curr()—->delta_exec = (unsigned long)(now –
curr->exec_start)
                              |–>__update_curr()
                              |–>curr_exec_start = now;
   
update_curr()函数只负担总结delta_exec以及更新exec_start。另外专门的学业由__update_curr()函数达成:
        1、更新当前历程的骨子里运营时刻(抽象模型中的runtime)。
        2、更新当前经过的杜撰时间vruntime。
        3、更新cfs_rq->min_vruntime。
          
在日前进度和下一个将在被调治的进度中挑选vruntime很小的值。然后用该值和cfs_rq->min_vruntime比较,如果比min_vruntime大,则更新cfs_rq为的min_vruntime为所求出的值。

那般既可以公平选拔进程,又能有限支撑高优先级进度
收获相当多的执行时间。
那正是CFS的非常重要观念了。

思虑下当创造新进度恐怕经过唤醒时,vruntime在切实地工作模型中的处理情势:
I、新建进度
   
进程的ideal_time长度和weight成正比,vruntime行走速度与weight值成反比。因而当各样进程在period时间内,都进行了温馨相应的ideal_time长期,那么他们的vruntime的增量相等。而nice为0的进度的vruntime行走速度等于runtime行走速度,所以各种进程都运营它和睦相应的ideal_runtime时间,另外进度的vruntime增量都等于nice值为0的进程的ideal_runtime。若是开头情形下,它们的全数进度的vruntime值都等于0,那么当贰个进程运营完runtime的岁月为ideal_time,那么它的vruntime将为最大,只要任何进度的运转总时间未曾高达各自对应的ideal_runtime值,那么它始终排在进度队列的末尾。

再补偿一下放权力重的来源,权重跟进度nice值之间有种种相应的涉嫌,能够透过全局数组prio_to_weight来转换,
nice值越大,权重越低

    对于新进程,task_fork_fair()->place_entity(cfs_rq, se,
1),其intial参数为1。新进度的vruntime值被设置为min_vruntime+sched_vslice(cfs_rq,
se),
sched_vslice()函数可总括出nice值为0的长河的ideal_runtime。其效用是将新加入的进程的符号为“它在period长日子内已经运维它对应的ideal_time短期”,那么新加盟进程在辩论上(全数进度都进行它对应的ideal_runtime时间,未有产生睡眠、进程终止等特殊景况)独有拭目以待period之后能力被调节。
    sched_vslice(cfs_rq,
se)—->calc_delta_fair(sched_slice(cfs_rq, se), se),
sched_slice()计算新建进度的ideal_runtime,calc_delta_fair()将ideal_runtime转换成vruntime。

以下来剖判代码。网络早已有十分多cfs的篇章。因而笔者企图换二个格局来写,选用多少个点来进展情景剖判,
包含进度成立时。进度被晋升,主动调解(schedule),时钟中断。

II、睡眠进程被唤醒
   
将经过的vruntime值设置为cfs_rq->min_vruntime值,然后再张开一下填补:将vruntime减去与sysctl_sched_latencyd相关的二个数值。进度进入梦眠情况时cfs_rq->min_vruntime就不仅或等于该进程的vruntime值,它在睡觉进度中vruntime值是不更换的,但是cfs_rq->min_vruntime的值却是单调扩大的,进程醒来后补充的量由sysctl_sched_latency给出,不管进度遭到的不公平待遇大照旧小,一律只补充这么多。

介绍代码在此之前先介绍一下CFS相关的构造
首先个是调治实体sched_entity,它象征二个调解单位。在组调节关闭的时候可以把她等同为进度。
每个task_struct中都有贰个sched_entity,进度的vruntime和权重都保留在那几个布局中。
那正是说万事的sched_entity怎么组织在一道吗?红黑树。全体的sched_entity以vruntime为key
(实际上是以vruntime-min_vruntime为单位,难道是制止溢出?反正结果是大同小异的)插入到红黑树中,
无差距于时候缓存树的最左側节点。也正是vruntime最小的节点,那样能够迅速选中vruntime最小的长河。
只顾仅唯有等待CPU的就绪态进程在那棵树上,睡眠进程和正在实施的长河都不在树上。
本身从ibm developer works上偷过来一张图来体现一下它们的关联:
汗。图片上传作用被关闭了。先盗链贰个上涨。别怪作者没品哈。。。

真实性模型总结:
   
a)进度在就绪队列中用键值key来排序,它并未有保留在任何变量中,而是在必要时由函数entity_key()总计得出。它极其
        key = task->vruntime – cfs_rq->min_vruntime
   
b)各类进度有两样的基本点(优先等第),越首要的进程权重值weight(task.se.load.weight)越大。
   
c)每种进度vruntime行走的快慢和weight值成反比。权重值为1024(NICE_0_LOAD)的进度vruntime行走速度和runtime同样。
   
d)各样进程每回得到CPU使用权最多施行与该进度对应的ideal_runtime长时间。该时长和weight值成正比,它从不用变量来保存,而是必要采纳sched_slice()函数总括得出。
   
e)ideal_runtime总括的原则是period,它也并未有用变量来保存,而是由__sched_period()总括得出。

 

 

图片 1

进度的开始的一段时期等第调整了其权重值,task_struct中与先行级相关数据成员:
   
a)static_prio,指普通进度的静态优先级(实时过程没用该参数),值越小则优先级越高。静态优先级是进度运行时分配的开始时期级。它能够用nice()或然sched_setscheduler()系统调用改造,不然在运行时期平素维持一定。

 

      
注意:关于a),注意本文的末尾加多的注释。

 

   
b)rt_priority,表示实时进程的优先级(普通进度没用该参数),它的值介于[0~99]之间。rt_priority的值越大其事先级越高。
   
c)normal_prio,由于static_prio和rt_priority与初期级的关联性分化,由此用normal_prio统一下“单位”,统一成:normal_prio值越小则进度优先级越高。因而,normal_prio也得以精通为:统一了单位的“静态”优先级。
   
d)prio,在系统中动用prio判定进度优先级,prio是经过的动态优先级,其代表经过的卓有成效优先级。对于实时进度来讲,有效优先级prio就等于它的normal_prio。普通进度能够权且提升优先级,通过改变prio完毕,动态优先级的增高不影响进程的静态优先级。父进度的动态优先级不会遗传给子进度,子进度的动态优先级prio开始化为父进度的静态优先级。

 

注:

后天開始分情景分析CFS。

出于在好几意况下须要一时半刻进步进度的优先级,由此不但必要静态优先级和日常优先级,还索要动态优先级prio;

 

参照他事他说加以考察《深切Linux内核架构》p70-76、
p_288-290;

二、创造进度 

        
linux内核的优先级承接左券(pip)

第一个现象选为进度创制时CFS相关变量的初阶化。
笔者们清楚。Linux创设进程使用fork恐怕clone大概vfork等种类调用,终于都会到do_fork。

         进度优先级翻盘难点的减轻  

倘若未有设置CLONE_STOPPED,则会跻身wake_up_new_task函数,大家看看那么些函数的十分重要部分

        为了在Linux中应用Priority
Inheritance
Protocol公约来消除先行级反转问题,Linux中引进实时互斥量rt_mutex,在task_struc结构体中也引进了pi_waiters链表,须要注意的流水线为:

[cpp] view
plaincopy

         rt_mutex_slowlock() —->
__rt_mutex_slowlock() —->

  1. /* 
  2.  * wake_up_new_task – wake up a newly created task for the first time. 
  3.  * 
  4.  * This function will do some initial scheduler statistics housekeeping 
  5.  * that must be done for every newly created context, then puts the task 
  6.  * on the runqueue and wakes it. 
  7.  */  
  8. void wake_up_new_task(struct task_struct *p, unsigned long clone_flags)  
  9. {  
  10.     …..  
  11.     if (!p->sched_class->task_new || !current->se.on_rq) {  
  12.         activate_task(rq, p, 0);  
  13.     } else {  
  14.         /* 
  15.          * Let the scheduling class do new task startup 
  16.          * management (if any): 
  17.          */  
  18.         p->sched_class->task_new(rq, p);  
  19.         inc_nr_running(rq);  
  20.     }  
  21.     check_preempt_curr(rq, p, 0);  
  22.     …..  
  23. }  

                
task_blocks_on_rt_mutex() —-> 
__rt_mutex_adjust_prio()

 上边十三分if语句作者不驾驭哪些动静下会为真。小编測试了须臾间。在上边八个支行各加三个计数器,
由此可见为确实际处情况无非有2次(笔者决不根据的推測是idle进度和init进度),而估算为假的景况有近万次。
于是大家只有看之下的道岔,即便哪位前辈知道真相的话还望告诉小编一声,十一分谢谢。

                                                                  
|–> rt_mutex_adjust_prio_chain()

再以下就是检測是或不是足以形成抢占,假若新历程能够抢占当前经过则张开进度切换。

         
__rt_mutex_adjust_prio调治了近年来有所锁的进度的动态优先级(承袭自等待队列中持有进程的参天优先级),rt_mutex_adjust_prio_chain()假如被调治的动态优先级的经过也在等候有个别能源,那么也要链式地调治相应进度的动态优先级。

大家一个一个函数来看
p->sched_class->task_new相应的函数是task_new_fair:

至于Priority
Inversion能够参谋《Operating System Concepts》9_ed p217-218
                                                                                                                      

[cpp] view
plaincopy

  1. /* 
  2.  * Share the fairness runtime between parent and child, thus the 
  3.  * total amount of pressure for CPU stays equal – new tasks 
  4.  * get a chance to run but frequent forkers are not allowed to 
  5.  * monopolize the CPU. Note: the parent runqueue is locked, 
  6.  * the child is not running yet. 
  7.  */  
  8. static void task_new_fair(struct rq *rq, struct task_struct *p)  
  9. {  
  10.     struct cfs_rq *cfs_rq = task_cfs_rq(p);  
  11.     struct sched_entity *se = &p->se, *curr = cfs_rq->curr;  
  12.     int this_cpu = smp_processor_id();  
  13.     sched_info_queued(p);  
  14.     update_curr(cfs_rq);  
  15.     place_entity(cfs_rq, se, 1);  
  16.     /* ‘curr’ will be NULL if the child belongs to a different group */  
  17.     if (sysctl_sched_child_runs_first && this_cpu == task_cpu(p) &&  
  18.             curr && curr->vruntime < se->vruntime) {  
  19.         /* 
  20.          * Upon rescheduling, sched_class::put_prev_task() will place 
  21.          * ‘current’ within the tree based on its new key value. 
  22.          */  
  23.         swap(curr->vruntime, se->vruntime);  
  24.         resched_task(rq->curr);  
  25.     }  
  26.     enqueue_task_fair(rq, p, 0);  
  27. }  

 这里有多个重大的函数,update_curr,place_entity。

当中update_curr在那边能够忽视。它是立异进度的部分随时间变化的音讯。大家松开前面再看,
place_entity是立异新进度的vruntime值。以便把她插入红黑树。
新历程的vruntime明确将来有三个测度,知足上边多少个标准时,沟通父子进度的vruntime:
1.sysctl安装了子进程优先实施
2.fork出的子进程与父进度在同三个cpu上
3.父进度不为空(那一个法则为啥会发生暂不鲜明,难道是fork第几个经过的时候?)
4.父进度的vruntime小于子进程的vruntime
多少个原则都还比較好明白,说下第四个,由于CFS总是采纳vruntime最小的长河试行,
故此必得保险子进度vruntime比父进度小,小编未有平昔把子进度的vruntime设置为很小的值,
而是採用交流的不二秘诀,能够免卫通过fork新过程来一大波占领cpu时间,立时还要讲到。

最后,调用enqueue_task_fair将新进度插入CFS红黑树中

以下大家看下place_entity是怎么总计新进度的vruntime的。

[cpp] view
plaincopy

  1. static void  
  2. place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int initial)  
  3. {  
  4.     u64 vruntime = cfs_rq->min_vruntime;  
  5.     /* 
  6.      * The ‘current’ period is already promised to the current tasks, 
  7.      * however the extra weight of the new task will slow them down a 
  8.      * little, place the new task so that it fits in the slot that 
  9.      * stays open at the end. 
  10.      */  
  11.     if (initial && sched_feat(START_DEBIT))  
  12.         vruntime += sched_vslice(cfs_rq, se);  
  13.     if (!initial) {  
  14.         //先不看这里,  
  15.     }  
  16.     se->vruntime = vruntime;  
  17. }  

 
这边是计量进程的启幕vruntime。

它以cfs队列的min_vruntime为条件,再增添进程在叁遍调整周期中所增加的vruntime。
此处并不是总括进程应该实行的岁月。而是先把进程的已经实行时间设为一个十分的大的值。
唯独该过程显明还尚无施行过啊,为何要这么做啊?
假设新进度都能获得最小的vruntime(min_vruntime),那么新进度会率先个被调治实施。
与此相类似程序员就会经过持续的fork新历程来让协和的次序一直占有CPU。那明显是不客观的,
这跟曾经选用时间片的水源中父亲和儿子进度要平均父进度的时间片是两个道理。

再解释下min_vruntime,那是每二个cfs队列一个的变量,它经常小于等于一体就绪态进度
的小不点儿vruntime。也可能有例外。比方对睡眠进程张开时间补偿会促成vruntime小于min_vruntime。

至于sched_vslice总括细节如今不审美,大意上说便是把概述中提交的多少个公式结合起来比如以下:
sched_vslice = (调整周期 * 进度权重 / 全部进程总权重) * NICE_0_LOAD
/ 进度权重
也正是算出进程应分配的实在cpu时间,再把它转载为vruntime。
把那些vruntime加在进度上从此,就一定于以为新进程在这一轮调治中早已实施过了。

好了。到那边又能够回来wake_up_new_task(希望你还没晕,能想起回去:-)),
看看check_preempt_curr(rq, p,
0);这一个函数就径直调用了check_preempt_wakeup

[cpp] view
plaincopy

  1. /* 
  2.  * Preempt the current task with a newly woken task if needed: 
  3.  */笔者略去了有个别不太重大的代码  
  4. static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int sync)  
  5. {  
  6.     struct task_struct *curr = rq->curr;  
  7.     struct sched_entity *se = &curr->se, *pse = &p->se; //se是方今经过。pse是新历程  
  8.     /* 
  9.      * Only set the backward buddy when the current task is still on the 
  10.      * rq. This can happen when a wakeup gets interleaved with schedule on 
  11.      * the ->pre_schedule() or idle_balance() point, either of which can 
  12.      * drop the rq lock. 
  13.      * 
  14.      * Also, during early boot the idle thread is in the fair class, for 
  15.      * obvious reasons its a bad idea to schedule back to the idle thread. 
  16.      */  
  17.     if (sched_feat(LAST_BUDDY) && likely(se->on_rq && curr != rq->idle))  
  18.         set_last_buddy(se);  
  19.     set_next_buddy(pse);  
  20.     while (se) {  
  21.         if (wakeup_preempt_entity(se, pse) == 1) {  
  22.             resched_task(curr);  
  23.             break;  
  24.         }  
  25.         se = parent_entity(se);  
  26.         pse = parent_entity(pse);  
  27.     }  
  28. }  

 
率先对此last和next八个字段给予证实。
假若那多少个字段不为NULL,那么last指向近来被调节出去的经过,next指向被调节上cpu的历程。
比方A正在施行,被B抢占。那么last指向A。next指向B。

那多少个指针有何样用呢?
当CFS在调治点选择下多少个执行进程时,会先行照拂那七个经过。大家前边拜访到,这里仅仅要记住。

<
那多少个指针仅仅使用叁遍。正是在地点这么些函数退出后,再次回到客户空间时会触发schedule,
在那里选用下一个调治进程时会优先选项next,次优先选项last。选拔完后。就能清空这两个指针。
如此那般设计的原由是,在上头的函数中检測结果是力所能致抢占并不意味着已经抢占,而独自是安装了调整标识,
在结尾触发schedule时抢占进程B并不一定是终于被调节的经过(为啥?由于大家估算是或不是能抢占
的依照是抢占进度B比进行进度A的vruntime小,但红黑树中也是有比抢占进度B的vruntime更加小的进度C,
如此在调节时就能够选中vruntime最小的C,实际不是并吞进程B)。但是大家本来愿意优先调解B,
由于大家正是为着施行B才设置了调节标识,所以这里用三个next指针指向B,以便给她个后门走,
要是B实在不争气,vruntime太大。就依旧继续实施被侵占进度A比較合理,由此last指向被攻克进度。
那是一个比next小一些的后门,假若next近便的小路失利,就让被侵夺进度A也走二遍后门,
纵然被侵夺进度A也不争气。vruntime也太大,仅仅好从红黑树中挑贰个vruntime最小的了。
不管它们活动是还是不是成功,一旦选出下贰个历程,就当下清空那五个指针,不能够老开着这么些后门吧。
须求潜心的是,schedule中清空那七个指针仅仅在2.6.29及随后的木本才有。以前的木本没有那句话。

接下来调用wakeup_preempt_entity检測是不是满意抢占条件。如果满意(重返值为1)
则对当下进度设置TIF_NEED_RESCHED标记。在脱离系统调用时会触发schedule函数举办进度切换,
本条函数后边再说。

大家看看wakeup_preempt_entity(se,
pse)。到底怎么推测前者是不是能够抢占后面一个

[cpp] view
plaincopy

  1. /* 
  2.  * Should ‘se’ preempt ‘curr’. 
  3.  * 
  4.  *             |s1 
  5.  *        |s2 
  6.  *   |s3 
  7.  *         g 
  8.  *      |<—>|c 
  9.  * 
  10.  *  w(c, s1) = -1 
  11.  *  w(c, s2) =  0 
  12.  *  w(c, s3) =  1 
  13.  * 
  14.  */  
  15. static int  
  16. wakeup_preempt_entity(struct sched_entity *curr, struct sched_entity *se)  
  17. {  
  18.     s64 gran, vdiff = curr->vruntime – se->vruntime;  
  19.     if (vdiff <= 0)  
  20.         return -1;  
  21.     gran = wakeup_gran(curr);  
  22.     if (vdiff > gran)  
  23.         return 1;  
  24.     return 0;  
  25. }  

本条函数重临-1代表新进度vruntime大于当前进程,当然不可能抢占。
再次来到0表示纵然新进度vruntime比前段时间进度小。然则未有小到调整粒度,经常也不能够抢占
回来1象征新进度vruntime比当下进程小的超过常规了调解粒度,可以抢占。
调治粒度是怎么概念吗?这一个也极度好通晓,仅仅是必得对后边的定义作出一些调动,
前面说每便都简短选取vruntime最小的长河调治,事实上也不完全部都以这么。
假若进度A和B的vruntime非常周边。那么A先实行了三个tick。vruntime比B大了,
B又推行四个tick,vruntime又比A大了。又切换来A。那样就能够在AB间频繁切换。对质量影响相当的大。
由此一旦当前经过的日子没实用完,就只是有当有进程的vruntime比近期进度小超过调整粒度时。
能力进行进度切换。

函数方面凝视中丰裕图就是那几个意思,大家看下:
横坐标表示vruntime。s1 s2
s3独家表示新历程,c表示近日历程,g表示调整粒度。
s3断定能抢占c。而s1不也许抢占c。
s2固然vruntime比c小。不过在调节粒度之内,是不是能抢占要看情形,像今后这么的景观就不能够抢占。

到这里,创设进度时的调解相关代码就介绍完了。

 

 

 

三、唤醒进度
咱俩再看看唤醒进度时的CFS动作。看下函数try_to_wake_up。相当长的函数,仅仅留几行代码

[cpp] view
plaincopy

  1. /*** 
  2.  * try_to_wake_up – wake up a thread 
  3.  * @p: the to-be-woken-up thread 
  4.  * @state: the mask of task states that can be woken 
  5.  * @sync: do a synchronous wakeup? 
  6.  * 
  7.  * Put it on the run-queue if it’s not already there. The “current” 
  8.  * thread is always on the run-queue (except when the actual 
  9.  * re-schedule is in progress), and as such you’re allowed to do 
  10.  * the simpler “current->state = TASK_RUNNING” to mark yourself 
  11.  * runnable without the overhead of this. 
  12.  * 
  13.  * returns failure only if the task is already active. 
  14.  */  
  15. static int try_to_wake_up(struct task_struct *p, unsigned int state, int sync)  
  16. {  
  17.     int cpu, orig_cpu, this_cpu, success = 0;  
  18.     unsigned long flags;  
  19.     struct rq *rq;  
  20.     rq = task_rq_lock(p, &flags);  
  21.     if (p->se.on_rq)  
  22.         goto out_running;  
  23.     update_rq_clock(rq);  
  24.     activate_task(rq, p, 1);  
  25.     success = 1;  
  26. out_running:  
  27.     check_preempt_curr(rq, p, sync);  
  28.     p->state = TASK_RUNNING;  
  29. out:  
  30.     current->se.last_wakeup = current->se.sum_exec_runtime;  
  31.     task_rq_unlock(rq, &flags);  
  32.     return success;  
  33. }  

 
update_rq_clock便是立异cfs_rq的石英石英手表,保持与系统时间一同。
重点是activate_task,它将经过增添红黑树况且对vruntime做一些调节。
然后用check_preempt_curr检查是还是不是构成并吞条件。假如能够抢占则设置TIF_NEED_RESCHED标识。

由于check_preempt_curr讲过了,大家仅仅顺着以下的顺序走一次
   activate_task
–>enqueue_task
–>enqueue_task_fair
–>enqueue_entity
–>place_entity

[cpp] view
plaincopy

发表评论

电子邮件地址不会被公开。 必填项已用*标注