前面在调度器启动之前先创建了两个任务,一个启动任务一个空闲任务,然后调度器会跳到启动任务去执行。在分析任务创建的源码之前,需要先分析一下列表和列表项,创建任务实际上就是初始化任务的各个成员变量(即初始化任务控制块的成员),在初始化完后这个任务就会进入到就绪状态,等待调度器来执行它,其中任务控制块就有一项用于描述任务当前的状态,这个状态就是列表项,存放列表项的容器就是列表(链表结构),前面提到就绪状态,那这个任务的列表项就是放到就绪列表中,列表不止有就绪列表,因为任务可以有很多种状态,包括就绪列表、延时列表、溢出延时列表、挂起列表、挂起就绪列表、等待删除任务列表。调度器依据这个状态列表项就可以得知任务当前的状态,是不是要执行这个任务了,如果是将要执行的任务,就会将这个状态列表项插入到就绪列表中,调度器会依据就绪列表中的列表项找出需要执行的任务。而其它列表的任务就继续等待,等待合适的时机列表项被换到就绪列表中。关于任务创建的具体过程和任务调度器依据列表来切换任务的具体过程在后面章节再做分析,这章主要分析列表初始化和列表项插入列表等有关列表和列表项的过程。说了这么多,记住列表就是链表,而列表项就是存在于列表中的项目即可。
列表和列表项的源码在FreeRTOS源码主目录下的list.c文件中,有以下这些函数接口
void vListInitialise( List_t * const pxList )
void vListInitialiseItem( ListItem_t * const pxItem )
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
其中相关的三个结构体变量定义如下:
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE /* 用于检查列表完整性 */
volatile UBaseType_t uxNumberOfItems; /* 记录这个列表中列表项的数量 */
ListItem_t * configLIST_VOLATILE pxIndex; /* 列表项索引 */
MiniListItem_t xListEnd; /* 列表结束标识,使用迷你列表项来标识 */
listSECOND_LIST_INTEGRITY_CHECK_VALUE /* 用于检查列表完整性 */
} List_t;
struct xLIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /* 用于检查列表项完整性 */
configLIST_VOLATILE TickType_t xItemValue; /* 列表项值用于决定插入的位置 */
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /* 指向下一个列表项 */
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /* 指向前一个列表项 */
void * pvOwner; /* 属于哪个任务控制块 */
struct xLIST * configLIST_VOLATILE pxContainer; /* 属于哪个列表 */
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /* 用于检查列表项完整性 */
};
typedef struct xLIST_ITEM ListItem_t;
上面列表还提到了迷你列表项,迷你列表项是列表项的缩写版,有时不需要那么全的功能,使用迷你列表项的话能节省内存空间,上面列表的结束就使用的是迷你列表项来表示,定义如下:
struct xMINI_LIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /* 用于检查列表项完整性 */
configLIST_VOLATILE TickType_t xItemValue; /* 迷你列表项值用于决定插入的位置 */
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /* 指向下一个列表项 */
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /* 指向前一个列表项 */
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
下面就分析一下列表和列表项是如何初始化,如何将列表项插入列表中,以及怎么从列表项中删除列表的。
列表初始化
void vListInitialise( List_t * const pxList )
{
/* 索引值指向末尾(初始化时列表中只有xListEnd这一个列表项) */
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
/* 初始化列表结束项的列表项值,默认数值为最大 */
pxList->xListEnd.xItemValue = portMAX_DELAY;
/* 列表的结束列表项指向自己,从而可知当前列表为空 */
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
/* 当前列表的列表项为空(不包括结束项) */
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
/* 完整性检查,实际上就是给列表中用于完整性检查的变量赋值 */
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}
图示如下:
列表项初始化
void vListInitialiseItem( ListItem_t * const pxItem )
{
/* 初始化该列表项属于哪个列表,这里为NULL,其它成员变量在后面根据情况来初始化 */
pxItem->pxContainer = NULL;
/* 完整性检查,实际上就是给列表项中用于完整性检查的变量赋值 */
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
往列表中插入列表项
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t *pxIterator;
/* 获取要插入的列表项值 */
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
/* 完整性检查,检查完整性变量初始化后是否被改变,需要实现函数configASSERT() */
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
if( xValueOfInsertion == portMAX_DELAY )
{
/* 判断要插入的位置,如果是portMAX_DELAY即为最大值,那就直接插入到列表末尾 */
pxIterator = pxList->xListEnd.pxPrevious;
}
else
{
/* 否则根据要插入列表项的xItemValue找到插入位置,一个循环从链表中一直往下找到合适的位置 */
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext )
{
}
}
/* 将原先位置的后一项放到新项的后面 */
pxNewListItem->pxNext = pxIterator->pxNext;
/* 将原先位置后一项的前一项改为新项 */
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
/* 将新项的前一项指向原先位置 */
pxNewListItem->pxPrevious = pxIterator;
/* 将原先位置后一项指向新项 */
pxIterator->pxNext = pxNewListItem;
/* 初始化新项属于要插入的列表 */
pxNewListItem->pxContainer = pxList;
/* 列表的列表项数量递增 */
( pxList->uxNumberOfItems )++;
}
往列表末尾插入列表项
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
/* 列表的pxIndex一般都是指向最后一项,由于列表是环形链表结构,所有第一项和最后一项都是同一项 */
ListItem_t * const pxIndex = pxList->pxIndex;
/* 完整性检查,检查完整性变量初始化后是否被改变,需要实现函数configASSERT() */
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
/* 插入到列表末尾 */
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
/* 修改原来末尾项*/
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
/* 初始化新列表项所属的列表*/
pxNewListItem->pxContainer = pxList;
/* 此项列表的列表项递增 */
( pxList->uxNumberOfItems )++;
}
图示如下:
从列表中删除列表项
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/* 获取要删除的列表项的列表 */
List_t * const pxList = pxItemToRemove->pxContainer;
/* 修改列表项前后项的指针指向 */
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
if( pxList->pxIndex == pxItemToRemove )
{
/* 如果该列表的索引刚好在删除列表项的位置则把位置改为前面那项 */
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 由于已经删除了,所以该列表项不属于任务列表了 */
pxItemToRemove->pxContainer = NULL;
/* 列表的列表项数量递减 */
( pxList->uxNumberOfItems )--;
/* 返回该列表的列表项数量 */
return pxList->uxNumberOfItems;
}