📄 task_switching.html
字号:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html><head> <title></title> <link rel="stylesheet" media="screen" type="text/css" href="./style.css" /> <link rel="stylesheet" media="screen" type="text/css" href="./design.css" /> <link rel="stylesheet" media="print" type="text/css" href="./print.css" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head><body><a href=start.html>start</a></br><h2><a name="task_switching" id="task_switching">Task Switching</a></h2><div class="level2"><p> The code responsible for stopping one task and resuming another is containd in the file <strong>linux-2.6.x/kernel/sched.</strong></p><p>This code is quite concerned with the job of switching memory maps between the old task and the new.</p><p>NOTE the <strong>unlikely</strong> macros alter the way the compiler deals with the if statement optimization. If a statement is unlikeky the fast path through the code will be to bypass the if statement. This helps greatly to prevent instruction pipeline stalls when a code branch causes a change in the code flow at assembley level.</p><p> The actual task switch is done in the <strong>switch_to</strong> function or macro. This is found in <strong>linux-2.6.x/include/asm/system.h</strong> </p><pre class="code c"><span class="kw4">static</span> <span class="kw2">inline</span>task_t * context_switch<span class="br0">(</span>runqueue_t *rq, task_t *prev, task_t *next<span class="br0">)</span><span class="br0">{</span> <span class="kw4">struct</span> mm_struct *mm = next->mm; <span class="kw4">struct</span> mm_struct *oldmm = prev->active_mm; <span class="kw1">if</span> <span class="br0">(</span>unlikely<span class="br0">(</span>!mm<span class="br0">)</span><span class="br0">)</span> <span class="br0">{</span> next->active_mm = oldmm; atomic_inc<span class="br0">(</span>&oldmm->mm_count<span class="br0">)</span>; enter_lazy_tlb<span class="br0">(</span>oldmm, next<span class="br0">)</span>; <span class="br0">}</span> <span class="kw1">else</span> switch_mm<span class="br0">(</span>oldmm, mm, next<span class="br0">)</span>; <span class="kw1">if</span> <span class="br0">(</span>unlikely<span class="br0">(</span>!prev->mm<span class="br0">)</span><span class="br0">)</span> <span class="br0">{</span> prev->active_mm = <span class="kw2">NULL</span>; WARN_ON<span class="br0">(</span>rq->prev_mm<span class="br0">)</span>; rq->prev_mm = oldmm; <span class="br0">}</span> <span class="coMULTI">/* Here we just switch the register state and the stack. */</span> switch_to<span class="br0">(</span>prev, next, prev<span class="br0">)</span>; <span class="kw1">return</span> prev;<span class="br0">}</span> </pre><p>And here is the <strong>switch_to</strong> macro </p><pre class="code c"> asmlinkage <span class="kw4">void</span> resume<span class="br0">(</span><span class="kw4">void</span><span class="br0">)</span>;<span class="co2">#define switch_to(prev,next,last) { \</span> <span class="kw4">void</span> *_last; \ __asm__ __volatile__<span class="br0">(</span> \ <span class="st0">"r0 = %1;<span class="es0">\n</span><span class="es0">\t</span>"</span> \ <span class="st0">"r1 = %2;<span class="es0">\n</span><span class="es0">\t</span>"</span> \ <span class="st0">"call resume;<span class="es0">\n</span><span class="es0">\t</span>"</span> \ <span class="st0">"%0 = r0;<span class="es0">\n</span><span class="es0">\t</span>"</span> \ : <span class="st0">"=d"</span> <span class="br0">(</span>_last<span class="br0">)</span> \ : <span class="st0">"d"</span> <span class="br0">(</span>prev<span class="br0">)</span>, \ <span class="st0">"d"</span> <span class="br0">(</span>next<span class="br0">)</span> \ : <span class="st0">"CC"</span>, <span class="st0">"R0"</span>, <span class="st0">"R1"</span>, <span class="st0">"P0"</span>, <span class="st0">"P1"</span><span class="br0">)</span>; \ <span class="br0">(</span>last<span class="br0">)</span> = _last; \<span class="br0">}</span> </pre><p> All the work is done in the <strong>resume</strong> code from <strong>linux-2.6.x/arch/blackfin/mach-common/entry.S</strong></p><p>This code saves the current context and then recovers the context of the new task. After that time a <strong>rts</strong> will return the code to the new task since the program counter and the stack pointer have all been set up to point to the ones saved when the new task was <strong>switched from</strong>.</p><p>This code does do a secondary jump to dummy new task to allow the stack and frame pointers to be recovered.</p><pre class="code c"> ENTRY<span class="br0">(</span>resume<span class="br0">)</span> <span class="coMULTI">/* * Beware - when entering resume, prev (the current task) is * in r0, next (the new task) is in r1. */</span> p0 = r0; p1 = r1; <span class="br0">[</span>--sp<span class="br0">]</span> = rets; <span class="br0">[</span>--sp<span class="br0">]</span> = fp; <span class="coMULTI">/* save usp */</span> p2 = usp; <span class="br0">[</span>p0+<span class="br0">(</span>TASK_THREAD+THREAD_USP<span class="br0">)</span><span class="br0">]</span> = p2; <span class="coMULTI">/* save current kernel stack pointer */</span> <span class="br0">[</span>p0+<span class="br0">(</span>TASK_THREAD+THREAD_KSP<span class="br0">)</span><span class="br0">]</span> = sp; <span class="coMULTI">/* save program counter */</span> r1.<span class="me1">l</span> = new_old_task; r1.<span class="me1">h</span> = new_old_task; <span class="br0">[</span>p0+<span class="br0">(</span>TASK_THREAD+THREAD_PC<span class="br0">)</span><span class="br0">]</span> = r1; <span class="coMULTI">/* restore the kernel stack pointer */</span> sp = <span class="br0">[</span>p1+<span class="br0">(</span>TASK_THREAD+THREAD_KSP<span class="br0">)</span><span class="br0">]</span>; <span class="coMULTI">/* restore user stack pointer */</span> p0 = <span class="br0">[</span>p1+<span class="br0">(</span>TASK_THREAD+THREAD_USP<span class="br0">)</span><span class="br0">]</span>; usp = p0; <span class="coMULTI">/* restore pc */</span> p0 = <span class="br0">[</span>p1+<span class="br0">(</span>TASK_THREAD+THREAD_PC<span class="br0">)</span><span class="br0">]</span>; jump <span class="br0">(</span>p0<span class="br0">)</span>; <span class="coMULTI">/* * Following code actually lands up in a new (old) task. */</span> new_old_task: fp = <span class="br0">[</span>sp++<span class="br0">]</span>; rets = <span class="br0">[</span>sp++<span class="br0">]</span>; <span class="coMULTI">/* * When we come out of resume, r0 carries "old" task, becuase we are * in "new" task. */</span> rts;</pre></div><!-- SECTION [1-3989] --><h3><a name="what_about_memory_switching" id="what_about_memory_switching">What about Memory Switching</a></h3><div class="level3"><p> The <strong>switch_mm</strong> code does the work of switching between the old task and the new task’s memory structures We’ll find that code in this file <strong>linux-2.6.x/include/asm/mmu_context.h</strong></p><pre class="code c"><span class="kw4">static</span> <span class="kw2">inline</span> <span class="kw4">void</span> switch_mm<span class="br0">(</span><span class="kw4">struct</span> mm_struct *prev, <span class="kw4">struct</span> mm_struct *next, <span class="kw4">struct</span> task_<span class="kw4">struct</span> *tsk<span class="br0">)</span><span class="br0">{</span><span class="br0">}</span></pre><p>No surprise, the blackfin has no memory management unit and hence no code required to set the system up to accomodate the new task’s memory area.</p><p>Expect to see a lot more code in this function on other architectures. </p></div><!-- SECTION [3990-] --></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -