FreeRTOS内核全局变量

想要分析FreeRTOS源码,想要理解FreeRTOS源码的整个宏观架构,有一个前提就是必须知道FreeRTOS内核中那些全局变量的意义,每个全局变量都是用来干什么的。只有了解了这些全局变量我们才能从宏观上看清FreeRTOS的真面目,学习其金华,领略其巧妙。

PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL;					//指向当前运行任务的任务控制块
PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];//优先级就绪列表数组,每个优先级下都有一条列表 

pxReadyTasksLists 是一个列表类型的数组,每个元素都是一条列表,所以最大优先级不要设置的太大,越大越耗费内存。FreeRTOS 中每个优先级下都有一条就绪列表,同优先级的任务都挂到此列表上。

PRIVILEGED_DATA static List_t xDelayedTaskList1;						//延时列表1
PRIVILEGED_DATA static List_t xDelayedTaskList2;						//延时列表2
PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList;				//指向延时列表的指针
PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList;		//指向延时溢出列表的指针

需要阻塞的任务都挂载到延时列表上。
FreeRTOS内核时钟节拍xTickCountTickType_t 类型的变量,xTickCount存在溢出的情况,溢出怎么办呢?用两条列表交替使用,巧妙的解决了时钟节拍溢出的问题,实现原理在下一篇文章详细解析。

PRIVILEGED_DATA static List_t xPendingReadyList;						//调度器挂起期间,就绪的任务暂时挂载到此列表上

调度器挂起期间,就绪的任务暂时挂载到此列表上。因为调度器挂起期间不允许任务切换,不允许任务切换就不能挂载到就绪列表上(如果挂载到就绪列表上会造成任务切换),所以需要暂时将就绪的任务挂载到此列表上,待任务调度器恢复以后,将此列表中的任务移到就绪列表中。

#if( INCLUDE_vTaskDelete == 1 )
	PRIVILEGED_DATA static List_t xTasksWaitingTermination;				//任务删除自己以后将自己挂在此列表上,在空闲任务中释放内存
	PRIVILEGED_DATA static volatile UBaseType_t uxDeletedTasksWaitingCleanUp = ( UBaseType_t ) 0U;//任务删除自己的个数
#endif
#if ( INCLUDE_vTaskSuspend == 1 )
	PRIVILEGED_DATA static List_t xSuspendedTaskList;					//任务挂起列表
#endif
PRIVILEGED_DATA static volatile UBaseType_t uxCurrentNumberOfTasks 	= ( UBaseType_t ) 0U;//系统中当前任务的数量
PRIVILEGED_DATA static volatile TickType_t xTickCount 				= ( TickType_t ) 0U; //时钟节拍数
PRIVILEGED_DATA static volatile UBaseType_t uxTopReadyPriority 		= tskIDLE_PRIORITY;  
PRIVILEGED_DATA static volatile BaseType_t xSchedulerRunning 		= pdFALSE;			 //任务调度器挂起还是运行  pdTRUE表示运行  pdFALSE表示挂起 
PRIVILEGED_DATA static volatile UBaseType_t uxPendedTicks 			= ( UBaseType_t ) 0U;//任务调度器在挂起期间时钟节拍的个数
PRIVILEGED_DATA static volatile BaseType_t xYieldPending 			= pdFALSE;			 //是否需要进行一次任务切换标志位
PRIVILEGED_DATA static volatile BaseType_t xNumOfOverflows 			= ( BaseType_t ) 0;	 //记录时钟节拍计数器溢出了多少次
PRIVILEGED_DATA static UBaseType_t uxTaskNumber 					= ( UBaseType_t ) 0U;
PRIVILEGED_DATA static volatile TickType_t xNextTaskUnblockTime		= ( TickType_t ) 0U; //保存距离最近一个解锁任务的解锁时间
PRIVILEGED_DATA static TaskHandle_t xIdleTaskHandle					= NULL;				 //空闲任务句柄

PRIVILEGED_DATA static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY; 特别说明一下uxTopReadyPriority
CPU在查找最高优先级时有两种方法,一种是硬件方法,一种是软件方法。
硬件方法:依赖CPU,CPU必须支持CLZ指令。uxTopReadyPriority的每个bit都代表一个优先级,如果某个bit为1表示对应优先级下有就绪任务。
软件方法:不依赖CPU,任何CPU都可以使用。uxTopReadyPriority代表的是就绪任务中的最高优先级。

既然说到最高优先级查找的问题了,我们干脆详细的剖析一下吧。
我们都知道任务的切换是在PendSV中进行的,PendSV中断服务函数中调用了vTaskSwitchContextvTaskSwitchContext又调用了taskSELECT_HIGHEST_PRIORITY_TASKtaskSELECT_HIGHEST_PRIORITY_TASK的作用是找到就绪任务中优先级最高的任务,让pxCurrentTCB指向该任务的任务控制块。

硬件方法:
taskSELECT_HIGHEST_PRIORITY_TASK是一个宏,源码如下

#define taskSELECT_HIGHEST_PRIORITY_TASK()														\
{																								\
UBaseType_t uxTopPriority;																		\
	portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority );								\
	configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0 );		\
	listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );		\
} /* taskSELECT_HIGHEST_PRIORITY_TASK() */

portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority );的功能是从uxTopReadyPriority中找到最高优先级,将优先级赋值给uxTopPriority
实现方法如下:

#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31UL - ( uint32_t ) __clz( ( uxReadyPriorities ) ) )

举个例子:现在只有优先级2和优先级5下有任务就绪,那么变量uxTopReadyPriority的二进制是这样的
uxTopReadyPriority = 00000000 00000000 00000000 00100100
CLZ指令是计算前导0指令。返回第一个1前边有多少个0 。
__clz( ( uxReadyPriorities ) ) 的结果就是26, 31 - 26 = 5。uxTopPriority 就是5了。这样就算出了最高优先级是5。

再来看listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );的作用是从就绪列表中找到最高优先级任务的TCB,将其赋值给pxCurrentTCB
实现方法如下:

#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )										\
{																							\
List_t * const pxConstList = ( pxList );													\
	/* Increment the index to the next item and return the item, ensuring */				\
	/* we don't return the marker used at the end of the list.  */							\
	/*列表指针向后遍历一个节点 */																								\
	( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;							\
	/*如果列表指针指向了列表的迷你列表项,就再向后遍历一个节点  (别忘记列表是双向环形的)*/\
	if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) )	\
	{																						\
		( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;						\
	}																						\
	/*pvOwner成员中保存的是TCB的地址*/			\
	( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;											\
}

到此硬件实现方法讲完了,可能有的大兄弟有个疑问uxTopReadyPriority 变量是如何维护的。
如果有任务就绪调用的是下面这个宏来 置位优先级bit

#define taskRECORD_READY_PRIORITY( uxPriority )	portRECORD_READY_PRIORITY( uxPriority, uxTopReadyPriority )
#define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) )

如果某个优先级下没有就绪任务调用的是下面这个宏复位优先级bit

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

对应优先级的就绪列表中没有就绪任务就复位优先级,宏portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) ); 的内部实现如下:

#define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) )

最后:FreeRTOS是一个很优秀的RTOS,源码中有很多写法很巧妙,值得我们去学习。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值