欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

FreeRTOS笔记(十二)资源管理

程序员文章站 2022-06-22 20:10:53
文章目录01 - 互斥机制1.1 - 临界区1.2 - 挂起调度器1.3 - 互斥量1.4 - 守护任务02 - 互斥机制的区别03 - 总结上一文链接:FreeRTOS笔记(十一)延迟中断  考虑完多任务(中断)之间的通信后,还需要考虑多任务(中断)之间的资源访问,因为资源往往是互斥使用的,比如打印机、串口等等,一个任务在使用的时候,不允许另一个任务去打断,否则就会出现信息不一致的情况,造......

上一文链接:FreeRTOS笔记(十一)延迟中断


  考虑完多任务(中断)之间的通信后,还需要考虑多任务(中断)之间的资源访问,因为资源往往是互斥使用的,比如打印机、串口等等,一个任务在使用的时候,不允许另一个任务去打断,否则就会出现信息不一致的情况,造成混乱

  而一般情况下资源的访问步骤必须是连续的,比如在C语言上代码if(0 == a) a = -1;,其实在CPU内部进行了若干个步骤:加载寄存器的值 → 与0比较 → 根据结果跳转。如果在加载寄存器之后被打断,而且a原来非0但是被修改为0,那么再次重入的时候,本应该不去执行a = -1,但是却执行了
FreeRTOS笔记(十二)资源管理

01 - 互斥机制

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

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

FreeRTOS笔记(十二)资源管理

  经过以上内容,可以了解到其实FreeRTOS提供了很多特性用于实现互斥机制,分别是临界区、挂起调度器、互斥量和守护任务,4中互斥各有优缺点,分别应用在不同的场合

1.1 - 临界区

  FreeRTOS的临界区是关闭所有中断(除掉电中断、undef中断等除外),指宏 taskENTER_CRITICAL()taskEXIT_CRITICAL()之间的代码区间,跟踪源码

taskENTER_CRITICAL()					#进入临界区
	portENTER_CRITICAL()
		vPortEnterCritical()
			uxCriticalNesting++;		#计数加1,用于嵌套
			portDISABLE_INTERRUPTS()	#屏蔽中断
				vPortRaiseBASEPRI()
					msr basepri, configMAX_SYSCALL_INTERRUPT_PRIORITY
portEXIT_CRITICAL()						#退出临界区
	portEXIT_CRITICAL()
		vPortExitCritical()
			uxCriticalNesting--;		#计数减1,用于嵌套
			portENABLE_INTERRUPTS()		#打开中断
				vPortSetBASEPRI( 0 )
					msr basepri, 0

  对于taskENTER_CRITICAL(),最后往basepri寄存器中写入configMAX_SYSCALL_INTERRUPT_PRIORITY,这个值是在FreeRTOSConfig.h中配置的,代表最大的可管理中断,详细可以看配置文件,在这里,可以理解为关闭所有的中断(除掉电中断、undef中断等除外),所以一旦调用taskENTER_CRITICAL(),那么任务就一直处于运行态,直到taskEXIT_CRITICAL()的出现。
  而taskEXIT_CRITICAL(),最后是往basepri寄存器中写入0,意思是开启所有中断,所以临界区就是提供一种最直接粗暴的方式去访问资源。
FreeRTOS笔记(十二)资源管理

1.2 - 挂起调度器

  挂起调度器使得任务的执行过程不被其它任务打断,但是可以被外部中断打断,FreeRTOS提供了挂起/唤醒调度器的API

API 功能
vTaskSuspendAll() 挂起调度器
xTaskResumeAll() 唤醒调度器

  跟踪源码

vTaskSuspendAll() 
	++uxSchedulerSuspended;	#计数加1,用于嵌套和标记
xTaskResumeAll()
	taskENTER_CRITICAL()	#进入临界区
	--uxSchedulerSuspended	#计数减1,用于嵌套和标记
	prvAddTaskToReadyList()	#调出就绪任务
	taskEXIT_CRITICAL()		#退出临界区

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

1.3 - 互斥量

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

  FreeRTOS提供了互斥量的相关API,互斥量本质是一个长度为1的队列,可以回顾队列和通信

API 功能
xSemaphoreCreateMutex() 创建互斥量
xSemaphoreGive() 任务状态下给出信号量
xSemaphoreTake() 任务状态下得到信号量
xSemaphoreTakeFromISR() 中断状态下给出信号量
xSemaphoreTakeFromISR() 中断状态下得到信号量
vSemaphoreDelete() 删除信号量

1.4 - 守护任务

  无论是临界区、挂起调度器还是互斥量,它们的使用都带来非常多的问题,核心原因是资源是公用的,资源的所有权和使用权都是公共的,任何一个任务都可以去直接操作,为了解决这个核心原因,可以把资源私有化,所有权和使用权都在一个任务A上面,其它任务只能间接去访问,比如把要写入的数据发给A,由A去写入,把要读的数据要求发给A,由A去读然后返回数据等等
FreeRTOS笔记(十二)资源管理

  守护任务的实现不需要什么特性或者机制的支持,是一个协议,设置好任务的代码逻辑后就可以实现,它非常干净利落,把资源私有化后,阻塞等待其它任务的要求
  例如打印操作就是用守护任务实现的,任何想打印的任务只需要把数据以通信的方式传输给守护任务,守护任务负责数据的排队和正确逻辑,不需要任何的申请-释放

02 - 互斥机制的区别

互斥机制 本质 优点 缺点
临界区 关闭任务中断和外部中断 确保资源访问不可能被打断,资源访问过程简单 其余任务停滞、外部中断得不到响应
挂起调度器 关闭任务中断 可以响应外部中断,不能被其它任务打断,资源访问过程简单 其余任务停滞
互斥量 资源需要申请和释放 不需要关中断,资源访问过程简单 容易出现死锁和优先级反转
守护任务 资源私有化,是一个协议 不再出现以上问题缺点 资源访问过程复杂,间接访问可能带来速度和效率问题

03 - 总结

  • 资源一般需要互斥访问,因此需要互斥机制
  • FreeRTOS可以实现4种互斥机制,临界区、挂起调度器、互斥量和守护任务
  • 4种互斥机制各有优缺点,需要在不同的场合使用

本文地址:https://blog.csdn.net/Hxj_CSDN/article/details/85937911