曾今只是使用过移植好的RTOS进行任务开发,对其实现的底层原理一直一知半解,正好接触到了李述桐老师的课程以及一些网上的资料,让我对实时操作系统的原理有了更深的理解,特此把一些原理和思考记录下来和大家一起分享,同时若有理解上的错误还恳请大家能够及时指正。
1、什么是共享资源?为什么要进行保护?以及何时该对资源进行保护?
RTOS中有多个任务函数,同时还有中断服务函数。共享资源就是在程序中所有的函数都可以访问到的一块内存空间,最直接的例子就是全局变量。
当任务A函数在操作一个全局变量X,优先级更高的任务B准备就绪后触发PendSV异常切换到任务B,巧的是任务B也要操作全局变量X。这个时候又有一个中断响应函数操作了全局变量X,当退出中断函数的时候,任务B对全局变量X的操作就作废了,当切换回任务A的时候,任务A先前对全局变量X的操作就作废了。为了避免这种现象的发生,就需要对这块资源进行保护
资源保护有两种方法,第一种是临界区保护:在进行全局变量X的操作前关闭中断,处理完成后再开启中断。因为在RTOS中只有其他任务函数和中断函数可能会对全局变量X进行操作,而任务调度又是在PendSV中断中完成的,所以直接关闭中断是最直接的保护方式。缺点是关闭中断后,所有的中断函数就不会响应,而且不能再关中断后使用系统的Delay函数,因为系统Delay会触发PendSV函数,这样会导致系统卡死。第二种是调度锁上锁:在进行全局变量X的操作前禁止任何任务切换,一直运行当前任务,处理完成后再允许任务切换。这样就可以避免RTOS中的其他任务函数对全局变量X进行操作,但还是会触发中断函数。只是PendSV中断函数不再进行任务切换而已。缺点是若中断函数中有对全局变量X的操作就不能使用这种方式进行保护了。
2、代码实现
在ucos-ii中的临界区保护有三种实现方式,这里在M3内核中使用第三种方式也是最常用的方式,主要实现的方式是,进入临界区保护:保存中断寄存器的状态,关中断;退出临界区保护:恢复先前中断寄存器的状态。下面是两种实现方式,原理都是一样的
使用汇编的实现方式:
#if OS_CRITICAL_METHOD == 3
#define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SR_Save();}
#define OS_EXIT_CRITICAL() {OS_CPU_SR_Restore(cpu_sr);}
#endif
OS_CPU_SR_Save
MRS R0, PRIMASK ;读取PRIMASK到R0,R0为返回值
CPSID I ;PRIMASK=1,关中断(NMI和硬件FAULT可以响应)
BX LR ;返回
OS_CPU_SR_Restore
MSR PRIMASK, R0 ;读取R0到PRIMASK中,R0为参数
BX LR ;返回
李述桐老师的实现方式:
#include "ARMCM3.h"
/*******临界区保护 进入临界区关中断*********/
uint32_t tTaskEnterCritical (void)
{
uint32_t primask = __get_PRIMASK(); //获取中断状态
__disable_irq(); //关中断
return primask; //返回primask状态
}
/*******临界区保护 退出临界区恢复中断*********/
void tTaskExitCritical (uint32_t status)
{
__set_PRIMASK(status); //恢复中断状态
}
调度锁上锁的原理就是定义一个uint8_t全局变量OSLockNesting、schedLockCount,当其不为0时,进入pendSV中断后就不切换任务直接退出,所以上锁就是对其加1使其不为0,所以调度锁一定要成对使用,要不然会让OSLockNesting、schedLockCount一直大于0,导致任务切换失败,影响系统实时性。
void OSSchedLock (void)
{
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
if (OSRunning == OS_TRUE) { /* Make sure multitasking is running */
OS_ENTER_CRITICAL();
if (OSIntNesting == 0u) { /* Can't call from an ISR */
if (OSLockNesting < 255u) { /* Prevent OSLockNesting from wrapping back to 0 */
OSLockNesting++; /* Increment lock nesting level */
}
}
OS_EXIT_CRITICAL();
}
}
void OSSchedUnlock (void)
{
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
if (OSRunning == OS_TRUE) { /* Make sure multitasking is running */
OS_ENTER_CRITICAL();
if (OSLockNesting > 0u) { /* Do not decrement if already 0 */
OSLockNesting--; /* Decrement lock nesting level */
if (OSLockNesting == 0u) { /* See if scheduler is enabled and ... */
if (OSIntNesting == 0u) { /* ... not in an ISR */
OS_EXIT_CRITICAL();
OS_Sched(); /* See if a HPT is ready */
} else {
OS_EXIT_CRITICAL();
}
} else {
OS_EXIT_CRITICAL();
}
} else {
OS_EXIT_CRITICAL();
}
}
}
李述桐老师的实现方式:精简版
void tTaskSechDisable (void) //调度器上锁函数 禁止调度
{
uint32_t status = tTaskEnterCritical();
if(schedLockCount<255)
{
schedLockCount++;
}
tTaskExitCritical(status);
}
void tTaskEnable (void) //调度器解锁函数 允许调度
{
uint32_t status = tTaskEnterCritical();
if(schedLockCount>0)
{
if(--schedLockCount == 0)
{
tTaskSched();
}
}
tTaskExitCritical(status);
}