列表和列表项是freeRTOS的基石,不管是任务的创建,任务的调度,还是队列的操作,都可以看见列表和列表项的身影。在任务的创建时,我们会把任务的状态列表项(TCB成员变量xStateListItem)添加到就绪任务列表中(其实是个列表数组,数组长度为最大优先级数,比如优先级为1的任务,都挂载到pxReadyTasksLists[1]的列表中)。当我们对任务进行延时阻塞时,我们会把当前任务的状态列表项从就绪任务列表中移除并加入到延时列表中,当某个任务的阻塞时间到时,系统会将任务的状态列表项从延时列表中移除并加入到就绪列表中。在我们操作队列入队或出队时,当因队列满或者空而不能入队或出队时,我们会把任务的事件列表项(列表项的值是任务优先级,在任务创建时就被赋值)挂载到队列的入队列表或出队列表中(成员变量xTasksWaitingToSend或xTasksWaitingToReceive),任务的状态列表项(列表项的值是阻塞的时间)挂载到延时列表中。这一系列的操作都是建立在队列和队列项的基础上,所以要想理解freeRTOS就必须先理解队列和队列项。
struct xLIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
configLIST_VOLATILE TickType_t xItemValue; /*< The value being listed. In most cases this is used to sort the list in ascending order. */
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */
void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */
struct xLIST * configLIST_VOLATILE pxContainer; /*< Pointer to the list in which this list item is placed (if any). */
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
};
typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */
其中成员变量listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE和listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE是用于检测列表项是否存在被覆盖的情况,用于检测列表项的完成性。在列表项初始化时会被赋值为一个常量(32位系统位0x5A5A5A5A),这没有什么可说的。
xItemValue:列表项的值,比如任务控制块(TCB)中状态列表项(TCB->xStateListItem)的xItemValue往往会被赋值为下车被唤醒的时间,而TCB中事件列表项的xItemValue往往被赋值为任务的优先级。
pxNext:指向下一个列表项
pxPrevious:指向前一个列表项
pxNext 和 pxPrevious用于将列表项构成双向链表的形式,比如最常用的情况,将所以就绪的任务按照优先级挂载到就绪列表中,而相同优先级的任务,就以这种双向链表的形式依次添加。所以最终的结果往往是以下形式:
pvOwner:这个指针往往用于表示当前的列表项属于哪个对象(比如指向任务的TCB,表示属于某个任务)。
pxContainer:往往表示该列表项所以哪个列表,比如当任务处于就绪态时,TCB->xStateListItem->pxContainer就指向该任务优先级的pxReadyTasksLists的列表中。
struct xMINI_LIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
configLIST_VOLATILE TickType_t xItemValue;
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
之所以用到迷你列表项,是因为列表结构体需要一个列表项成员,但又不需要列表项中的所有字段,同时在嵌入式资源有限和不影响性能的情况下,本着能省则省的原则,所以才有了迷你版列表项。
/*
* Definition of the type of queue used by the scheduler.
*/
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
volatile UBaseType_t uxNumberOfItems;
ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */
MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */
listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
} List_t;
uxNumberOfItems:表示该列表下有多少个列表项,即成员变量
pxIndex:用于遍历列表。
xListEnd:即列表的结尾,用于标记列表结束。xListEnd.xItemValue被初始化为一个常数,其值与硬件架构相关,为0xFFFF(16位架构)或者0xFFFFFFFF(32位架构)。
void vListInitialise( List_t * const pxList )
void vListInitialise( List_t * const pxList )
{
/* The list structure contains a list item which is used to mark the
* end of the list. To initialise the list the list end is inserted
* as the only list entry. */
//pxIndex用于遍历列表,在没有列表项的情况下指向列表项的末尾
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */
//用于完整性检查,给列表项pxList->xListEnd的成员listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE赋值
//16位架构赋值0x5a5a, 32位架构赋值0x5a5a5a5aUL
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( &( pxList->xListEnd ) );
/* The list end value is the highest possible value in the list to
* ensure it remains at the end of the list. */
//被初始化为一个常数,为0xFFFF(16位架构)或者0xFFFFFFFF(32位架构)。
pxList->xListEnd.xItemValue = portMAX_DELAY;
/* The list end next and previous pointers point to itself so we know
* when the list is empty. */
//在列表中无列表项成员时,xListEnd.pxNext和xListEnd.pxPrevious分别指向自己
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */
/* Initialize the remaining fields of xListEnd when it is a proper ListItem_t */
#if ( configUSE_MINI_LIST_ITEM == 0 )
{
pxList->xListEnd.pvOwner = NULL;
pxList->xListEnd.pxContainer = NULL;
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( &( pxList->xListEnd ) );
}
#endif
//无成员
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
/* Write known values into the list if
* configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
//用于完整性检查
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}
void vListInitialiseItem( ListItem_t * const pxItem )
由于列表项初始化很简单,就是将列表项的所属赋值空,完整性检测赋值。这里不赘述。
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
void vListInsertEnd( List_t * const pxList,
ListItem_t * const pxNewListItem )
{
ListItem_t * const pxIndex = pxList->pxIndex;
/* Only effective when configASSERT() is also defined, these tests may catch
* the list data structures being overwritten in memory. They will not catch
* data errors caused by incorrect configuration or use of FreeRTOS. */
//检查列表和列表项的完整性
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
/* Insert a new list item into pxList, but rather than sort the list,
* makes the new list item the last item to be removed by a call to
* listGET_OWNER_OF_NEXT_ENTRY(). */
//由于pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd )
//将插入列表项的pxNext指向pxList->xListEnd
//将插入列表项的pxPrevious指向pxList->xListEnd.pxPrevious
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
/* Only used during decision coverage testing. */
mtCOVERAGE_TEST_DELAY();
pxIndex->pxPrevious->pxNext = pxNewListItem; //将原来的倒数第二项的pxNext指向新插入列表项
pxIndex->pxPrevious = pxNewListItem; //pxList->xListEnd.pxPrevious重新指向新插入的列表项
/* Remember which list the item is in. */
pxNewListItem->pxContainer = pxList; //列表项属于列表
( pxList->uxNumberOfItems )++; //列表的成员数量+1
}
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
向尾部插入,我们很好理解,但是插入函数也并没有指定插入的位置,那么当列表中有多个成员时,我们会向哪插入呢?这就因为没给列表项都有一个列表项值xItemValue,而队列的插入会根据这个列表项的值进行升序排列。我们知道,在延时阻塞的过程中,会将任务的状态列表项添加到延时列表中,而状态列表项的值就是唤醒的时间,当列表的插入按照唤醒时刻的升序排列时,那么列表最前面的任务最先解除阻塞。我们知道在创建任务时,任务的事件列表项的xItemValue存放的是任务优先级的补数(configMAX_PRIORITIES - uxPriority),而freeRTOS的规则是优先级数字越小,优先级越低,当进行补数处理后,数值越大优先级越低。所以在队列的入队或出队阻塞中,会调用vListInsert()将当前任务插入队列的xTasksWaitingToSend或xTasksWaitingToReceive列表。这样就能更容易找到高优先级任务。
void vListInsert( List_t * const pxList,
ListItem_t * const pxNewListItem )
{
ListItem_t * pxIterator;
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
/* Only effective when configASSERT() is also defined, these tests may catch
* the list data structures being overwritten in memory. They will not catch
* data errors caused by incorrect configuration or use of FreeRTOS. */
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
/* Insert the new list item into the list, sorted in xItemValue order.
*
* If the list already contains a list item with the same item value then the
* new list item should be placed after it. This ensures that TCBs which are
* stored in ready lists (all of which have the same xItemValue value) get a
* share of the CPU. However, if the xItemValue is the same as the back marker
* the iteration loop below will not end. Therefore the value is checked
* first, and the algorithm slightly modified if necessary. */
if( xValueOfInsertion == portMAX_DELAY )
{
pxIterator = pxList->xListEnd.pxPrevious;
}
else
{
//按照xItemValue顺序查找比pxNewListItem->xItemValue大的列表项的前一项
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. *//*lint !e440 The iterator moves to a different value, not xValueOfInsertion. */
{
/* There is nothing to do here, just iterating to the wanted
* insertion position. */
}
}
pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;
/* Remember which list the item is in. This allows fast removal of the
* item later. */
pxNewListItem->pxContainer = pxList;
( pxList->uxNumberOfItems )++;
}
关于列表的插入,还是有相应的图形描述最直观,这里面贴两张别人画的图,可以更直观的了解插入过程。
根据列表项的pxContainer成员找到列表项所属的列表,然后改变列表项前级列表项和后级列表项pxPrevious和pxNext成员的指向,同时将列表成员数-1。
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/* The list item knows which list it is in. Obtain the list from the list
* item. */
List_t * const pxList = pxItemToRemove->pxContainer;
//将删除列表项的后级列表项的pxPrevious成员指向删除列表项的前级列表项
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
//将删除列表项的前级列表项的pxNext成员指向删除列表的后级列表项
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
/* Only used during decision coverage testing. */
mtCOVERAGE_TEST_DELAY();
/* Make sure the index is left pointing to a valid item. */
if( pxList->pxIndex == pxItemToRemove )
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//删除列表项的pxContainer指向空,不隶属于任何TCB
pxItemToRemove->pxContainer = NULL;
//列表成员数-1
( pxList->uxNumberOfItems )--;
return pxList->uxNumberOfItems;
}
由于列表的操作还算简单,就三个:初始化,插入、删除。但是虽然简单却构成了整个freeRTOS的基石。所以这一块还算很重要的。后面的任务、队列、信号量、互斥量我们还会遇到。