Nucleus分析报告
1.3.2 中断的调度
Nucleus的中断分为管理的和非管理的中断。
管理的中断需要向操作系统注册该中断向量,中断产生后通过该中断向量注册的低级中断服务程序(LISR)来激活高级的中断服务程序(HISR)。LISR主要完成硬件中断的处理,及激活HISR。HISR的调度类似于任务,具有优先级,可以使用大多数Nucleus的系统调用。 非管理的中断,则不需要通过操作系统进行管理,直接将中断服务程序挂到中断向量表上,上下文的保存与恢复都要用户自己来做,该中断自己不能嵌套,最好不要被管理的中断再次中断否则会引起堆栈出错,而且非管理的中断不能使用绝大多数的Nucleus系统调用,因为它可能会破坏操作系统某些保护的数据结构(当有线程在运行时)。非管理的中断适用于那些比较频繁的中断,如果通过操作系统来管理这些中断的话,其上下文保存与恢复的时间就比较长,中断的实时性就不能满足要求。
非管理的中断比较简单,类似于以前我们写的中断服务程序,这里就不多说。下面我们讨论的中断的调度都指的是Nuclesu管理中断的调度。
中断的调度需要用到以下比较重要的数据结构:
TCD_Registered_LISRs[256]:对应68360的256个中断向量。0表示该中断向量没有注册,是操作系统不能处理的中断(Unhandled_Interrupt);非0表示该中断向量已注册,且其值为在LISR函数指针数组TCD_LISR_Pointers中的索引下标。
TCD_LISR_Pointers[30]:LISR函数指针数组,指向当中断产生时要调用的低级中断服务程序LISR的入口函数。
TCD_Interrupt_Count:表示有多少个中断服务程序(ISRs)正在进行处理。0:没有中断;1:只有一个中断;>1:中断嵌套。
TCD_Interrupt_Level:允许中断的级别,用来给68360的状态寄存器SR赋值。0x700表示屏蔽所有中断,0x500表示屏蔽5级及5级以下的中断,0表示打开所有中断。
TCD_Unhandled_Interrupt:系统出错时,表示不能处理的中断向量号。
TCD_Created_HISRs_List:已创建的HISR链表的头指针。 TCD_Active_HISR_Heads[3]:对应HISR优先级0~2,每个数组元素是该优先级已激活HISR链表的头指针。
TCD_Active_HISR_Tails[3]:对应HISR优先级0~2,每个数组元素是该优先级已激活HISR链表的尾指针。
TCD_Execute_HISR:当前正在执行或要执行的具有最高优先级的HISR指针。
每个HISR具有一个0~2的优先级,0表示最高优先级,2表示最低优先级,相同优先级的HISR按照先入先出的顺序处理,优先级不同的HISR按照优先级的高低进行调度。HISR是不能被挂起的,因此其所有的系统调用都要加上NU_NO_SUSPEND参数。
中断的调度包括:中断向量的注册、HISR的创建与删除、上下文的保护与恢复、LISR的执行、HISR的激活以及HISR的调度等。
①、中断向量的注册(TCC_Register_LISR)
一个中断要通过操作系统管理起来,首先要将其中断向量通过系统调用NU_Register_LISR(INT vector, VOID (*new_lisr)(INT), VOID (**old_lisr)(INT))注册起来。该系统
9
Nucleus分析报告
调用的第2个参数是LISR的入口函数指针,也就是中断产生后要执行的LISR。
中断的注册首先要判断该中断向量是否已经注册过。如果该中断已经注册过,则利用TCD_Registered_LISRs索引到该中断向量在LISR函数指针数组TCD_LISR_Pointers中的下标,然后将新的LISR(new_lisr)填入TCD_LISR_Pointers。
如果该中断向量没有注册过,则在LISR函数指针数组TCD_LISR_Pointers中找出一个没有使用的单元,将new_lisr填入该单元,同时将该单元的下标填入该中断向量在TCD_Registered_LISRs中对应的单元。另外,还要判断INT_Loaded_Flag标志,如果该标志为0,则要替换掉当前的中断向量表,否则不修改当前的中断向量表。
②、HISR的创建(TCC_Create_HISR)
HISR也就是中断产生后,要在LISR中激活的高级中断服务程序。
HISR的创建比任务创建简单,不用进行设置任务状态、恢复任务等操作,只需创建一个HISR控制块HCB,初始化HCB中的一些参数,为HISR创建一个Solicited类型的堆栈帧,将该HISR的指针挂到已创建HISR链表TCD_Created_HISRs_List,同时分配一个用户指定的入口函数指针,该函数用来完成真正的中断处理。
③、HISR的删除(TCC_Delete_HISR)
HISR的删除默认HISR处于非激活状态,仅仅是将HISR从已创建HISR链表TCD_Created_HISRs_List中删除,并清除HISR ID标志,并不能释放与HISR相关的内存(控制块、堆栈等),同时也不影响HISR的激活,对HISR的调度可能会产生微小的影响(由于HISR ID被清除)。一般来说,HISR的删除没有什么意义,除非把跟HISR相关的中断也关掉。
④、上下文的保护(TCT_Interrupt_Context_Save)
通常的中断服务程序对要用到的寄存器都要进行堆栈保护,Nucleus操作系统除了做这些外,还要对当前线程进行保护,使得高级中断服务程序HISR可以抢占任务,让HISR得到快速的响应。Nucleus在系统空闲(没有线程运行)时,中断堆栈使用的是系统堆栈TCD_System_Stack;如果有线程(任务或HISR)在运行,使用的是任务或HISR堆栈,上下文保护完成之后,则将堆栈切换到系统堆栈TCD_System_Stack。
上下文保护首先将所有中断屏蔽掉,等保护完成之后再将中断打开。其次判断中断计数器TCD_Interrupt_Count,如果本次中断是中断嵌套,则将TCD_Interrupt_Count加1,然后返回。如果本次中断不是中断嵌套,则判断当前有没有线程在运行,如果当前有线程在运行,则为当前线程建立一个Interrupt类型的堆栈帧,将当前的堆栈指针保存在线程控制块中,再将堆栈指针切换到系统堆栈顶部TCD_System_Stack,然后返回;如果当前没有线程运行,则直接将堆栈指针切换到系统堆栈顶部TCD_System_Stack,然后返回。
⑤、LISR的执行(TCC_Dispatch_LISR)
中断上下文保护完成后,就要根据中断向量,在LISR指针数组TCD_LISR_Pointers中索引到本中断向量的LISR入口函数指针,然后执行LISR函数,通常LISR要做的只是处理硬件中断及激活HISR。LISR执行完毕,则将上下文恢复,将控制权交给TCT_ Shedule进行系统调度。
要注意的是,如果某个中断向量没有注册,则会产生系统错误,进入系统错误线程
10
Nucleus分析报告
ERC_System_Error处理,这种错误是致命的错误,导致整个系统进入一个死循环。
⑥、上下文的恢复(TCT_Interrupt_Context_Restore)
上下文恢复首先判断是不是中断嵌套,如果是中断嵌套,则将TCD_Interrupt_Count减1,将堆栈保护的寄存器恢复,然后利用RTE指令从中断返回。如果不是中断嵌套,则将当前线程TCD_Current_Thread清为0,将堆栈切换到系统堆栈顶部TCD_System_Stack,将控制交给TCT_Shedule进行线程的重新调度,在这里HISR会抢占任务优先运行。
对于没有嵌套的中断恢复,并没有执行RTE指令,从中断产生的指令往下执行,而是将控制交给TCT_Shedule进行重新调度,这类中断恢复可以分三种情况进行分析:
i)、系统空闲(做TCD_Shedule死循环)时,产生了中断,则上下文恢复后,再次运行TCD_ Shedule,然后调度由LISR激活的HISR(TCD_Execute_HISR)。 ii)、中断产生时,有一个任务在运行。由于在上下文保护时,已经给当前任务建立了一个中断类型的堆栈帧,同时LISR运行时没有修改当前任务的状态(没有将当前任务挂起),也没有修改TCD_Execute_Task。当中断恢复完成之后,TCD_Shedule首先把TCD_Execute_HISR设置为当前线程TCD_Current_Thread,优先调度HISR,如果HISR运行过程中激活了一个比当前被中断任务优先级更高的任务,则TCD_Execute_Task会被修改,等HISR运行完毕,则将TCD_Execute_Task设置成当前线程。如果TCD_Execute_Task没被修改,则被中断的任务恢复运行;如果有更高级的任务ready,则等高级任务挂起后,被中断的任务才能恢复运行。
iii)、中断产生时,有一个高级中断服务程序HISR在运行。如果本次中断的HISR优先级比当前的HISR(TCD_Execute_HISR)优先级低,则LISR激活HISR时不会修改TCD_Execute_HISR,中断恢复后,继续执行中断前的HISR(上下文保护时已给当前的HISR建立了一个中断类型的堆栈帧),然后再根据HISR的优先级进行调度。如果本次中断的HISR优先级比当前的HISR优先级高,则LISR激活HISR时会修改TCD_Execute_HISR,中断恢复后,优先执行优先级高的HISR,等到TCT_Shedule调度到本次被中断的HISR时,被中断的HISR接着被中断的部分继续执行。
⑦、HISR的激活(TCT_Active_HISR)
HISR是在LISR中被激活的,TCT_Active_HISR只是激活由LISR指定的HISR以及修改TCD_Execute_HISR,并不真正地执行HISR,HISR在TCT_Shedule中才被真正地调度执行。
激活HISR首先根据激活次数hisr->tc_activation_count来判断该HISR是否已被激活。 如果HISR已被激活,则只将激活次数tc_activation_count加1。
如果该HISR没有被激活过,且该HISR的优先级激活链表为空,则将该HISR挂到本优先级激活链表上,同时根据HISR的优先级决定是否修改TCD_Execute_HISR。
如果该HISR没有被激活过,且该HISR的优先级激活链表非空,则直接将该HISR挂到本优先级激活链表的尾指针,不用修改TCD_Execute_HISR,因为本优先级激活链表的头指针就有可能是TCD_Execute_HISR,或者有更高优先级的HISR已被激活。
⑧、HISR的调度
中断恢复后,如果当前的TCD_Execute_HISR是被中断停下来的HISR,则经TCT_Shedule调度后,被中断的HISR恢复运行。
11
Nucleus分析报告
如果TCD_ Execute_HISR已被修改,是一个新的HISR,则TCT_Shedule会将该HISR放入TCT_HISR_Shell中进行调度。
TCT_HISR_Shell完成HISR的调度。
首先循环调度当前的TCD_Execute_HISR,也就是循环执行HISR创建时用户指定的入口函数,直至其激活次数tc_activation_count等于0。如果TCD_Execute_HISR只被激活了一次,则HISR的入口函数只会执行一次。
TCD_Execute_HISR调度完毕(激活次数为0),如果TCD_Execute_HISR所在优先级的激活链表只有这一个HISR,则将本优先级激活链表清空(将TCD_Active_HISR_Heads[X]及TCD_Active_HISR_Tails[X]置为
NU_NULL),然后从激活链表头指针数组
TCD_Active_HISR_Heads中按优先级顺序搜索到一个已被激活的最高优先级的HISR,由此来修改TCD_Execute_HISR,如果没有其他HISR被激活,则TCD_Execute_HISR为空指针。 TCD_Execute_HISR调度完毕,如果TCD_Execute_HISR所在优先级激活链表不只这一个HISR被激活,则将TCD_Execute_HISR从本优先级激活链表删除,将本优先级下一个激活的HISR设置为TCD_Execute_HISR。
最后,为当前正在调度的HISR建立一个solicited类型的堆栈帧,将堆栈指针保存在HISR控制块中,清除当前线程TCD_Current_Thread,将堆栈切换到系统堆栈顶部TCD_System_Stack。然后将控制权交给TCT_Shedule重新调度,如果还有其它的HISR被激活,则重复上面的过程;否则进入任务的调度,或在TCT_Shedule死循环,等待HISR被激活或任务ready。
一个完整的中断处理流程如下:
12
相关推荐: