前言
本文对ucosiii中的临界区代码进行分析
正文
ucos源码中经常出现如下代码:
CPU_SR_ALLOC();
OS_CRITICAL_ENTER();
OS_CRITICAL_EXIT_NO_SCHED();
这是为了开启临界区,临界区内将不会出现任务切换。
假设没有临界区,如果有甲乙
两个任务,甲
要执行一段代码把abc
三个值设置成123
,如果甲
把ab
设置完12
后出现任务切换,切换到任务乙
,乙
要把a
设置成3
,那么切换回来后,甲
继续把c
设置成3
,这时甲
认为abc
的值为123
,实际上却是323
。
在OS_CRITICAL_ENTER()
和OS_CRITICAL_EXIT_NO_SCHED()
之间的代码称为临界区,临界区内将不会产生任务切换。
CPU_SR_ALLOC()
的源码如下:
#define CPU_SR_ALLOC() CPU_SR cpu_sr = (CPU_SR)0
只是声明了一个变量cpu_sr
OS_CRITICAL_ENTER()
的源码如下:
#define OS_CRITICAL_ENTER() \
do { \
CPU_CRITICAL_ENTER(); \
OSSchedLockNestingCtr++; \
if (OSSchedLockNestingCtr == 1u) { \
OS_SCHED_LOCK_TIME_MEAS_START(); \
} \
CPU_CRITICAL_EXIT(); \
} while (0)
CPU_CRITICAL_ENTER()
是为了关闭系统中断,CPU_CRITICAL_EXIT()
是为了开启系统中断,核心部分是OSSchedLockNestingCtr++
,
OS_CRITICAL_EXIT_NO_SCHED()
源码为:
#define OS_CRITICAL_EXIT_NO_SCHED() \
do { \
CPU_CRITICAL_ENTER(); \
OSSchedLockNestingCtr--; \
if (OSSchedLockNestingCtr == (OS_NESTING_CTR)0) { \
OS_SCHED_LOCK_TIME_MEAS_STOP(); \
} \
CPU_CRITICAL_EXIT(); \
} while (0
其中的核心为OSSchedLockNestingCtr--
;
接下来对OSSchedLockNestingCtr
变量进行分析,起到重要作用的两处是在OSSched (void)
函数和OSIntExit (void)
函数里,两个函数中都有如下代码:
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* Scheduler still locked?
return;
}
可见两个函数都是判断到OSSchedLockNestingCtr
不为0就不会执行。
两个函数的最后分别调用了:
#define OS_TASK_SW() NVIC_INT_CTRL = NVIC_PENDSVSET
#define OSIntCtxSw() NVIC_INT_CTRL = NVIC_PENDSVSET
这两个宏都是开启PendSV
中断,进行任务切换,对PendSV
的分析可以看【UCOSIII源码阅读笔记】第三篇——PendSV异常处理分析
所以开启临界区的最终目的是阻止开启PendSV
中断,也就是阻止开启任务切换。
而对于应用层来说,ucos提供了两个函数用来实现OSSchedLockNestingCtr++
和OSSchedLockNestingCtr--
分别是:
void OSSchedLock (OS_ERR *p_err)
任务切换加锁
void OSSchedUnlock (OS_ERR *p_err)
任务切换解锁
通过这两个函数对一段代码进行加锁保护,就可以防止出现访问冲突了。