schedule函数主要流程.docx
《schedule函数主要流程.docx》由会员分享,可在线阅读,更多相关《schedule函数主要流程.docx(10页珍藏版)》请在冰豆网上搜索。
schedule函数主要流程
上层调度,linux调度的核心函数为schedule,schedule函数封装了内核调度的框架。
细节实现上调用具体的调度类中的函数实现。
schedule函数主要流程为:
1,将当前进程从相应的运行队列中删除;
2,计算和更新调度实体和进程的相关调度信息;
3,将当前进程重新插入到调度运行队列中,对于CFS调度,根据具体的运行时间进行插入而对于实时调度插入到对应优先级队列的队尾;
4,从运行队列中选择运行的下一个进程;
5,进程调度信息和上下文切换;
当进程上下文切换后,调度就基本上完成了,当前运行的进程就是切换过来的进程了。
[cpp]viewplaincopyprint?
1. /*内核和其他部分用于调用进程调度器的入口,选择
2. 哪个进程可以运行,何时将其投入运行。
schedule通常
3. 都需要和一个具体的调度类相关联,也就是说,他
4. 会找到一个最高优先级的调度类,后者需要有自己的
5. 可运行队列,然后问后者谁才是下一个该运行的进程
6. 该函数唯一重要的事情是,他回调用pick_next_task*/
7. asmlinkagevoid__schedschedule(void)
8. {
9. structtask_struct*prev,*next;
10. unsignedlong*switch_count;
11. structrq*rq;
12. intcpu;
13.
14. need_resched:
15. preempt_disable();
16. cpu=smp_processor_id();
17. rq=cpu_rq(cpu);/*得到特定cpu的rq*/
18. rcu_sched_qs(cpu);
19. prev=rq->curr;/*当前的运行进程*/
20. switch_count=&prev->nivcsw;/*进程切换计数*/
21.
22. release_kernel_lock(prev);
23. need_resched_nonpreemptible:
24.
25. schedule_debug(prev);
26.
27. if(sched_feat(HRTICK))
28. hrtick_clear(rq);
29.
30. spin_lock_irq(&rq->lock);
31. update_rq_clock(rq);/*更新rq的clock属性*/
32. clear_tsk_need_resched(prev);/*清楚prev进程的调度位*/
33.
34. if(prev->state&&!
(preempt_count()&PREEMPT_ACTIVE)){
35. if(unlikely(signal_pending_state(prev->state,prev)))
36. prev->state=TASK_RUNNING;
37. else/*从运行队列中删除prev进程,根据调度类的
38. 不同,实现不同*/
39. deactivate_task(rq,prev,1);
40. switch_count=&prev->nvcsw;
41. }
42. /*现只对实时进程有用*/
43. pre_schedule(rq,prev);
44.
45. if(unlikely(!
rq->nr_running))
46. idle_balance(cpu,rq);
47. /*将当前进程,也就是被切换出去的进程重新
48. 插入到各自的运行队列中,对于CFS算法插入
49. 到合适的位置上,对于实时调度插入到同一个
50. 优先级队列的链表尾部*/
51. put_prev_task(rq,prev);
52. /*从各自的运行队列中选择下一个进程来运行*/
53. next=pick_next_task(rq);
54.
55. if(likely(prev!
=next)){
56. /*更新切换出去和进来进程以及对应rq的相关变量*/
57. sched_info_switch(prev,next);
58. perf_event_task_sched_out(prev,next,cpu);
59.
60. rq->nr_switches++;/*切换记录*/
61. rq->curr=next;
62. ++*switch_count;
63. /*上下文切换,在进程切换已经介绍*/
64. context_switch(rq,prev,next);/*unlockstherq*/
65. /*
66. *thecontextswitchmighthaveflippedthestackfromunder
67. *us,hencerefreshthelocalvariables.
68. */
69. cpu=smp_processor_id();
70. rq=cpu_rq(cpu);
71. }else
72. spin_unlock_irq(&rq->lock);
73. /*对于实时进程有用到*/
74. post_schedule(rq);
75.
76. if(unlikely(reacquire_kernel_lock(current)<0))
77. gotoneed_resched_nonpreemptible;
78.
79. preempt_enable_no_resched();
80. if(need_resched())
81. gotoneed_resched;
82. }
对于cpu_rq函数
[cpp]viewplaincopyprint?
1. /*通过向上加偏移的方式得到rq,这里可以看出
2. runqueues为一个rq结构的数组,cpu为数组下标*/
3. #definecpu_rq(cpu) (&per_cpu(runqueues,(cpu)))
deactivate_task函数实现
[cpp]viewplaincopyprint?
1. /*
2. *deactivate_task-removeataskfromtherunqueue.
3. */
4. staticvoiddeactivate_task(structrq*rq,structtask_struct*p,intsleep)
5. {
6. if(task_contributes_to_load(p))
7. rq->nr_uninterruptible++;
8. /*具体操作*/
9. dequeue_task(rq,p,sleep);
10. dec_nr_running(rq);/*rq中当前进程的运行数减一*/
11. }
我们看具体的操作
[cpp]viewplaincopyprint?
1. staticvoiddequeue_task(structrq*rq,structtask_struct*p,intsleep)
2. {
3. if(sleep){/*如果sleep不为0,更新se中相关变量*/
4. if(p->se.last_wakeup){
5. update_avg(&p->se.avg_overlap,
6. p->se.sum_exec_runtime-p->se.last_wakeup);
7. p->se.last_wakeup=0;
8. }else{
9. update_avg(&p->se.avg_wakeup,
10. sysctl_sched_wakeup_granularity);
11. }
12. }
13. /*更新进程的sched_info数据结构中相关属性*/
14. sched_info_dequeued(p);
15. /*调用具体调度类的函数从他的运行队列中删除*/
16. p->sched_class->dequeue_task(rq,p,sleep);
17. p->se.on_rq=0;
18. }
可见,调用了具体运行队列的删除函数,我们看最关键的选择下一个进程的方式。
[cpp]viewplaincopyprint?
1. /*
2. *Pickupthehighest-priotask:
3. */
4. /*以优先级为序,从高到低,一次检查每个调度类
5. 并且从高优先级的调度类中,选择最高优先级的进程
6. */
7. staticinlinestructtask_struct*
8. pick_next_task(structrq*rq)
9. {
10. conststructsched_class*class;
11. structtask_struct*p;
12.
13. /*
14. *Optimization:
weknowthatifalltasksarein
15. *thefairclasswecancallthatfunctiondirectly:
16. */
17. if(likely(rq->nr_running==rq->cfs.nr_running)){
18. p=fair_sched_class.pick_next_task(rq);
19. if(likely(p))
20. returnp;
21. }
22.
23. class=sched_class_highest;
24. for(;;){/*对每一个调度类*/
25. p=class->pick_next_task(rq);/*调用该调度类中的函数,找出下一个task*/
26. if(p)
27. returnp;
28. /*
29. *WillneverbeNULLastheidleclassalways
30. *returnsanon-NULLp:
31. */
32. /*访问下一个调度类*/
33. class=class->next;
34. }
35. }
可见,对于调度类的选择,同样以优先级进行。
对于进程调度信息的切换最终会调用__sched_info_switch
[cpp]viewplaincopyprint?
1. /*
2. *Calledwhentasksareswitchedinvoluntarilydue,typically,toexpiring
3. *theirtimeslice. (Thismayalsobecalledwhenswitchingtoorfrom
4. *theidletask.) Weareonlycalledwhenprev!
=next.
5. */
6. staticinlinevoid
7. __sched_info_switch(structtask_struct*prev,structtask_struct*next)
8. {
9. structrq*rq=task_rq(prev);
10.
11. /*
12. *prevnowdepartsthecpu. It'snotinterestingtorecord
13. *statsabouthowefficientwewereatschedulingtheidle
14. *process,however.
15. */
16. if(prev!
=rq->idle)/*如果被切换出去的进程不是idle进程*/
17. sched_info_depart(prev);/*更新prev进程和他对应rq的相关变量*/
18.
19. if(next!
=rq->idle)/*如果切换进来的进程不是idle进程*/
20. sched_info_arrive(next);/*更新next进程和对应队列的相关变量*/
21. }
[cpp]viewplaincopyprint?
1. /*
2. *Calledwhenaprocessceasesbeingtheactive-runningprocess,either
3. *voluntarilyorinvoluntarily. Nowwecancalculatehowlongweran.
4. *Also,iftheprocessisstillintheTASK_RUNNINGstate,call
5. *sched_info_queued()tomarkthatithasnowagainstartedwaitingon
6. *therunqueue.
7. */
8. staticinlinevoidsched_info_depart(structtask_struct*t)
9. {
10. /*计算在进程在rq中运行的时间长度*/
11. unsignedlonglongdelta=task_rq(t)->clock-
12. t->sched_info.last_arrival;
13. /*更新RunQueue中的Task所得到CPU执行
14. 时间的累加值。
*/
15. rq_sched_info_depart(task_rq(t),delta);
16.
17. /*如果被切换出去进程的状态是运行状态
18. 那么将进程sched_info.last_queued设置为rq的clock
19. last_queued为最后一次排队等待运行的时间*/
20. if(t->stat