当前位置: 移动技术网 > 科技>办公>打印机 > FreeRTOS笔记(十二)资源管理

FreeRTOS笔记(十二)资源管理

2020年07月01日  | 移动技术网科技  | 我要评论
文章目录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,但是却执行了
Alt

01 - 互斥机制

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

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

Alt

  经过以上内容,可以了解到其实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,意思是开启所有中断,所以临界区就是提供一种最直接粗暴的方式去访问资源。
Alt

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不再计数,表达系统心跳暂时停止,于是调度器也不会进行任务切换,可以回顾任务切换
Alt

1.3 - 互斥量

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

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

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

1.4 - 守护任务

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

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

02 - 互斥机制的区别

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

03 - 总结

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

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

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网