1. 简介
在FreeRTOS中,List是为了任务调度器深度定制的链表,因此,理解FreeRTOS内核中的链表有助于理解任务调度的原理和实现。当然该链表也可以为应用程序所用。本文将介绍FreeRTOS中List的基本用法和关键的数据结构。
2. 数据结构
2.1. 链表项(ListItem_t)
/*
* Definition of the only type of object that a list can contain.
*/
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. */
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. */
链表项包含一个数值变量(xItemValue
)。
除此之外,每个链表项包含一个指向下一个链表项的指针,一个指向上一个链表项的指针,因此,这是一个双向链表。
出于性能的考虑,FreeRTOS的链表项还提供了两个特殊的成员:
pvOwner
:指向了包含该链表项的对象(通常是TCB),因此,FreeRTOS的链表与所有者互联,即所有者与链表项相互关联。pvContainer
:指向了包含该链表项的链表,因此链表与链表项相互关联。
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE
与listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE
宏用于检测结构体数据是否被破坏,需要开启configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES
功能才能生效。
2.2. 链表(List_t)
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. */
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;
可以看到,链表只存储ListItem_t
类型的指针。
uxNumberOfItems
记录了链表项的数量。
pxIndex
指向最后一次(调用listGET_OWNER_OF_NEXT_ENTRY()
)返回的项,可用于遍历整个链表。
有趣的是,xListEnd
是简化的ListItem_t
,因为该链表项总是存在,且指向链表尾,所以不需要Owner和Container信息。
通常情况下,链表根据xItemValue
的大小升序排列链表项。这个值在不同的使用场景中代表的含义不同:
- 在定时器的定时器队列中,用于记录定时器的超时时刻;
- 在事件组维护的任务链表中,用于记录任务关心的事件位图:
- 在互斥锁维护的任务链表中,用于记录Owner Task的优先级;
- 在延迟任务队列中,用于记录任务的超时时刻。
3. 创建
3.1. 链表
函数原型:
void vListInitialise(List_t * const pxList) PRIVILEGED_FUNCTION
该函数必须在List_t
使用前被调用,主要负责初始化List_t
结构体中所有的成员变量。比较重要的操作是,初始化xListEnd
——设置该项的值为最大值(portMAX_DELAY
),因此,该项总是在链表的尾部,作为一个Marker。
PRIVILEGED_FUNCTION
是一个特殊的宏,用于具有MPU(Memory Protection Unit)功能的平台。作用是使用编译器attribute section指令,将函数放置在特定的内存区域。
3.2. 链表项
函数原型:
void vListInitialiseItem(ListItem_t * const pxItem) PRIVILEGED_FUNCTION
该函数必须在ListItem_t
使用前被调用,主要负责设置pxContainer
为NULL
,表示其未在任何链表中。
4. 插入
API1函数原型:
void vListInsert(List_t * const pxList, ListItem_t * const pxNewListItem) PRIVILEGED_FUNCTION
将一个链表项插入链表,需要注意的是,插入的位置由被插入的链表项的值(xItemValue
)的大小决定。被插入的链表项升序排列,如果存在值相同的情况,则将新的链表项放置在相同项中的末尾,这是为了优先级相同xItemValue
的TCB在Ready链表中按入队顺序排列。
特别地,对于和Marker的值相同的链表项,该实现将会把其放置在xListEnd
的前一个位置,这是为了防止顺序查找算法永不停止。
API2函数原型:
void vListInsertEnd(List * const pxList, ListItem_t * const pxNewListItem) PRIVILEGED_FUNCTION
与API1不同的是,新的链表项将被插入到pxIndex
所指向的前一个节点。这么做的好处是调用listGET_OWNER_OF_NEXT_ENTRY()
时能正常返回插入前的下一个链表项。
5. 删除
函数原型:
void vListRemove(ListItem_t * const pxItemToRemove) PRIVILEGED_FUNCTION
将指定的链表项从链表中移除。有趣的是,这里并不需要提供List_t
指针,因为ListItem_t
本身包含了Container,这体现了链表和链表项互联的优势。
6. 其他操作
宏:含义如其名,不需要多解释了
#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner )
#define listGET_LIST_ITEM_OWNER( pxListItem )
#define listSET_LIST_ITEM_VALUE( pxListItem, xValue )
#define listGET_LIST_ITEM_VALUE( pxListItem )
#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList )
#define listGET_HEAD_ENTRY( pxList )
#define listGET_NEXT( pxListItem )
#define listGET_END_MARKER( pxList )
#define listLIST_IS_EMPTY( pxList )
#define listCURRENT_LIST_LENGTH( pxList )
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) // 获取下一个链表项的所有者,需要注意的是,该宏会修改List_t的pxIndex,可用于实现链表的遍历。这里体现了所有者与链表项的互联的优势。
#define listGET_OWNER_OF_HEAD_ENTRY( pxList )
#define listIS_CONTAINED_WITHIN( pxList, pxListItem )
#define listLIST_ITEM_CONTAINER( pxListItem )
#define listLIST_IS_INITIALISED( pxList )