一、概述
FreeRtos的每一个任务都有一个对应的优先级,较高优先级的任务具有优先运行的权利,当高优先级的任务调用系统的延时函数或者需要阻塞等待某些信号时,会让出CPU的使用权,从而使低优先级的任务得以运行。
FreeRtos支持抢占式调度,当低优先级的任务正在运行时,如果有高优先级的任务处于就绪状态,系统优先运行高优先级的任务。
如果两个或者多个任务具有相同的优先级,这些任务会轮换运行,并且它们会占用相同的时间片。
FreeRtos有两种切换任务的方式,一种是让内核自己按照约定好的规则进行切换,一种是调用portYIELD函数主动进行任务切换。
二、FreeRtos任务管理
在了解任务切换之前,我们需要先了解FreeRtos是如何进行任务管理的。
FreeRtos的任务共有5种状态:
- 运行态,表示任务正在运行;
- 阻塞态,任务调用了vTaskDelay或者其它阻塞API;
- 就绪态,任务处于等待运行的状态;
- 挂起态,任务被挂起,调用vTaskSuspend函数;
- 删除态,任务被删除;
#define tskRUNNING_CHAR ( 'X' )
#define tskBLOCKED_CHAR ( 'B' )
#define tskREADY_CHAR ( 'R' )
#define tskDELETED_CHAR ( 'D' )
#define tskSUSPENDED_CHAR ( 'S' )
FreeRtos使用四个列表对阻塞态、就绪态、挂起态任务进行管理,使用pxCurrentTCB对运行态任务进行管理(系统运行时,同一时刻只能有一个任务处于运行状态):
PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL;
PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];
PRIVILEGED_DATA static List_t xDelayedTaskList1;
PRIVILEGED_DATA static List_t xDelayedTaskList2;
PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList;
PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList;
PRIVILEGED_DATA static List_t xPendingReadyList;
任务在创建的时候,系统会将所有任务按照优先级的顺序放入pxReadyTasksLists列表中,换句话说,在任务调度器启动前,所有的任务都处于就绪状态,并且pxCurrentTCB始终指向优先级最高的任务;
当任务调度器启动后,如果某个任务调用了诸如vTaskDelay的系统延时函数或者其它会造成阻塞的API函数,系统会将对应的任务添加到pxDelayedTaskList列表中,此时任务就处于阻塞状态;
当任务调度器启动之后,如果调用了vTaskSuspend函数挂起了某个任务,系统会将其添加到挂起态任务列表xPendingReadyList,此时任务进入挂起状态,直到调用vTaskResume函数接触阻塞。
pxDelayedTaskList和pxOverflowDelayedTaskList
相信很多人都会有这样的疑问:为什么要使用两个列表对阻塞态任务进行管理?
要想了解这一点,我们首先需要了解一个全局变量:xTickCount
PRIVILEGED_DATA static volatile TickType_t xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;
在32位系统中TickType_t是一个32位无符号的整型数据,它所能表示的最大值是0xFFFFFFFF,当系统运行满0xFFFFFFFF个时钟节拍后就会产生溢出。
FreeRtos通过调用prvAddCurrentTaskToDelayedList函数将任务添加到延时(阻塞)任务列表,在该函数中系统同样使用一个32位的无符号整数记录任务唤醒的时刻,所以随着系统的不断运行,也会产生溢出,所以需要两个链表来进行记录,当系统计时器溢出时,只需要切换延时任务列表就可以了,实际代码如下:
xTimeToWake = xConstTickCount + xTicksToWait;
listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );
if( xTimeToWake < xConstTickCount )
{
vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
}
else
{
vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
if( xTimeToWake < xNextTaskUnblockTime )
{
xNextTaskUnblockTime = xTimeToWake;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
三、FreeRtos的任务状态转换
FreeRtos的任务在创建之初都会被放到就绪列表中,即处于就绪态。当任务调度器启动之后,处于最高优先级的任务就会被运行,处于运行态。运行态的任务可以通过调用vTaskDelay或者具有阻塞功能的其它API(如xQueueReceive等函数)将任务从运行态变为阻塞态,当阻塞时间到该任务会从阻塞态重新回到就绪态。运行态的任务还可以通过vTaskSuspend函数从运行态变为挂起状态,挂起态的任务只能手动调用vTaskResume函数主动恢复成就绪状态。
处于就绪态的任务还可以通过portYIELD函数主动进行任务切换,使其进入运行状态。
FreeRtos单个任务转换状态图:
FreeRtos带中文注释源码Gitee地址:https://gitee.com/zBlackShadow/FreeRtos10.4.3.git
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)