进程调度之优先级详解...

This commit is contained in:
gatieme
2016-06-25 23:52:33 +08:00
parent 9050e43407
commit 667d1565f1
@@ -420,10 +420,34 @@ if (!prev->mm)
执行环境的切换是在switch_to()中完成的, switch_to完成最终的进程切换,它保存原进程的所有寄存器信息,恢复新进程的所有寄存器信息,并执行新的进程
调度过程可能选择了一个新的进程, 而清理工作则是针对此前的活动进程, 请注意, 这不是发起上下文切换的那个进程, 而是系统中随机的某个其他进程, 内核必须想办法使得进程能够与context_switch例程通信, 这就可以通过switch_to宏实现. 因此switch_to函数通过3个参数提供2个变量,
**为什么switch_to需要3个参数**
调度过程可能选择了一个新的进程, 而清理工作则是针对此前的活动进程, 请注意, 这不是发起上下文切换的那个进程, 而是系统中随机的某个其他进程, 内核必须想办法使得进程能够与context_switch例程通信, 这就可以通过switch_to宏实现. 因此switch_to函数通过3个参数提供2个变量.
在新进程被选中时, 底层的进程切换冽程必须将此前执行的进程提供给context_switch, 由于控制流会回到陔函数的中间, 这无法用普通的函数返回值来做到, 因此提供了3个参数的宏
我们考虑这个样一个例子, 假定多个进程A, B, C...在系统上运行, 在某个时间点, 内核决定从进程A切换到进程B, 此时prev = A, next = B, 即执行了switch_to(A, B), 而后当被抢占的进程A再次被选择执行的时候, 系统可能进行了多次进程切换/抢占(至少会经历一次即再次从B到A),假设A再次被选择执行时时当前活动进程是C, 即此时prev = C. next = A.
在每个switch_to被调用的时候, prev和next指针位于各个进程的内核栈中, prev指向了当前运行的进程, 而next指向了将要运行的下一个进程, 那么为了执行从prev到next的切换, switcth_to使用前两个参数prev和next就够了.
在进程A被选中再次执行的时候, 会出现一个问题, 此时控制权即将回到A, switch_to函数返回, 内核开始执行switch_to之后的点, 此时内核栈准确的恢复到切换之前的状态, 即进程A上次被切换出去时的状态, prev = A, next = B. 此时, 内核无法知道实际上在进程A之前运行的是进程C.
因此, 在新进程被选中执行时, 内核恢复到进程被切换出去的点继续执行, 此时内核只知道谁之前将新进程抢占了, 但是却不知道新进程再次执行是抢占了谁, 因此底层的进程切换机制必须将此前执行的进程(即新进程抢占的那个进程)提供给context_switch. 由于控制流会回到函数的该中间, 因此无法通过普通函数的返回值来完成. 因此使用了一个3个参数, 但是逻辑效果是相同的, 仿佛是switch_to是带有两个参数的函数, 而且返回了一个指向此前运行的进程的指针.
>switch_to(prev, next, last);
>
>即
>
>prev = last = switch_to(prev, next);
其中返回的prev值并不是做参数的prev值, 而是prev被再次调度的时候抢占掉的那个进程last.
在上个例子中, 进程A提供给switch_to的参数是prev = A, next = B, 然后控制权从A交给了B, 但是恢复执行的时候是通过prev = C, next = A完成了再次调度, 而后内核恢复了进程A被切换之前的内核栈信息, 即prev = A, next = B. 内核为了通知调度机制A抢占了C的处理器, 就通过last参数传递回来, prev = last = C.
内核实现该行为特性的方式依赖于底层的体系结构, 但内核显然可以通过考虑两个进程的内核栈来重建所需要的信息
```c
/*
* Saving eflags is important. It switches not only IOPL between tasks,
@@ -449,6 +473,8 @@ if (!prev->mm)
##3.7 finish_task_switch完成清理工作
-------
witch_to完成了进程的切换, 新进程在该调用后开始执行, 而switch_to之后的代码只有在当前进程下一次被选择运行时才会执行.