FreeRTOS列表项及链表分析
结构体分析
xLIST_ITEM结构体
xLIST_ITEM结构体代码如下所示,
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. */
TickType_t xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending 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. */
void * configLIST_VOLATILE pvContainer; /*< 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. */
TickType_t 是类型uint32_t或uint16_t,由portmacro.h中的configUSE_16_BIT_TICKS定义,xItemValue表示xLIST_ITEM的值,目的一般是用来排序。pxNext与pxPrevious两个则是指向前级xLIST_ITEM节点及后级xLIST_ITEM节点的指针。configLIST_VOLATILE看作是volatile关键字即可,pvOwner指针变量,指向拥有该节点的内核对象,一般是TCB。pvContainer是一个指针,主要指向节点所在的链表。结构框图由下图所示。
xMINI_LIST_ITEM结构体
xMINI_LIST_ITEM结构体代码如下所示,
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;
};
xItemValue用来标识xMINI_LIST_ITEM结构体的值,TickType_t 是类型uint32_t或uint16_t,由portmacro.h中的configUSE_16_BIT_TICKS定义,xItemValue一般用作排序,pxNext和pxPrevious为xLIST_ITEM指针变量,分别指向下一个xLIST_ITEM节点和上一个xLIST_ITEM节点。结构框图由下图所示。
xLIST结构体
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
configLIST_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;
UBaseType_t为无符号长整形数据类型,参数uxNumberOfItems链表节点计数器,用来表示该链表下有多少个链表节点的,pxIndex节点指针变量,用于遍历链表的所有节点。xListEnd是一个MiniListItem_t节点,一般表示链表的最后一个节点。因为FreeRTOS定义的链表是循环双向链表,因此XListEnd也是链表的第一节点。其结构示意图如下所示。
相关函数
节点初始化函数
节点初始化函数代码void vListInitialiseItem( ListItem_t * const pxItem )
void vListInitialiseItem( ListItem_t * const pxItem )
{
/* Make sure the list item is not recorded as being on a list. */
pxItem->pvContainer = NULL;
/* Write known values into the list item if
configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
/*-----------------------------------------------------------*/
代码将传入的节点的pvContainer设置为NULL,表示该节点不属于任何链表。初始化后节点状态如下所示。
链表初始化函数
链表初始化函数代码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. */
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
/* The list end value is the highest possible value in the list to
ensure it remains at the end of the list. */
pxList->xListEnd.xItemValue = portMAX_DELAY;
/* The list end next and previous pointers point to itself so we know
when the list is empty. */
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 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 The mini list structure is used as the list end to save RAM. This is checked and valid. */
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 );
}
初始化时让pxIndex指向其结构体系中的链表尾项节点(ListEnd),并且让列表结尾项中的xItemValue的值设为portMAX_DELAY。让列表结尾项(ListEnd)中的pxNext及pxPrevious指向自身的节点结尾项(ListEnd)。接着设置uxNumberOfItems的值为0,uxNumberOfItems记录本链表种普通节点(ListItem_t节点)的总数量。其示意图可以如下所示。
链表插入普通节点函数
链表插入节点函数代码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(). */
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
/* Only used during decision coverage testing. */
mtCOVERAGE_TEST_DELAY();
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
/* Remember which list the item is in. */
pxNewListItem->pvContainer = ( void * ) pxList;
( pxList->uxNumberOfItems )++;
}
传入一个链表及一个节点。首先申明了一个节点指针pxIndex,与链表的索引节点指针指向的是同一个位置,即链表的尾节点xListEnd,接着让新节点的后节点指针指向pxIndex所指向的指针,前节点指向pxIndex前节点。pxIndex的前节点的下一个节点指向插入的新节点,pxIndex的前一个节点为新节点,让新节点的容器指向当前链表。完成一次新节点插入后图示如下。
插入第二个节点的示意图如下所示。
链表删除普通节点函数
链表删除普通节点函数UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/* The list item knows which list it is in. Obtain the list from the listitem. */
List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
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();
}
pxItemToRemove->pvContainer = NULL;
( pxList->uxNumberOfItems )--;
return pxList->uxNumberOfItems;
}
传入所要删除的节点,返回删除节点后链表中节点的数量。首先定义了一个临时的链表头指针pxList,其指向所要删除节点所在的链表。接着把所删除节点的后节点的前指针指向所删除节点的前节点,所删除节点的前节点的后指针。接着判断链表的索引是否指向了所删除的节点,如果是则将链表节点指向所删除节点的前一个节点。接着将所需要删除节点的容器设为空,表示该节点不属于任何一个链表,接着链表链表中标示节点数量的值减1,之后返回该链表所剩的节点数。
普通节点按升序排序插入链表
普通节点按升序排序插入链表函数
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 TCB's 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
{
/* *** NOTE ***********************************************************
If you find your application is crashing here then likely causes are
listed below. In addition see http://www.freertos.org/FAQHelp.html for
more tips, and ensure configASSERT() is defined!
http://www.freertos.org/a00110.html#configASSERT
1) Stack overflow -
see http://www.freertos.org/Stacks-and-stack-overflow-checking.html
2) Incorrect interrupt priority assignment, especially on Cortex-M
parts where numerically high priority values denote low actual
interrupt priorities, which can seem counter intuitive. See
http://www.freertos.org/RTOS-Cortex-M3-M4.html and the definition
of configMAX_SYSCALL_INTERRUPT_PRIORITY on
http://www.freertos.org/a00110.html
3) Calling an API function from within a critical section or when
the scheduler is suspended, or calling an API function that does
not end in "FromISR" from an interrupt.
4) Using a queue or semaphore before it has been initialised or
before the scheduler has been started (are interrupts firing
before vTaskStartScheduler() has been called?).
**********************************************************************/
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
{
/* 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->pvContainer = ( void * ) pxList;
( pxList->uxNumberOfItems )++;
}
传入链表及所插入的节点。首先声明一个指向节点的指针,大概为pxIterator,其次声明一个变量ValueOfInsertion用于存储当前节点的xItemValue值。如果插入的列表项的值为最大值,那么就让pxIterator的值指向链表最后mini节点之前的节点。否则就找到比插入节点的xItemValue值大的节点的之前的节点,并且把地址给pxIterator。接着就是常规的插入操作,比较易懂。