FreeRTOS_临界资源管理

1.临界资源

  • 在 FreeRTOS 中,临界资源是指在访问共享资源时需要保持原子性的部分,即必须完整运行,不能被打断的代码段。在多任务环境下,当多个线程需要同时访问同一个共享资源时,如果不加以限制,可能会导致数据损坏或不一致的情况。

常用的临界资源:

  • 1、外设:通信需要严格时序,如IIC、SPI等。
  • 2、系统:系统自身需求
  • 3、用户:用户需求

对于临界段要做到执行时间越短越好,否则会影响系统的实时性。

2. 临界资源保护

临界段只有在出现任务调度系统中断时才会被打断。
所以我们可以通过屏蔽中断暂停任务调度来实现临界资源保护。

  • 若有两个任务都可以使用临界资源,那就在任一任务访问临界资源前,先禁止任务调度
  • 若有任务和多个优先级不同的中断都要访问临界资源,则在访问临界资源前,先关闭中断

3. 屏蔽中断

屏蔽中断有两套宏:任务中屏蔽中断和ISR中屏蔽中断:

 /*任务中使用*/
 taskENTER_CRITICAL();/*用于进入临界区,即禁用中断并锁定共享资源*/
 taskEXIT_CRITICAL();/*用于退出临界区,即释放锁定的共享资源并重新启用中断*/
 L
 /*ISR中使用*/
 taskENTER_CRITICAL_FROM_ISR();
 taskEXIT_CRITICAL_FROM_ISR();

2.1 在任务中屏蔽中断

在任务中屏蔽中断的示例代码如下:

int a;

void add_func(int val)
{
/* 在任务中,当前时刻中断是使能的
 * 执行下面这句代码后,屏蔽中断
 */
	taskENTER_CRITICAL();	

	/* 访问临界资源 */
    a += val;

	/* 重新使能中断 */
	taskEXIT_CRITICAL();
}
  • taskENTER_CRITICAL()宏用于进入临界区,它会将当前任务的优先级设置为最高,禁止所有中断,保证在临界区的代码不会被其它任务或中断打断。
  • taskEXIT_CRITICAL()宏用于退出临界区,它会将任务优先级恢复至原来的值,并允许中断,让其它任务或中断可以继续执行。
  • 这套taskENTER_CRITICA()/taskEXIT_CRITICAL()宏,是可以递归使用的,它的内部会记录嵌套的深度,只有嵌套深度变为0时,调用taskEXIT_CRITICAL()才会重新使能中断。

2.2 在ISR中屏蔽中断

要使用含有"FROM_ISR"后缀的宏,示例代码如下:

void a_fuc( void )
{
    UBaseType_t uxSavedInterruptStatus;
    uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();
    /* 访问临界资源 */
    a++;

    taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
}

void vAnInterruptServiceRoutine( void )
{
    /* 用来记录当前中断是否使能 */
    UBaseType_t uxSavedInterruptStatus;
    
    /* 在ISR中,当前时刻中断可能是使能的,也可能是禁止的
     * 所以要记录当前状态, 后面要恢复为原先的状态
     * 执行这句代码后,屏蔽中断
     */
    uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();
    
    /* 访问临界资源 */
    b++;
    a_fuc();

    /* 恢复中断状态 */
    taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
    /* 现在,当前ISR可以被更高优先级的中断打断了 */
}

使用taskENTER_CRITICA()/taskEXIT_CRITICAL()来访问临界资源是很粗鲁的方法:

  • 中断无法正常运行
  • 任务调度无法进行
  • 所以,之间的代码要尽可能快速地执行

4. 暂停调度器

上面说了,直接进行中断屏蔽是非常粗鲁的,所以我们尽量不使用它。当临界资源只是有两个任务去对它进行访问时,我们可以只暂时屏蔽任务调度,这样仅能保护临界资源还能不妨碍中断的正常执行。
挂起和恢复任务调度器的函数如下:

/* 暂停调度器 */
void vTaskSuspendAll( void );

/* 恢复调度器
 * 返回值: pdTRUE表示在暂定期间有更高优先级的任务就绪了
 *        可以不理会这个返回值
 */
BaseType_t xTaskResumeAll( void );

示例代码如下:

int a;
void xxx_func(int val)
{
	vTaskSuspendAll();

	/* 访问临界资源 */
    a = val;

	xTaskResumeAll();
}

这套vTaskSuspendAll()/xTaskResumeAll()宏,是可以递归使用的,它的内部会记录嵌套的深度,只有嵌套深度变为0时,xTaskResumeAll()才会重新使能调度器。

5、总结

上面两种进行临界区保护的方法各有利弊:

保护方法优点缺点
屏蔽中断简单易用、可靠性高、可嵌套性影响系统(中断)响应
暂停任务调度中断还可以正常响应需配合调度器、易造成优先级反转、可能被中断影响

具体使用哪个还需要根据临界区代码的具体形式和所处位置来决定。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FreeRTOS提供了多种方法来保护临界资源,其中最常用的是使用任务间通信机制中的二值信号量(Binary Semaphore)或互斥量(Mutex)。这两种机制都可以用来保护临界资源,防止多个任务同时访问临界资源导致数据不一致或错误。 使用二值信号量时,可以将信号量的初始值设置为1,表示临界资源未被占用。当一个任务需要访问临界资源时,首先获取信号量,如果信号量的值为1,则表示临界资源未被占用,该任务可以访问临界资源;如果信号量的值为0,则表示临界资源已被占用,该任务需要等待直到信号量的值变为1才能访问临界资源。当任务访问完临界资源后,需要释放信号量,将其值设置为1,表示临界资源已经被释放。 使用互斥量时,可以将互斥量的初始值设置为1,表示临界资源未被占用。当一个任务需要访问临界资源时,首先获取互斥量,如果互斥量的值为1,则表示临界资源未被占用,该任务可以访问临界资源;如果互斥量的值为0,则表示临界资源已被占用,该任务需要等待直到互斥量的值变为1才能访问临界资源。当任务访问完临界资源后,需要释放互斥量,将其值设置为1,表示临界资源已经被释放。 需要注意的是,使用二值信号量或互斥量保护临界资源时,需要保证所有访问该临界资源的任务都使用同一个信号量或互斥量对象进行保护,否则会出现数据不一致或错误的情况。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值