探索ucos-ii之路--(二)临界区

临界区概述

和其它内核一样,μ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
*
*********************************************************************************

OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()通常用来保护应用程序中的临界代码;
然而要特别小心,如果在调用一些如OSTimeDel()之类的功能函数之前关中断,应用程序将会死机;
原因是任务被挂起一段时间,直到挂起时间到,但由于中断关掉了,
时钟节拍中断一直得不到服务,显然所有的挂起类调用都有这样的问题,所以要特别小心。

作为一条普遍适用的规则,调用UCOS-ii功能函数时,中断总是开着的。
并且我们自己应用程序中中断关闭的时间应该尽可能的短, 
避免系统关中断时间太长可能会引起中断丢失。

使用详解

使用详解, 比如说我们的某个任务在执行过程中需要访问并且修改某个全局变量,
而如果系统中也有其他任务要访问这个变量, 这时候修改可能会被其他程序的访问造成一些影响 
那么这时候访问这个全局变量的代码就是临界区, 我们在操作时就需要开关中断


void Task(void *pdata)
{
    OS_ENTER_CRITICAL(); //关闭中断

	访问并且修改全局变量	// uCOS II 临界代码段

    OS_EXIT_CRITICAL(); //打开中断
}

OS_ENTER_CRITICAL ( ) 和 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()从堆栈中恢复之前保存的状态。这样就允许了临界区嵌套的情况。但现在看来,这种方法还存在很大的问题,甚至会出现致命的漏洞。

比如对于下面类似的代码



你可能感兴趣的:(C++,源代码,宏,汇编语言,ucos-II)