FreeRTOS学习-链表(List_t)

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_VALUElistSECOND_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使用前被调用,主要负责设置pxContainerNULL,表示其未在任何链表中。

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 )
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值