4.5.5 中断处理程序结束后的线程调度
在V1.0的实现中,在中断上下文中对线程的调度,是发生在时钟中断结束的时候,而在V1.5的实现中,对线程的调度,则发生在任何中断处理程序结束后。而Hello China的中断处理过程是这样的:
① CPU特定的汇编语言处理部分,主要是保存线程硬件上下文等。
② 汇编语言完成硬件上下文的保存后,调用GeneralIntHandler函数,该函数是C语言实现的,从该函数往后,所有的处理动作,都是由C语言完成(不考虑驱动程序内部采用的汇编语言处理)。
③ GeneralIntHandler再调用system对象提供的DispatchInterrupt函数。
④ DispatchInterrupt函数再调用系统中注册的中断处理程序。
在V1.0的实现中,对线程的调度,是发生在时钟中断处理程序之后,返回DispatchInterrupt函数之前的,因此只会在时钟中断中,对系统中的线程进行调度。在V1.5的实现中,把对线程的调度,放在了DispatchInterrupt函数中,该函数完成特定中断处理程序的调用后,将调用ScheduleFromInt函数,重新对线程进行调度。V1.5的实现如下:
static VOID DispatchInterrupt(__COMMON_OBJECT* lpThis, LPVOID lpEsp, UCHAR ucVector) { __INTERRUPT_OBJECT* lpIntObject=NULL; __SYSTEM* lpSystem =NULL; if((NULL==lpThis) || (NULL==lpEsp)) return; lpSystem=(__SYSTEM*)lpThis; lpIntObject=lpSystem->lpInterruptVector[ucVector]; //POSITION 1 lpSystem->ucIntNestLevel+=1; //Increment nesting level. if(NULL==lpIntObject) //The current interrupt vector has not handler object. { DefaultIntHandler(lpEsp,ucVector); return; } while(lpIntObject) //Travel the whole interrupt list of this vector. { if(lpIntObject->InterruptHandler(lpEsp, lpIntObject->lpHandlerParam)) //If an interrupt object handles the interrupt,then returns. { break; } lpIntObject=lpIntObject->lpNextInterruptObject; } //POSITION 2 lpSystem->ucIntNestLevel-=1; if(0==lpSystem->ucIntNestLevel) //The most outside interrupt. { KernelThreadManager.ScheduleFromInt( (__COMMON_OBJECT*)&KernelThreadManager, lpEsp); //Re-schedule kernel thread. } else { BUG(); } return; }
其中黑体标注部分,是与Hello China V1.0的实现不同的部分。在中断处理程序结束后,会调用ScheduleFromInt函数,完成一次线程调度。需要注意的是,在V1.5的实现中,对System对象也做了少量的修改,引入了一个ucIntNestLevel的成员变量,用于表示中断的嵌套级别。每当进入中断调度程序的时候,该变量会增加1(代码中POSITION1位置处),每当完成一个中断处理程序的调用(退出中断处理程序)的时候,该变量减1 (对应代码中POSITION 2位置处)。若该变量值大于1,说明发生了嵌套中断(即当前中断处理过程中,又被更高优先级的中断打断)。对于核心线程的调度,只有在最外层中断处理程序结束后,才会引发。这样符合实际情况,因为如果在不是最外层的中断处理程序结束后调度线程,可能会导致外层中断无法得到处理,严重情况下会引起系统崩溃。
虽然引入了ucIntNestLevel变量标识中断嵌套级别,但V1.5版本的Hello China没有针对中断嵌套做特殊的优化,因此暂时可做“不支持嵌套中断”的处理。但实际上,经过测试,在中断嵌套的情况下,Hello China V1.5的表现也是很好的。