从一个任务转变到另一个任务的实际过程叫作设备场景切换。因为设备场景是处理器专用的,实现设备场景切换的实现也是这样。那意味着它总是要用汇编来写。与其向你展示我在ADEOS 中使用的80x86 专用的汇编代码,不如我用一种类C 的伪代码来展示设备场景切换tb例程。
void
contextSwitch(PContext pOldContext, PContext pNewContext)
{
if(saveContext(pOldContext))
{
//
// Restore new context only on a nonzero exit from saveContext().
//
restoreContext(pNewContext);
// This line is never executed!
}
// Instead, the restored task continues to execute at this point.
}
例程 contextSwitch()实际上是被调度程序凋用,而调度程序又在那此终止中断的tb系统调用中被调用,因此它不一定在这里终止中断。此外,由于调用调度程序的操作系统调用是用高级语言写的,所以大部分运行任务的寄存器已经被保存到它自己当地的栈中了。这减少了例程saveContext()和restoreContext()需要做的工作。它们只需要关心指令指针,栈指针以及标志位的保存。例程 contextSwitch()的实际行为是很难仅仅通过看前面的代码来理解的。大部分的软件开发者以连续的方式思考问题,认为每一行代码会紧接着上一条代码破执行。然而,这个代码实际为并行地执行了两次。当一个任务(新任务)转变到运行状态,另一个(旧任务)必须同时返回到就绪状态。想一下新任务当它在restoreContext()代码中被恢复的时候就会明白。无论新任务以前做什么,它在saveContext 代码里总是醒着的——因为这就是它的指令存放的地方。新任务如何知道它是否是第一次(也就是,在准备休眠的过程)或者是第二次(醒来的过程)从saveContext()中出来的呢?它确实需要知道这个差别,因此我不得不用一种有点隐蔽的方法来实现saveContext()。例程saveContext()不是保存了准确的目前的指令指针。实际上是保存了一些指令前面的地址。那样,当保存的设备场景恢复的时候,程序从saveContext 中另一个下同的点继续。这也使得saveContext 可能返回不同的值:当任务要休眠的时候为非零,当任务唤起的时候为零。例程contextSwitch()利用这个返回的值来决定是否调用restoreContext()。如果不进行这个检测,那么与这个新任务相关的代码永远不会执行。
我知道这可能是一个复杂的事件序列,因此我在图8-3 中说明了整个的过程。