和其它内核一样,μC/OS-Ⅱ为了处理临界段代码需要关中断,处理完毕后再开中断。这使得μC/OS-Ⅱ能够避免同时有其它任务或中断服务进入临界段代码。关中断的时间是实时内核开发商应提供的最重要的指标之一,因为这个指标影响用户系统对实时事件的响应性。μC/OS-Ⅱ努力使关中断时间降至最短,但就使用μC/OS-Ⅱ而言,关中断的时间很大程度上取决于微处理器的架构以及编译器所生成的代码质量。
微处理器一般都有关中断/开中断指令,用户使用的C语言编译器必须有某种机制能够在C中直接实现关中断/开中断地操作。某些C编译器允许在用户的C源代码中插入汇编语言的语句。这使得插入微处理器指令来关中断/开中断很容易实现。而有的编译器把从C语言中关中断/开中断放在语言的扩展部分。μC/OS-Ⅱ定义两个宏(macros)来关中断和开中断,以便避开不同C编译器厂商选择不同的方法来处理关中断和开中断。μC/OS-Ⅱ中的这两个宏调用分别是:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。因为这两个宏的定义取决于所用的微处理器,故在文件OS_CPU.H中可以找到相应宏定义。每种微处理器都有自己的OS_CPU.H文件。
********************************************************************************* * * 函数名 : OS_ENTER_CRITICAL( ) asm {PUSHF; CLI} * 描述 : Disable interrupts, 用于代码进入临界区时关闭中断 * 实现 : os_cpu.h中 -=> #define OS_ENTER_CRITICAL() asm {PUSHF; CLI} * ********************************************************************************* * * 函数名 : OS_EXIT_CRITICAL( ) * 描述 : Enable interrupts, 用于代码离开临界区时开启中断 * 实现 : os_cpu.h中 -=> #define OS_EXIT_CRITICAL() asm POPE * *********************************************************************************
void Task(void *pdata) { OS_ENTER_CRITICAL(); //关闭中断 访问并且修改全局变量 // uCOS II 临界代码段 OS_EXIT_CRITICAL(); //打开中断 }
在UCOS2的源代码及相关应用程序中,总会出现往往通过OS_CRITICAL_METHOD宏来使用和实现不同的开关中断代码宏,
/* ********************************************************************************************************* * Intel 80x86 (Real-Mode, Large Model) * * Method #1: Disable/Enable interrupts using simple instructions. After critical section, interrupts * will be enabled even if they were disabled before entering the critical section. * * Method #2: Disable/Enable interrupts by preserving the state of interrupts. In other words, if * interrupts were disabled before entering the critical section, they will be disabled when * leaving the critical section. * * Method #3: Disable/Enable interrupts by preserving the state of interrupts. Generally speaking you * would store the state of the interrupt disable flag in the local variable 'cpu_sr' and then * disable interrupts. 'cpu_sr' is allocated in all of uC/OS-II's functions that need to * disable interrupts. You would restore the interrupt disable state by copying back 'cpu_sr' * into the CPU's status register. ********************************************************************************************************* */ // Method #1: #ifdef OS_CRITICAL_METHOD 1 # #define OS_ENTER_CRITICAL() __asm__ { "CLI" } #define OS_EXIT_CRITICAL() __asm__ { "STI" } # #endif // Method #2 #ifdef OS_CRITICAL_METHOD 2 # #define OS_ENTER_CRITICAL() __asm__ { "PUSHF\n\tCLI" } #define OS_EXIT_CRITICAL() __asm__ { "POPF" } # #endif // Method #3 #ifdef OS_CRITICAL_METHOD 3 # #define OS_ENTER_CRITICAL() { cpu_str = OSCPUSaveSR( ) } #define OS_EXIT_CRITICAL() { OSCPURestoreSR(cpu_str) } # #endif
第一种方式,OS_ENTER_CRITICAL()简单地关中断,OS_EXIT_CRITICAL()简单地开中断。这种方式虽然简单高效,但无法满足嵌套的情况。如果有两层临界区保护,在退出内层临界区时就会开中断,使外层的临界区也失去保护。虽然ucos的内核写的足够好,没有明显嵌套临界区的情况,但谁也无法保证一定没有,无法保证今后没有,无法保证在附加的驱动或什么位置没有,所以基本上第一种方法是没有人用的。
第二种方式,OS_ENTER_CRITICAL()会在关中断前保存之前的标志寄存器内容到堆栈中,OS_EXIT_CRITICAL()从堆栈中恢复之前保存的状态。这样就允许了临界区嵌套的情况。但现在看来,这种方法还存在很大的问题,甚至会出现致命的漏洞。
比如对于下面类似的代码