摘自:http://www.arm32.com/post/229.html
为了处理临界区代码,必须关中断,等处理完毕后,再开中断。关中断可以避免其他任务或中断进入临界区代码。uC/OS-II定义了这两个宏来实现,但注意一条:调用uC/OS-II功能函数时,中断应该总是开着的。
#define OS_ENTER_CRITICAL() disable_int()
#define OS_EXIT_CRITICAL() enable_int()
但这样有一个问题,如果禁止中断的情况下调用 uC/OS-II 功能函数,那么从功能函数返回时,中断可能变成允许的了,而实际上还是希望是禁止的。
2 )当 OS_CRITICAL_METHOD= = 2 时,实现如下:
#define OS_ENTER_CRITICAL()
asm(“PUSH PSW”);
asm(“DI”);
#define OS_EXIT_CRITICAL()
asm(“POP PSW”);
执行 OS_ENTER_CRITICAL() 时,先将中断状态保存到堆栈,然后关中断;执行 OS_EXIT_CRITICAL() 时,再从堆栈中恢复原来的中断开 / 关状态。这种方法不会改变中断状态,避免前面的问题。
#define OS_ENTER_CRITICAL()
cpu_sr = get_processor_psw();
disable_interrupts();
#define OS_EXIT_CRITICAL()
set_ processor_psw(cpu_sr);
====================
这里注意的是OS_CRITICAL_METHOD ,ucos提供了3种方法实现,第一种方法是直接简单的开关中断方式,但是一旦嵌套会发生意外,比如:
view plaincopy to clipboardprint?
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
void Task (void *data)
.........
OS_ENTER_CRITICAL();
//进入临界区1
//调用了某一函数,该函数也需要进入临界区:
{
OS_ENTER_CRITICAL();
................
OS_EXIT_CRITICAL();
}
//这里就自然变成废墟了
...........
//临界区切掉
OS_EXIT_CRITICAL();
}
void Task (void *data)
{
.........
OS_ENTER_CRITICAL();
//进入临界区1
//调用了某一函数,该函数也需要进入临界区:
{
OS_ENTER_CRITICAL();
................
OS_EXIT_CRITICAL();
}
//这里就自然变成废墟了
...........
//临界区切掉
OS_EXIT_CRITICAL();
}
此方法太多弊端,所以新内核中看不到她的影子了
于是出现第二种方法,执行OS_ENTER_CRITICAL()时首先保存中断状态到堆栈中,然后关中断,执行OS_EXIT_CRITICAL()时,再从堆栈中恢复原来的中断开/关状态。这种方法不会改变中断状态,由于用到堆栈,这样会带来隐忧,看邵贝贝翻译的有这样说:
“但是,用户在使用这种方法的时候还得十分小心,因为如果用户在调用象OSTimeDly()之类的服务之前就禁止中断,很有可能用户的应用程序会崩溃。发生这种情况的原因是任务被挂起直到时间期满,而中断是禁止的,因而用户不可能获得节拍中断!很明显,所有的PEND调用都会涉及到这个问题,用户得十分小心。一个通用的办法是用户应该在中断允许的情况下调用µC/OS-Ⅱ的系统服务!”
第3种方法直接保存到任务局部变量中去
#if OS_CRITICAL_METHOD == 3
#define OS_ENTER_CRITICAL() (cpu_sr = OSCPUSaveSR())
#define OS_EXIT_CRITICAL() (OSCPURestoreSR(cpu_sr))
#endif
避免了使用堆栈