分类: 嵌入式
2015-07-21 21:50:38
同样是通过关中断来保护临界区,OS_ENTER_CRITICAL/OS_EXIT_CRITICAL一共实现了三种实现方式,如下所示:
os_cpu_a.asm中
OS_CPU_SR_Save
MRS R0, PRIMASK ; Set prio int mask to mask all (except faults)
CPSID I
BX LR
OS_CPU_SR_Restore
MSR PRIMASK, R0
BX LR
第一种方式,OS_ENTER_CRITICAL()简单地关中断,OS_EXIT_CRITICAL()简单地开中断。这种方式虽然简单高效,但无法满足嵌套的情况。如果有两层临界区保护,在退出内层临界区时就会开中断,使外层的临界区也失去保护。虽然ucos的内核写的足够好,没有明显嵌套临界区的情况,但谁也无法保证一定没有,无法保证今后没有,无法保证在附加的驱动或什么位置没有,所以基本上第一种方法是没有人用的。
第二种方式,OS_ENTER_CRITICAL()会在关中断前保存之前的标志寄存器内容到堆栈中,OS_EXIT_CRITICAL()从堆栈中恢复之前保存的状态。这样就允许了临界区嵌套的情况。但现在看来,这种方法还存在很大的问题,甚至会出现致命的漏洞。
在OS_CRITICAL_METHOD=2的情况下,假设有如下代码:
第三种,在关中断前,使用局部变量保存中断状态。这也是几乎所有实时操作系统共有的选择。但ucos是一朵奇葩,为了兼容前两种方式,OS_ENTER_CRITICAL()/ OS_EXIT_CRITICAL()宏定义并没有提供传递状态参数的功能。所以它的临界去必须这么用:
好吧,如果有了问题,就要有解决方案,毕竟我不是为了让大家对ucos失去信心的。我们可以参考下一般的实时操作系统是如何实现关中断临界区的,就是以显式的方式用局部变量保存中断状态。
int_lock()和int_unlock()的可以用汇编更高效地实现,也可以选择只恢复中断标志的状态。这种方法让我们显示地管理状态保存的情况,我觉得至少要比宏定义清楚多了
C代码: 汇编码:
OS_CPU_SR cpu_sr = 0; 0x08003236 F04F0900 MOV r9,#0x00
OS_ENTER_CRITICAL(); 0x08003244 F7FDFD6A BL.W 0x08000D1C (0x080001D0)
0x08003248 4681 MOV r9,r0
;保存PRIMASK 到cpu_sr
OS_CPU_SR_Save
MRS R0, PRIMASK ; 0x08000D1C 就是 OS_CPU_SR_Save的函数代表的地址
0x08000D1C F3EF8010 MRS r0,PRIMASK
; Set prio int mask to mask all (except faults)
CPSID I 0x08000D20 B672 CPSID I
BX LR 0x08000D22 4770 BX lr
0x0800326E 4648 MOV r0,r9 ;恢复
OS_CPU_SR_Restore 0x08003270 F7FDFD58 BL.W 0x08000D24
MSR PRIMASK, R0 0x08000D24 F3808810 MSR PRIMASK,r0
BX LR 104: BX LR
// OS_CPU_SR cpu_sr = 0;所以cpu_sr是用来保存PRIMASK 然后再恢复用的