FreeRTOS学习(九)资源管理

1.资源管理概述

1.1 必要性

  在多任务系统中,会存在一种潜在的风险。比如,当一个任务正在使用某个资源时,被另一个任务或中断抢占访问该资源,将造成数据损坏。可能存在类似风险的场景有以下几种:
(1)访问外设

  • 比如 UART I2C SPI 这些常用的公共资源,存在多个任务共享的情况

  举个例子,任务A通过串口向主机发送传感器数据,任务B抢占任务A发送命令请求,这时便破坏了传感器数据的完整性。
(2)读-改-写操作
  在代码被编译成汇编语言后,有些寄存器值的修改过程是分多步完成的:①从内存中读到寄存器,②在寄存器中修改数据,③然后再写回内存,即“读-改-写”。因此在这个过程中也有可能被任务或中断抢占破坏这个完整过程。
(3)变量的非原子访问
  更新结构体的多个成员变量。
(4)函数重入
  被多个任务调用的函数有可能是不可重入函数。

1.2 互斥机制

  由于多任务系统存在以上隐患,FreeRTOS总是希望访问资源的这段代码在执行的时候全程不要被打断,或者即使打断但是其它任务不能访问同样的资源。
  全程不被打断其实过于苛刻,所以基本都是专注于能够被打断但是重入后依然没有问题的设计。任务被打断的原因有2个:中断(外部)的到来、调度器中断(内部)调度任务,可以简述为外部中断任务中断,所以只要解决这2个问题,就可以实现资源互斥访问机制。
在这里插入图片描述

  • 因为都是中断,所以最粗暴的方式就是直接关闭所有中断(临界区),这样任务中断和外部中断都不能打断这段代码,但是这种方法太粗暴,FreeRTOS应该要实时响应外部中断
  • 所以可以只关任务中断而打开外部中断(挂起调度器),这样还是有问题,系统有时候不能关闭任务切换,比如存在周期性任务
  • 那么就可以用一种申请-释放的方式访问资源(互斥量),申请的一方即使被打断,后来者也不能访问,可是申请-释放的方式是公用的,容易导致死锁和优先级反转
  • 所以原则上不应该相信其它任务可以很好管理资源,于是专门派一个任务负责资源的分配和释放(守护任务),其它任务不能公用,只能间接使用。

  通过以上可知,FreeRTOS提供了4种特性用于实现互斥机制,分别是临界区、挂起调度器、互斥量和守护任务,各有优缺点,分别应用在不同的场合。

在这里插入图片描述

2.资源管理方法

2.1 临界区

  一种最为简单粗暴的方法,通过关闭阈值优先级以下的任务来实现互斥,具体实现过程前面讲过。
在这里插入图片描述
  这种方式的要求是临界区之间的代码要尽可能精简,避免影响 FreeRTOS的中断响应,应用场景有任务创建、数据连续读取等。

if (CS1259Ready())             		//等待AD开始信号
{
    taskENTER_CRITICAL();        	//进入临界区

    Z13Adc = ReadADC();
    printf("%d\t\t",Z13Adc); 
    Z13Res = CalRes(Z13Adc);
    printf("%d\r\n",Z13Res);

    taskEXIT_CRITICAL();         	//退出临界区
    vTaskDelay(5/portTICK_RATE_MS);
}
2.2 挂起调度器

  挂起调度器使得任务的执行过程不被其它任务打断,但是可以被外部中断打断
在这里插入图片描述

API功能
vTaskSuspendAll()挂起调度器
xTaskResumeAll()唤醒调度器
vTaskSuspendAll() 
	++uxSchedulerSuspended;	//计数加1,用于嵌套和标记
xTaskResumeAll()
	taskENTER_CRITICAL()	//进入临界区
	--uxSchedulerSuspended	//计数减1,用于嵌套和标记
	prvAddTaskToReadyList()	//调出就绪任务
	taskEXIT_CRITICAL()		//退出临界区

  vTaskSuspendAll() 挂起调度器只是简单地加1计数,因为这个uxSchedulerSuspended全局变量会在Systick中断中使用(具体到xTaskIncrementTick()函数),如果uxSchedulerSuspended不为0(挂起),那么xTickCount不再计数,表达系统心跳暂时停止,于是调度器也不会进行任务切换。

2.3 互斥量

  互斥量不需要关闭任何中断,它采用一种申请-释放的方式去访问资源,申请和释放的其实不是资源,而是代表资源的钥匙,在这种方式下,资源与一把钥匙绑定,要想访问资源,必须先拿到这把钥匙,没有钥匙的只能等待前者释放,所以即使拥有钥匙的一方被打断,后者也不能访问资源。
在这里插入图片描述

2.4 守护任务

  无论是临界区、挂起调度器还是互斥量,它们的使用都带来非常多的问题,核心原因是资源是公用的,资源的所有权和使用权都是公共的,任何一个任务都可以去直接操作,为了解决这个核心原因,可以把资源私有化,所有权和使用权都在一个任务A上面,其它任务只能间接去访问,比如把要写入的数据发给A,由A去写入,把要读的数据要求发给A,由A去读然后返回数据等等。
在这里插入图片描述
  守护任务的实现不需要什么特性或者机制的支持,是一个协议,设置好任务的代码逻辑后就可以实现,它非常干净利落,把资源私有化后,阻塞等待其它任务的要求
  例如穿戴手表中的主机从机之间的数据传输,从机所有传感器的数据都是通过队列发送到守护任务,由守护任务统一发送给主机

3.全局变量

  在嵌入式开发中,难免要使用一些全局变量,如果存在多个任务对同一个全局变量操作,那么将带来共享资源管理问题(上面提到的原子操作问题),即需要对这个全局变量进行保护。

A任务正在使用全局变量S,A任务由于任务切换暂停运行切换到B任务,而B任务也要使用S,这时候B任务修改了S的值。当再次切换到A任务的时候这个变量S就变了,A任务可能就运行出错。

  如果存在以上使用场景,可以为全局变量添加互斥保护。即在任务对全局变量进行操作时必须获得互斥量,然后进行读写,读写完后释放信号量。

A任务要使用队列S,先申请,申请成功以后才可以使用。B任务也要使用S的时候也要先申请,当时发现S已经被A任务使用了,所以B任务就没法使用(假设当前的队列长度为1),直到A任务使用完S并且释放掉B任务才申请使用!

  这里提到了一种特殊情况:如果一个任务只是对全局变量进行读操作,一个任务只是对全局变量进行写操作,这种情况是是否还要对全局变量添加互斥保护。

  • 这种情况最好也添加互斥保护,在程序架构设计时尽量避免这种情况。

4.总结

互斥机制本质优点缺点
临界区关闭任务中断和外部中断确保资源访问不可能被打断,资源访问过程简单其余任务停滞、外部中断得不到响应
挂起调度器关闭任务中断可以响应外部中断,不能被其它任务打断,资源访问过程简单其余任务停滞
互斥量资源需要申请和释放不需要关中断,资源访问过程简单容易出现死锁和优先级反转
守护任务资源私有化不再出现以上问题缺点资源访问过程复杂,间接访问可能带来速度和效率问题
  • 资源一般需要互斥访问,因此需要互斥机制
  • FreeRTOS可以实现4种互斥机制,临界区、挂起调度器、互斥量和守护任务
  • 4种互斥机制各有优缺点,需要在不同的场合使用
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值