RTOS共享资源管理——临界区保护与调度锁

曾今只是使用过移植好的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);
}

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值