FreeRTOS——支持时间片

所谓时间片就是同一个优先级任务下可以有多个任务,每个任务轮流的享有相同的CPU时间,享有CPU的时间我们叫做时间片。在RTOS中,最小的时间单位是一个tick,即 SysTick 的中断周期。对于FreeRTOS,时间片只能是一个tick。与其说FreeRTOS支持时间片,倒不如说他的时间片就是正常的任务调度。

一、原理分析

之 所 以 在 同 一 个 优 先 级 下 可 以 有 多 个 任 务 , 最 终 还 是 得 益 于taskRESET_READY_PRIORITY()和taskSELECT_HIGHEST_PRIORITY_TASK()这两个函函数的实现方法。接下来我们分析下这两个函数是如何在同一个优先级下有多个任务的时候起作用的。
系统在任务切换的时候总会从就绪列表中寻找优先级最高的任务来执行,寻找优先级最高的任务这个功能由taskSELECT_HIGHEST_PRIORITY_TASK()函数来实现,该函数在task.c 中定义。
taskSELECT_HIGHEST_PRIORITY_TASK()函数

#define taskSELECT_HIGHEST_PRIORITY_TASK()\
 {\
	 UBaseType_t uxTopPriority;\
	 /* 寻找就绪任务的最高优先级 */\ (1)
	 portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority );\
	 /* 获取优先级最高的就绪任务的 TCB,然后更新到 pxCurrentTCB */\ (2)
	 listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB,\
	 &( pxReadyTasksLists[ uxTopPriority ] ) );\
 }

(1) : 寻 找 就 绪 任 务 的 最 高 优 先 级 。 即 根 据 优 先 级 位 图 表
uxTopReadyPriority 找到就绪任务的最高优先级,然后将优先级暂存在 uxTopPriority。
(2): 获取优先级最高的就绪任务的 TCB,然后更新到 pxCurrentTCB。
目前我们的实验是在优先级 2 上有任务 1 和任务 2,假设任务 1 运行了一个 tick,那接下来再从对应优先级 2 的就绪列表上选择任务来运行就应该是选择任务 2?怎么选择,代码上怎么实现?奥妙就在 listGET_OWNER_OF_NEXT_ENTRY()函数中,该函数在 list.h 中定义。
listGET_OWNER_OF_NEXT_ENTRY()函数

 #define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )\
 {\
	 List_t * const pxConstList = ( pxList );\
	 /* 节点索引指向链表第一个节点调整节点索引指针,指向下一个节点,
	 如果当前链表有 N 个节点,当第 N 次调用该函数时, pxIndex 则指向第 N 个节点 */\
	 ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;\
	 /* 当遍历完链表后, pxIndex 回指到根节点 */\
	 if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) )\
	 {\
	 	( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;\
	 }\
	 /* 获取节点的 OWNER,即 TCB */\
	 ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;\
 }

listGET_OWNER_OF_NEXT_ENTRY()函数的妙处在于它并不是获取链表下的第一个节点的 OWNER,而是用于获取下一个节点的 OWNER。有下一个那么就会有上一个的说法,怎么理解?假设当前链表有 N 个节点,当第 N 次调用该函数时, pxIndex 则指向第 N个节点, 即每调用一次, 节点遍历指针 pxIndex 则会向后移动一次,用于指向下一个节点。
本实验中,优先级 2 下有两个任务,当系统第一次切换到优先级为 2 的任务(包含了任务 1 和任务 2,因为它们的优先级相同) 时, pxIndex 指向任务 1, 任务 1 得到执行。 当任务 1 执行完毕,系统重新切换到优先级为 2 的任务时, 这个时候 pxIndex 指向任务 2,任务 2 得到执行, 任务 1 和任务 2 轮流执行,享有相同的 CPU 时间, 即所谓的时间片。
本实验中,任务 1 和任务 2 的主体都是无限循环,那如果任务 1 和任务 2 都会调用将自己挂起的函数(实际运用中,任务体都不能是无限循环的,必须调用能将自己挂起的函数) ,比如 vTaskDelay()。 调用能将任务挂起的函数中,都会先将任务从就绪列表删除,然 后 将 任 务 在 优 先 级 位 图 表 uxTopReadyPriority 中 对 应 的 位 清 零 , 这 一 功 能 由taskRESET_READY_PRIORITY()函数来实现, 该函数在 task.c 中定义。
taskRESET_READY_PRIORITY()函数

 #define taskRESET_READY_PRIORITY( uxPriority )\
 {\
	 if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ ( uxPriority ) ] ) )\
	 == ( UBaseType_t ) 0 )\
	 {\
		 portRESET_READY_PRIORITY( ( uxPriority ),\
		 ( uxTopReadyPriority ) );\
	 }\
 }

taskRESET_READY_PRIORITY() 函 数 的 妙 处 在 于 清 除 优 先 级 位 图 表uxTopReadyPriority 中相应的位时候,会先判断当前优先级链表下是否还有其它任务,如果有则不清零。 假设当前实验中,任务 1 会调用 vTaskDelay(),会将自己挂起,只能是将任务 1 从就绪列表删除,不能将任务 1 在优先级位图表 uxTopReadyPriority 中对应的位清 0,因为该优先级下还有任务 2,否则任务 2 将得不到执行。

参考:[野火®]《FreeRTOS 内核实现与应用开发实战—基于STM32》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章