临界区定义#
操作系统在处理关键代码时,也就是进入临界区时,需要关闭中断,防止中断打断当前执行的关键代码,造成系统资源的破坏。
关键就在于关闭中断,控制中断寄存器进行中断关闭。但是,如果单纯的在进入临界区之前关中断,在退出临界区之后开中断,岂不是破坏了原本的中断状态?例如在某些地方可能已经被关闭了中断,那么退出临界区之后我们要恢复的就是关中断的状态。
我们要做的就是在进入临界区前记录一下此刻的中断状态,然后关中断。在退出临界区之后,恢复之前记录的状态即可。
临界区代码#
临界区关中断时,需要操作做的是CPSR寄存器,也就是硬件相关的寄存器,临界区代码也就是port代码。
OS_CPU_SR_Save: CPSID I ;禁用中断 PUSH {R1} ;这里虽然已经保存过R1,但是为了安全冗余还是再次保存R1 MRS R1, BASEPRI ;保存中断屏蔽寄存器,将来用来恢复 MSR BASEPRI, R0 ;将R0传入中断屏蔽寄存器,传入一个优先级,屏蔽这个优先级以下的中断 DSB ;确保数据操作到此为止全部执行完毕,控制流水线 ISB ;确保指令操作到此为止全部执行完毕,控制流水线 ;在修改系统寄存器的场景中,一定要先DSB后ISB MOV R0, R1 ;将R1放到R0作为返回值 POP R1 ;将先前R1的值从栈中弹出 CPSIE I ;启用中断 BX LR ;返回OS_CPU_SR_Restore: CPSID I ;禁用中断 MSR BASEPRI, R0 ;恢复原来的中断屏蔽 DSB ISB ;指令和数据屏障 CPSIE I BX LR ;返回总体的流程大致是:
- 完全关闭中断
- 保存中断屏蔽寄存器BASEPRI
- 传递新的值给BASEPRI
- 将之前保存的BASEPRI的值拿来做返回值并返回
其中传递新的值给BASEPRI,新的值是一个有关硬件和操作系统的值,在这里没有给出。让我们来看一下这个宏定义。
cpu_sr = OS_CPU_SR_Save(CPU_CFG_KA_IPL_BOUNDARY << (8u - CPU_CFG_NVIC_PRIO_BITS))
- CPU_CFG_NVIC_PRIO_BITS CPU中有多少位用来描述中断优先级,对CortexM来说是4位,两位响应优先级,两位抢占优先级。
- CPU_CFG_KA_IPL_BOUNDARY ** 嵌入式操作系统中用到的中断即PendSV和SysTick**等各种中断的最高的中断优先级,也就是说要屏蔽掉所有嵌入式操作系统中的中断,但是允许其他的硬件中断和异常。
在CortexM架构中,优先级又分为主优先级和子优先级,NVIC负责处理二者之间的关系。抢占优先级更高的允许中断其他的中断,响应优先级更高的在同样的抢占优先级下优先顺序执行。但是二者优先级组合在一起一共4位,是整个中断的优先级。而BASEPRI进行屏蔽时,就是使用的这个优先级进行屏蔽。