Based On FreeRTOS Kernel V10.3.1
1、相关文件
链表结构是 OS 内部经常使用到的,FreeRTOS 自然也不例外,在深入分析各个模块的工作原理之前,首先来分析 FreeRTOS 的链表结构,和链表相关的代码被定义在:
list.h
list.c
2、数据结构
不得不说,FreeRTOS 另一个成功的因素,在于他的代码注释,非常的完备,有的时候,代码、结构等的定义,和具体的场景相关性很强,也就是说,没有分析到更后面的使用场景,那么可能便很难理解当前看到的结构定义;
FreeRTOS 这一点做得非常的棒,它有很多很棒的注释,能够帮助大家在初期能够把握一些全局性的东西;
FreeRTOS 使用双向链表来描述链表结构,FreeRTOS 中定义了 3 个相关的结构:
ListItem_t:用来表示链表中的一个元素;
MiniListItem_t:用来表示链表中初始的那个元素;
List_t:用来表示一个链表;
2.1、ListItem_t
ListItem_t 用于描述链表中的一个元素,它的定义为:
-
/*
-
* Definition of the only type of object that a list can contain.
-
*/
-
struct
xLIST;
-
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 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. */
-
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;
结构体中的各个成员的描述为:
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE :
这个是新版本加上的,用于链表是否有效的判断,当定义了 configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 这个为 1 的时候,链表的这个成员便有效,否则是空定义;初始化的时候,这个占位的 Tag 被设置成为固定的 0x5a5a5a5aUL,作用是,在使用链表的时候,判断这个成员是否可能被踩;
TickType_t xItemValue:
这个成员用于排序,看得出来,被定义成为了 TickType_t 类型,也就是按照时间的值来排序;
struct xLIST_ITEM * configLIST_VOLATILE pxNext:
指向下一个成员的指针;
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious:
指向上一个成员的指针;
void * pvOwner:
指向拥有这个 Item 成员的结构体,通常是描述进程 TCB 的指针;
struct xLIST * configLIST_VOLATILE pxContainer:
指向这个 Item 所在的链表的指针;
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE:
含义和第一个成员一样
2.2、MiniListItem_t
从名字就看得出来,是一个迷你型的 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;
-
};
-
typedef
struct
xMINI_LIST_ITEM MiniListItem_t;
看得出来,它和 ListItem_t 的定义非常类似,关键成员少了 pvOwner、pxContainer;为什么少定义这两个呢?因为这个成员根本用不到这两个,后面我们在谈原因;
2.3、List_t
主角登场,List_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;
可以看到,FreeRTOS 对它的注释是:
Definition of the type of queue used by the scheduler
被调度器使用的队列,也就是说进程调度要用到它;
结构体一前一后两个定义不再多说,和前面的一样,为了检测结构是否被踩的可能性;
volatile UBaseType_t uxNumberOfItems:
定义了当前这个链表中有多少个 Item ,增加一个链表元素,这个值加1,反之,减1;
ListItem_t * configLIST_VOLATILE pxIndex:
用于链表遍历的节点,怎么个遍历法,后面马上献上;
MiniListItem_t xListEnd:
用于链表的最后的元素,相当于一个标记;
3、函数
既然数据结构介绍完毕(虽然看上去比较抽象,包括几个不容易理解的数据结构),那么接下来一边分析函数,一边分析数据结构的使用方式;
3.1、vListInitialise
一个链表的初始化函数为:
-
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 !e9087 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 !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. */
-
-
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 );
-
}
传入一个表征链表的结构体指针 List_t * const pxList
请注意一点,在 List_t 结构中,用于标记链表最后的 xListEnd 结构是一个定义,而不是指针,这里首先将传入链表的
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); // MiniListItem_t 结构强转
强转为 ListItem_t 结构,并赋值给了 pxIndex,也就是给 pxIndex 内容为这个 List 的 xListEnd 的地址;
接下来便将 xListEnd 的 xItemValue 写入最大值 0xFFFFFFFF(32位CPU);
然后便将 xListEnd 的 next 和 prev 指针全部指向它自己,已达到初始化的目的;
最后初始化该链表中有效元素的个数为 0 个,即 uxNumberOfItems = ( UBaseType_t ) 0U;
最后是,如果使能了 Check 链表有效性的那个宏,那么这里给链表结构的前后两个 TAG 位赋值成为固定的 0x5a5a5a5aUL;
初始化过程为:
3.2、vListInitialiseItem
初始化一个链表元素:
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->pxContainer =
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 );
-
}
仅仅是将元素的容器指针给赋值成为 NULL;
3.3、vListInsertEnd
这个 API 是往指定的链表的后部插入一个 Item:
-
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->pxContainer = pxList;
-
-
( pxList->uxNumberOfItems )++;
-
}
入参 pxList 是指定的链表指针,pxNewListItem 是待插入的 Item;
前面两行不多说了,配置用于检查链表和 Item 被踩的 Tag;
首先获取链表的 pxIndex 结构指针,此指针在链表初始化的时候,是指向了 xListEnd;
所以对于一个新的链表创建后,依次插入两个元素 NewItem_1 和 NewItem_2 的过程如下所示:
可以看到,pxIndex 始终处于最开始的那个 xListEnd 结构,而 xListEnd 结构在 List 中有定义成为只有 next 和 prev 的结构,这就凸显出 FreeRTOS 在设计的时候,一点点空间都在节约的精益求精的准则;
3.4、vListInsert
这个 API 和前一个不同的是,前一个是插入到尾部,这个 API 按照 Item 中的 xItemValue 排序插入,xItemValue 越大,越靠近 xListEnd:
-
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
-
{
-
/* *** NOTE ***********************************************************
-
If you find your application is crashing here then likely causes are
-
listed below. In addition see https://www.freertos.org/FAQHelp.html for
-
more tips, and ensure configASSERT() is defined!
-
https://www.freertos.org/a00110.html#configASSERT
-
-
1) Stack overflow -
-
see https://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
-
https://www.freertos.org/RTOS-Cortex-M3-M4.html and the definition
-
of configMAX_SYSCALL_INTERRUPT_PRIORITY on
-
https://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 !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 )++;
-
}
如果 xItemValue 为 portMAX_DELAY(32bit CPU 中,这个值是 0xFFFFFFFF),那么直接插入到 xListEnd 右边:
否则,按照从 xListEnd 开始,从最右边遍历,从最右往左,依次排列 xItemValue 最小的,比如;
3.5、uxListRemove
该接口用于将链表中的特定元素摘除:
-
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;
-
-
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->pxContainer =
NULL;
-
( pxList->uxNumberOfItems )--;
-
-
return pxList->uxNumberOfItems;
-
}
首先从指定元素中的 pxContainer 获取到该元素所属的链表结构;再将元素从链表中摘除;
4、宏
除了上面几个常用到的 API,在 list.h 中还以宏的方式提供了一些常用的宏以及宏函数,下面我们依次看下:
4.1、listSET_LIST_ITEM_OWNER、listGET_LIST_ITEM_OWNER
listSET_LIST_ITEM_OWNER 用于设置一个 Item 的 Owner:
-
/*
-
* Access macro to set the owner of a list item. The owner of a list item
-
* is the object (usually a TCB) that contains the list item.
-
*
-
* \page listSET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER
-
* \ingroup LinkedList
-
*/
-
#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) )
listGET_LIST_ITEM_OWNER 用于获取一个 Item 的 Owner:
-
/*
-
* Access macro to get the owner of a list item. The owner of a list item
-
* is the object (usually a TCB) that contains the list item.
-
*
-
* \page listGET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER
-
* \ingroup LinkedList
-
*/
-
#define listGET_LIST_ITEM_OWNER( pxListItem ) ( ( pxListItem )->pvOwner )
4.2、listSET_LIST_ITEM_VALUE、listGET_LIST_ITEM_VALUE
listSET_LIST_ITEM_VALUE 用于设置 Item 的 xItemValue 值:
-
/*
-
* Access macro to set the value of the list item. In most cases the value is
-
* used to sort the list in descending order.
-
*
-
* \page listSET_LIST_ITEM_VALUE listSET_LIST_ITEM_VALUE
-
* \ingroup LinkedList
-
*/
-
#define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( ( pxListItem )->xItemValue = ( xValue ) )
listGET_LIST_ITEM_VALUE 用于获取 Item 的 xItemValue 值:
-
/*
-
* Access macro to retrieve the value of the list item. The value can
-
* represent anything - for example the priority of a task, or the time at
-
* which a task should be unblocked.
-
*
-
* \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE
-
* \ingroup LinkedList
-
*/
-
#define listGET_LIST_ITEM_VALUE( pxListItem ) ( ( pxListItem )->xItemValue )
4.3、listGET_ITEM_VALUE_OF_HEAD_ENTRY
listGET_ITEM_VALUE_OF_HEAD_ENTRY 用于获取指定链表的 Entry 的 xItemValue,这里我们需要知道 Entry 的定义,其实就是 xListEnd->pxNext 的那个元素:
-
/*
-
* Access macro to retrieve the value of the list item at the head of a given
-
* list.
-
*
-
* \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE
-
* \ingroup LinkedList
-
*/
-
#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext->xItemValue )
4.4、listGET_HEAD_ENTRY
listGET_HEAD_ENTRY 用于获取指定链表的 Entry 的 Item 指针:
-
/*
-
* Return the list item at the head of the list.
-
*
-
* \page listGET_HEAD_ENTRY listGET_HEAD_ENTRY
-
* \ingroup LinkedList
-
*/
-
#define listGET_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext )
4.5、listGET_NEXT
listGET_NEXT 用户获取传入 Item 的 next 指针:
-
/*
-
* Return the next list item.
-
*
-
* \page listGET_NEXT listGET_NEXT
-
* \ingroup LinkedList
-
*/
-
#define listGET_NEXT( pxListItem ) ( ( pxListItem )->pxNext )
4.6、listGET_END_MARKER
listGET_END_MARKER 用来获取指定链表的 xListEnd 标记位置:
-
/*
-
* Return the list item that marks the end of the list
-
*
-
* \page listGET_END_MARKER listGET_END_MARKER
-
* \ingroup LinkedList
-
*/
-
#define listGET_END_MARKER( pxList ) ( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) )
4.6、listLIST_IS_EMPTY
listLIST_IS_EMPTY 用来获取指定链表中是否有 Item,主要是通过查看链表的数据统计的变量来获取:
-
/*
-
* Access macro to determine if a list contains any items. The macro will
-
* only have the value true if the list is empty.
-
*
-
* \page listLIST_IS_EMPTY listLIST_IS_EMPTY
-
* \ingroup LinkedList
-
*/
-
#define listLIST_IS_EMPTY( pxList ) ( ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ? pdTRUE : pdFALSE )
4.7、listCURRENT_LIST_LENGTH
listCURRENT_LIST_LENGTH 获取链表元素的个数:
-
/*
-
* Access macro to return the number of items in the list.
-
*/
-
#define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems )
4.8、listGET_OWNER_OF_NEXT_ENTRY
listGET_OWNER_OF_NEXT_ENTRY 用于获取指定链表的下一个 TCB 结构:
-
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \
-
{ \
-
List_t * const pxConstList = ( pxList ); \
-
/* Increment the index to the next item and return the item, ensuring */ \
-
/* we don't return the marker used at the end of the list. */ \
-
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
-
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \
-
{ \
-
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
-
} \
-
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \
-
}
4.9、listGET_OWNER_OF_HEAD_ENTRY
listGET_OWNER_OF_HEAD_ENTRY 用于获取 Entry 节点的 Owner:
-
/*
-
* Access function to obtain the owner of the first entry in a list. Lists
-
* are normally sorted in ascending item value order.
-
*
-
* This function returns the pxOwner member of the first item in the list.
-
* The pxOwner parameter of a list item is a pointer to the object that owns
-
* the list item. In the scheduler this is normally a task control block.
-
* The pxOwner parameter effectively creates a two way link between the list
-
* item and its owner.
-
*
-
* @param pxList The list from which the owner of the head item is to be
-
* returned.
-
*
-
* \page listGET_OWNER_OF_HEAD_ENTRY listGET_OWNER_OF_HEAD_ENTRY
-
* \ingroup LinkedList
-
*/
-
#define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( (&( ( pxList )->xListEnd ))->pxNext->pvOwner )
4.10、listIS_CONTAINED_WITHIN
listIS_CONTAINED_WITHIN 用于判断给定的 Item 是否属于一个指定的链表:
-
/*
-
* Check to see if a list item is within a list. The list item maintains a
-
* "container" pointer that points to the list it is in. All this macro does
-
* is check to see if the container and the list match.
-
*
-
* @param pxList The list we want to know if the list item is within.
-
* @param pxListItem The list item we want to know if is in the list.
-
* @return pdTRUE if the list item is in the list, otherwise pdFALSE.
-
*/
-
#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( ( pxListItem )->pxContainer == ( pxList ) ) ? ( pdTRUE ) : ( pdFALSE ) )
4.11、listLIST_ITEM_CONTAINER
listLIST_ITEM_CONTAINER 用于获取 Item 属于的链表结构:
-
/*
-
* Return the list a list item is contained within (referenced from).
-
*
-
* @param pxListItem The list item being queried.
-
* @return A pointer to the List_t object that references the pxListItem
-
*/
-
#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pxContainer )
4.12、listLIST_IS_INITIALISED
listLIST_IS_INITIALISED 用于判断一个指定的链表是否被初始化过:
-
/*
-
* This provides a crude means of knowing if a list has been initialised, as
-
* pxList->xListEnd.xItemValue is set to portMAX_DELAY by the vListInitialise()
-
* function.
-
*/
-
#define listLIST_IS_INITIALISED( pxList ) ( ( pxList )->xListEnd.xItemValue == portMAX_DELAY )
到这里,链表相关的函数和结构,以及常用到的宏也就分析完毕了,更多的在后面分析任务以及任务调度的时候,会经常涉及这些;
值得注意的一点是,List 中的 xListEnd 作为一个 marker,是不动的,而 pxIndex 成员,用来做链表遍历,初始化的时候,指向 xListEnd 位置,但是每次调用 listGET_OWNER_OF_NEXT_ENTRY 后,pxIndex 都会往当前的 pxIndex->pxNext 索引一次;这也是为后面的进程相关的逻辑做准备,提高效率;