详解Linux内核进程调度函数schedule()的触发和执行时机
<p>内核的调度操作分为触发和执行两个部分,触发时仅仅设置一下当前进程的TIF_NEED_RESCHED标志,执行的时候则是通过schedule()函数来完成进程的选择和切换。当前进程的thread_info->flags中TIF_NEED_RESCHED位表示需要调用schedule()函数进行调度。内核在两种情况下会设置该标志,一个是在时钟中断进行周期性的检查时,另一个是在被唤醒进程的优先级比正在运行的进程的优先级高时。</p>
<p>
<strong>周期性地更新当前任务的状态时:</strong></p>
<p>
定时中断处理函数中会调用schedule_tick()用于处理关于调度的周期性检查和处理,其调用路径是和时钟处理有关的tick_periodic()->update_process_times()->scheduler_tick()或者tick_sched_handle()->update_process_times()->scheduler_tick(),主要用于更新就绪队列的时钟、CPU负载和当前任务的运行时间统计等,如下所示:</p>
<div class="jb51code">
<div>
<div class="syntaxhighlightercpp" id="highlighter_979984">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
<div class="line number8 index7 alt1">
8</div>
<div class="line number9 index8 alt2">
9</div>
<div class="line number10 index9 alt1">
10</div>
<div class="line number11 index10 alt2">
11</div>
<div class="line number12 index11 alt1">
12</div>
<div class="line number13 index12 alt2">
13</div>
<div class="line number14 index13 alt1">
14</div>
<div class="line number15 index14 alt2">
15</div>
<div class="line number16 index15 alt1">
16</div>
<div class="line number17 index16 alt2">
17</div>
<div class="line number18 index17 alt1">
18</div>
<div class="line number19 index18 alt2">
19</div>
<div class="line number20 index19 alt1">
20</div>
<div class="line number21 index20 alt2">
21</div>
<div class="line number22 index21 alt1">
22</div>
<div class="line number23 index22 alt2">
23</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="cpp comments">//linux-3.13/kernel/sched/core.c</code>
</div>
<div class="line number2 index1 alt1">
<code class="cpp keyword bold">void</code> <code class="cpp plain">scheduler_tick(</code><code class="cpp keyword bold">void</code><code class="cpp plain">)</code>
</div>
<div class="line number3 index2 alt2">
<code class="cpp plain">{</code>
</div>
<div class="line number4 index3 alt1">
<code class="cpp spaces"> </code><code class="cpp color1 bold">int</code> <code class="cpp plain">cpu = smp_processor_id(); </code><code class="cpp comments">//获取当前cpu编号</code>
</div>
<div class="line number5 index4 alt2">
<code class="cpp spaces"> </code><code class="cpp keyword bold">struct</code> <code class="cpp plain">rq *rq = cpu_rq(cpu); </code><code class="cpp comments">//取得对应cpu的rq(就绪队列)</code>
</div>
<div class="line number6 index5 alt1">
<code class="cpp spaces"> </code><code class="cpp keyword bold">struct</code> <code class="cpp plain">task_struct *curr = rq->curr; </code><code class="cpp comments">//获取当前运行的任务</code>
</div>
<div class="line number7 index6 alt2">
</div>
<div class="line number8 index7 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">sched_clock_tick();</code>
</div>
<div class="line number9 index8 alt2">
</div>
<div class="line number10 index9 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">raw_spin_lock(&rq->lock);</code>
</div>
<div class="line number11 index10 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">update_rq_clock(rq); </code><code class="cpp comments">//更新队列时钟</code>
</div>
<div class="line number12 index11 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">curr->sched_class->task_tick(rq, curr, 0); </code><code class="cpp comments">//调用当前任务的调度类对应的函数</code>
</div>
<div class="line number13 index12 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">update_cpu_load_active(rq); </code><code class="cpp comments">//更新本处理器的负载</code>
</div>
<div class="line number14 index13 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">raw_spin_unlock(&rq->lock);</code>
</div>
<div class="line number15 index14 alt2">
</div>
<div class="line number16 index15 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">perf_event_task_tick();</code>
</div>
<div class="line number17 index16 alt2">
</div>
<div class="line number18 index17 alt1">
<code class="cpp preprocessor">#ifdef CONFIG_SMP</code>
</div>
<div class="line number19 index18 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">rq->idle_balance = idle_cpu(cpu);</code>
</div>
<div class="line number20 index19 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">trigger_load_balance(rq, cpu); </code><code class="cpp comments">//必要时进行负载均衡</code>
</div>
<div class="line number21 index20 alt2">
<code class="cpp preprocessor">#endif</code>
</div>
<div class="line number22 index21 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">rq_last_tick_reset(rq);</code>
</div>
<div class="line number23 index22 alt2">
<code class="cpp plain">}</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
其中curr->sched_class->task_tick(rq, curr, 0);这行代码调用了当前任务的调度类的task_tick()函数,这个函数根据具体情况决定是否需要对当前任务设置TIF_NEED_RESCHED标志,如果需要则最终调用set_tsk_need_resched()设置该标志。需要注意的是,此处仅仅是设置标志而没有执行schedule()函数,在各种系统调用、中断的返回代码最后,才会根据这个标志来决定是否执行schedule()函数。</p>
<p>
<strong>睡眠的任务被唤醒时:</strong></p>
<p>
当睡眠任务所等待的事件到达时,内核(例如驱动程序的中断处理函数)将会调用wake_up()唤醒相关的任务,并最终调用try_to_wake_up()。它完成三件事:将任务重新添加到就绪队列,将运行标志设置为TASK_RUNNING,如果被唤醒的任务可以抢占当前运行任务则设置当前任务的TIF_NEED_RESCHED标志。</p>
<p>
设置了TIF_NEED_RESCHED标志之后,真正调用执行schedule()函数的时机只有两种,第一种是系统调用或者中断返回时,根据TIF_NEED_RESCHED标志决定是否调用schedule()函数(从效率方面考虑,趁着还在内核态把该处理的事情处理完毕);第二种情况是当前任务因为原因需要睡眠,进程睡眠后立即调用schedule()函数,在内核中这种情况也比较多,比如磁盘、网卡等设备驱动程序中。</p>
<p>
<strong>参考文献:《Linux技术内幕》 </strong></p>
<p>
PS:刚开始学习Linux内核的时候很容易被各种结构体各种概念充斥脑海,一团乱麻。这时候需要把它们各自负责的功能以及之间相互的配合理清楚,推荐这本书。看完《Linux内核设计与实现》后可以相互比照,效果不错。</p>
<p>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。</p>
<p>
原文链接:http://blog.csdn.net/Q_AN1314/article/details/79133046</p>
頁:
[1]