linux调度器(三)——周期性调度器与CFS

本文深入剖析了CFS调度器的工作原理,重点介绍了周期性调度器如何通过更新进程的执行时间和虚拟时间,并根据进程的优先级调整其在调度队列中的位置,最终决定何时将进程标记为可调度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注:这里并没有详细分析到每个内部函数,如果要了解这些细节的话,可以看后面的 OTHER CFS CLASS API CFS主要的内部函数。
    
周期性调度器在调度框架上由 scheduler_tick 完成:在每一个cpu的时钟周期都触发一次该函数调用,它更新运行队列的时钟及load,然后调用当前进程的调度器类的周期调度函数。
[cpp]  view plain copy
  1. update_rq_clock(rq); /* 更新运行队列的时钟rq->clock*/  
  2. /* 更新运行队列load,本质是将数组中先前存储的负荷值向后移动一个位置,将当前负荷记入数组的第一个位置 */  
  3. update_cpu_load_active(rq);     
  4. curr->sched_class->task_tick(rq, curr, 0);  
我们再来看一下CFS调度器类的周期调度函数( task_tick_fair ),该函数是对 entity_tick 的组调度的一个封装(因为只有真正的task的se才会在cpu上运行,而group的se是不会在cpu上运行的,所以这里参数的se就是一个task,然后对从它到它所在的组的根的所有group se调用entity_tick),所以我们直接来看一下 entity_tick 函数:
[cpp]  view plain copy
  1. update_curr(cfs_rq); 
  2. //完成当前se(这个se可以是task或group)的执行时间,虚拟时间,cfs_rq的相应时间及统计的更新:
  3. curr->exec_start=now;curr->sum_exec_runtime+=delta_exec;及curr->vruntime+= calc_delta_fair();
  4. 更新cfs_rq的min_vruntime.其中delta_exec表示实质执行的时间,而calc_delta_fair则计算delta_exec
  5. 相对应的虚拟时间,即delta_fair={if se->load.weight != NICE_0_LOAD return delta_exec; 
  6. else return delta_exec *NICE_0_LOAD/se.load->weight},可以看到当该se是0优先级的话,
  7. 那么它的虚拟时间等于实质执行的物理时间,否则如果该se的load越大那么它的虚拟时间就越小,
  8. 这也是为什么load越大的进程能够执行的时间越多——它的虚拟时间增长的越慢  
  9.   
  10.     update_entity_shares_tick(cfs_rq); //这个函数只有对于SMP才有作用,所以我们这里先不分析  
  11.   
  12. #ifdef CONFIG_SCHED_HRTICK  //HRTICK情况,不知道干嘛   
  13.     if (queued) {  
  14.         resched_task(rq_of(cfs_rq)->curr);  
  15.         return;  
  16.     }  
  17.     if (!sched_feat(DOUBLE_TICK) &&  
  18.             hrtimer_active(&rq_of(cfs_rq)->hrtick_timer))  
  19.         return;  
  20. #endif  
  21.   
  22.     //可运行的大于1,或者不支持WAKEUP_PREEMPT,后面这个条件在后来的版本已经被去掉了  
  23.     if (cfs_rq->nr_running > 1 || !sched_feat(WAKEUP_PREEMPT)) 
  24.         check_preempt_tick(cfs_rq, curr); 
  25. //该函数用于检查当前进程是否运行了足够长的时间(实质运行时间大于sched_slice理想运行时间或者当前运行
  26. 队列最左边的se到现在的等待的时间已经超过curr理想应该运行的时间),如果出现了上面的情况则调用
  27. resched_task将该进程设置为TIF_NEED_RESCHED,即可调度的;如果运行时间小于sysctl_sched_min_granularity
  28. (最小执行时间)或者内核不允许WAKEUP_PREEMPT(内核允许的调度特性在/proc/sys/kernel/sched_features
  29. 定义WAKEUP_PREEMPT使用第3 bit),则直接返回。注意这里的每个判断条件的顺序是非常重要的(前面的更重要):
  30. 1.如果进程已经运行超过它的理想运行时间那么它将无条件被resched_task;
  31. 2.如果不支持WAKEUP_PREEMPT那么直接返回不对当前的进行resched_task;
  32. 3.如果运行的时间小于sysctl_sched_min_granularity那么也直接返回;
  33. 4.最左边的等待时间大于理想运行时间时resched_task(curr),即要4满足的话,必须当前的执行时间小于它理想运行时间,
  34. 并且支持WAKEUP_PREEMPT及运行了至少sysctl_sched_min_granularity,最后已经等待的时间大于理想运行时间  

可以看到每个时钟都对会当前运行的se进行实质执行时间及虚拟执行时间进行更新,最后检查该进程是否运行的足够长的时间,如果是的话则将它置为TIF_NEED_RESCHED供主调度在适当的时机进行切换。可见周期性调度器还是比较简单,没有涉及到真正的调度任务,只是设置一个重调度请求的标志而已,下面看看主调器,也就是它来响应周期性调度器的 TIF_NEED_RESCHED

图 CFS与周期调度器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值