在RTOS内核开发中,处理进入阻塞状态的任务是一个重要的问题。为了支持多优先级,可以创建多个就绪链表(数组形式),每个就绪链表表示一个优先级。对于阻塞状态的任务,需要从就绪链表中移除,并在一段时间后从阻塞状态恢复。因此,需要创建一个阻塞链表来存放进入阻塞状态的任务。
另外,对于32位变量xTicksToDelay存在潜在的溢出问题。可以利用一个32位的xNextTaskUnblockTime变量来表示任务下次解除阻塞的时间。然而,由于xNextTaskUnblockTime变量随着运行时间的流逝存在溢出风险,因此需要定义一个溢出阻塞链表来存放所有下次解除阻塞的时间溢出的任务。这样就拥有两个阻塞链表,在滴答定时器中断服务函数中可以通过链表指针将这两个链表交换,以保证永远处理的是正确的阻塞链表。
阻塞链表和溢出阻塞链表的定义如下:
/* task.c */
// 阻塞链表和其指针
static List_t xDelayed_Task_List1;
static List_t volatile *pxDelayed_Task_List;
// 溢出阻塞链表和其指针
static List_t xDelayed_Task_List2;
static List_t volatile *pxOverflow_Delayed_Task_List;
在初始化函数中,需要初始化就绪链表数组以及阻塞链表和溢出阻塞链表:
/* task.c */
// 就绪列表初始化函数
void prvInitialiseTaskLists(void)
{
// 初始化延时阻塞链表
vListInitialise(&xDelayed_Task_List1);
vListInitialise(&xDelayed_Task_List2);
// 初始化指向延时阻塞链表的指针
pxDelayed_Task_List = &xDelayed_Task_List1;
pxOverflow_Delayed_Task_List = &xDelayed_Task_List2;
}
为了处理阻塞链表和溢出阻塞链表的交换,可以定义一个宏来实现链表的交换:
/* task.c */
// 延时阻塞链表和溢出延时阻塞链表交换
#define taskSWITCH_DELAYED_LISTS()\
{\
List_t volatile *pxTemp;\
pxTemp = pxDelayed_Task_List;\
pxDelayed_Task_List = pxOverflow_Delayed_Task_List;\
pxOverflow_Delayed_Task_List = pxTemp;\
xNumOfOverflows++;\
prvResetNextTaskUnblockTime();\
}
此外,需要重设xNextTaskUnblockTime变量的值,以保证在切换阻塞链表后,阻塞链表为空或不为空时,xNextTaskUnblockTime的值能正确更新。
/* task.c */
// 重设 xNextTaskUnblockTime 变量值
static void prvResetNextTaskUnblockTime(void)
{
TCB_t *pxTCB;
// 切换阻塞链表后,阻塞链表为空
if(listLIST_IS_EMPTY(pxDelayed_Task_List) != pdFALSE)
{
// 下次解除延时的时间为可能的最大值
xNextTaskUnblockTime = portMAX_DELAY;
}
else
{
// 如果阻塞链表不为空,下次解除延时的时间为链表头任务的阻塞时间
(pxTCB) = (TCB_t *)listGET_OWNER_OF_HEAD_ENTRY(pxDelayed_Task_List);
xNextTaskUnblockTime=listGET_LIST_ITEM_VALUE(&((pxTCB)->xStateListItem));
}
}
阻塞延时函数是一个重要的功能,当任务调用阻塞延时函数时,会将任务从就绪链表中删除,然后加入到阻塞链表中。具体实现如下:
/* task.c */
// 阻塞延时函数
void vTaskDelay(const TickType_t xTicksToDelay)
{
// 将当前任务加入到阻塞链表
prvAddCurrentTaskToDelayedList(xTicksToDelay);
// 任务切换
taskYIELD();
}
在滴答定时器中断服务函数中,需要处理阻塞任务的延时。具体流程如下:
/* task.c */
// 任务阻塞延时处理
BaseType_t xTaskIncrementTick(void)
{
TCB_t *pxTCB = NULL;
TickType_t xItemValue;
BaseType_t xSwitchRequired = pdFALSE;
// 更新系统时基计数器 xTickCount
const TickType_t xConstTickCount = xTickCount + 1;
xTickCount = xConstTickCount;
// 如果 xConstTickCount 溢出,则切换延时列表
if(xConstTickCount == (TickType_t)0U)
{
taskSWITCH_DELAYED_LISTS();
}
// 最近的延时任务延时到期
if(xConstTickCount >= xNextTaskUnblockTime)
{
// 依次处理延时阻塞链表中的任务
// ...
}
return xSwitchRequired;
}
以上是对RTOS内核开发中处理阻塞任务的一些关键问题和解决方案的讨论。在实际的RTOS内核开发中,需要根据具体的需求和场景进行进一步的优化和定制。
如果使用的开发环境为 Keil 且程序工作不正常,可以勾选Use MicroLIB试试。
未经允许不得转载:大白鲨游戏网 » RTOS内核开发与优化