临界段的保护
什么是临界段
临界段,用一句话概括就是一段在执行时不能被中断的代码段。在RT-Thread中,临界段最常出现的场景就是对全局变量的操作,全局变量就好像是一个靶子,谁都可以对其开枪,但是有一人开枪,其他人就不能开枪,否则就不知道是谁命中了靶子。
那么什么情况下临界段会被打断?一个是系统调度,还有一个就是外部中断。在RT-Thread中,系统调度最终也是产生PendSV中断,在PendSV Handler中实现线程的切换,所以还是可以归结为中断。既然这样,RT-Thread对临界段的保护就处理得很干脆了,直接把中断关闭,但NMI FAULT和硬FAULT除外。
Cortex-M内核快速关中断指令
为了快速地开关中断,Cortex-M内核专门设置了一条CPS指令,有4种用法。
代码清单CPS指令用法
CPSID I ;PRIMASK=1 ;关中断 CPSIE I ;PRIMASK=0 ;开中断 CPSID F ;FAULTMASK=1 ;关异常 CPSIE F ;FAULTMASK=0 ;开异常
代码清单中PRIMASK和FAULTMAST是Cortex-M内核中3个中断屏蔽寄存器中的2个,还有一个是BASEPRI,有关这3个寄存器的详细用法如表所示。
关中断
RT-Thread关中断的函数在contex_rvds.s中定义,在rthw.h中声明,具体实现参见代码清单4-2。
关中断
/* ; * rt_base_t rt_hw_interrupt_disable(); ; */ rt_hw_interrupt_disable PROC EXPORT rt_hw_interrupt_disable MRS r0, PRIMASK CPSID I BX LR ENDP
1):关键字PROC表示汇编子程序开始。
2):使用EXPORT关键字导出标号rt_hw_interrupt_disable,使其具有全局属性,在外部头文件声明后(在rthw.h中声明),就可以在C文件中调用。
3):通过MRS指令将特殊寄存器PRIMASK寄存器的值存储到通用寄存器r0。当在C中调用汇编的子程序返回时,会将r0作为函数的返回值。所以在C中调用rt_hw_interrupt_disable()时,需要事先声明一个变量用来存储rt_hw_interrupt_disable()的返回值,即r0寄存器的值,也就是PRIMASK的值。
4):关闭中断,即使用CPS指令将PRIMASK寄存器的值置1。
5):子程序返回。
6):ENDP表示汇编子程序结束,与PROC成对使用。
开中断
RT-Thread开中断的函数在contex_rvds.s中定义,在rthw.h中声明。
开中断
/* * void rt_hw_interrupt_enable(rt_base_t level); */ rt_hw_interrupt_enable PROC EXPORT rt_hw_interrupt_enable MSR PRIMASK, r0 BX LR ENDP
1):关键字PROC表示汇编子程序开始。
2):使用EXPORT关键字导出标号rt_hw_interrupt_enable,使其具有全局属性,在外部头文件声明后(在rthw.h中声明),就可以在C文件中调用。
3):通过MSR指令将通用寄存器r0的值存储到特殊寄存器PRIMASK。
4):子程序返回。
5):ENDP表示汇编子程序结束,与PROC成对使用。
临界段代码的应用
在进入临界段之前,我们会先把中断关闭,退出临界段时再把中断打开,而且Cortex-M内核中设置了快速关中断的CPS指令,开关中断的函数的实现和临界段代码的保护。
开关中断的函数的实现和临界段代码的保护 开关中断的函数的实现 /* * void rt_hw_interrupt_disable(); */ rt_hw_interrupt_disable PROC EXPORT rt_hw_interrupt_disable CPSID I BX LR ENDP ;/* ; * void rt_hw_interrupt_enable(void); ; */ rt_hw_interrupt_enable PROC EXPORT rt_hw_interrupt_enable CPSIE I BX LR ENDP PRIMASK = 0; /* PRIMASK初始值为0,表示没有关中断 */ /* 临界段代码保护 */ { /* 临界段开始 */ rt_hw_interrupt_disable(); /* 关中断,PRIMASK = 1 */ { /* 执行临界段代码,不可中断 */ } /* 临界段结束 */ rt_hw_interrupt_enable(); /* 开中断,PRIMASK = 0 */ }