FreeRTOS之列表和列表项

列表和列表项是FreeRTOS的一个数据结构,是FreeRTOS的基石。

1.列表
列表是FreeRTOS中的一个数据结构,和链表类似,它的定义位于文件list.c和list.h中。
列表的结构体定义如下:

typedef struct xLIST
{
    listFIRST_LIST_INTEGRITY_CHECK_VALUE              // 用来检查列表完整性:configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1时,添加一个变量,初始化列表的时候赋一个特殊的值
    configLIST_VOLATILE UBaseType_t uxNumberOfItems;  // 记录列表中列表项的数目
    ListItem_t * configLIST_VOLATILE pxIndex;         // 记录当前列表项的索引号,用于遍历列表 
    MiniListItem_t xListEnd;                          // 列表的最后一个列表项
    listSECOND_LIST_INTEGRITY_CHECK_VALUE             // 同listFIRST_LIST_INTEGRITY_CHECK_VALUE一样
} List_t;

// 宏listFIRST_LIST_INTEGRITY_CHECK_VALUE和listSECOND_LIST_INTEGRITY_CHECK_VALUE是用来检查列表完整性的。
// 当宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1时,这两个地方会各添加一个变量xListIntegrityValue1和xListIntegrityValue2,初始化列表的时候,这两个变量中会写入一个特殊的值。
// 默认这个功能不开启。

2.列表项
FreeRTOS提供了两种列表项:列表项和迷你列表项,它们的定义都位于文件list.h中。
(1)列表项的结构体定义如下:

struct xLIST_ITEM
{
    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE            // 用来检查列表完整性
    configLIST_VOLATILE TickType_t xItemValue;           // 列表项的项值,列表按照该值的大小顺序排列
    struct xLIST_ITEM * configLIST_VOLATILE pxNext;      // 指向下一个列表项
    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;  // 指向前一个列表项
    void * pvOwner;                                      // 记录此链表归谁拥有,也就是指向任务控制块
    void * configLIST_VOLATILE pvContainer;              // 用来记录此列表项归哪个列表
    listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE           // 用来检查列表完整性
};
typedef struct xLIST_ITEM ListItem_t;

(2)迷你列表项的结构体定义:

struct xMINI_LIST_ITEM
{
    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE            // 用于检查迷你列表项的完整性
    configLIST_VOLATILE TickType_t xItemValue;           // 列表项值,列表中的列表项按照该值顺序排列
    struct xLIST_ITEM * configLIST_VOLATILE pxNext;      // 指向下一个列表项
    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;  // 指向上一个列表项
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;

3.列表初始化
列表初始化通过函数vListInitialise()来完成,此函数在文件list.c中定义,如下:

void vListInitialise( List_t * const pxList )
{
    // pxList此时只有一个列表项xListEnd,因此pxIndex只能指向这里
    pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );

    // 列表是按照列表项值顺序排列的,那么xListEnd作为列表的最后一个列表项,它的列表项值应该是最大的
    // portMAX_DELAY的定义位于文件portmacro.h中
    pxList->xListEnd.xItemValue = portMAX_DELAY;

    // pxList只有xListEnd这一个列表项,因此pxNext和pxPrevious只能指向自身
    pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
    pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );

    pxList->uxNumberOfItems = ( UBaseType_t ) 0U;

    // 初始化列表项中用于完整性检查字段,只有宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES为1时才有效
    listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
    listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}

4.列表项初始化
列表项的初始化由函数vListInitialiseItem()来完成,定义如下:

void vListInitialiseItem( ListItem_t * const pxItem )
{
    // 初始化pvContainer为NULL,保证该列表项不属于任何列表
    pxItem->pvContainer = NULL;

    // 初始化用于完整性检查的变量,如果开启了这个功能的化
    listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
    listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}

5.列表项插入

void vListInsert( List_t * const pxList,             // 列表项要插入的列表
                  ListItem_t * const pxNewListItem   // 要插入的列表项
                )
{
ListItem_t *pxIterator;
// 获取要插入的列表项的项值,因为列表是按照列表项值升序排列的,根据这个值可以确定列表项要插入的位置
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;  

    // 用来检查列表和列表项的完整性,其实就是检查列表和列表项中用于完整性检查的变量值是否被改变。
    // configASSERT()有效时才能实现。
    listTEST_LIST_INTEGRITY( pxList );
    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );

    // 获取要插入的点,插入的情况分两种:
    // 第1种:pxList的最后一个列表项xListEnd的列表项值是portMAX_DELAY,若当前要插入的列表项的项值时portMAX_DELAY的话,那么它肯定是位于列表最末尾了。
    // 第2种:要插入的列表项的项值不能与portMAX_DELAY的情况,该情况需要与列表中的列表项一个一个去比较项值
    if( xValueOfInsertion == portMAX_DELAY )
    {
        pxIterator = pxList->xListEnd.pxPrevious;
    }
    else
    {
        for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) 
        {

        }
    }

    // 双向链表插入
    pxNewListItem->pxNext = pxIterator->pxNext;
    pxNewListItem->pxNext->pxPrevious = pxNewListItem;
    pxNewListItem->pxPrevious = pxIterator;
    pxIterator->pxNext = pxNewListItem;

    // 记录该列表项属于哪个列表
    pxNewListItem->pvContainer = ( void * ) pxList;

    // 列表项数目加1
    ( pxList->uxNumberOfItems )++;
}

注:当列表中的列表项增多时,会有一个神奇的发现:列表居然是一个环形列表。

6.列表项末尾插入
列表项末尾插入通过函数vListInsertEnd()函数实现,显示如下:

void vListInsertEnd( 
                List_t * const pxList,            // 列表项要插入的列表
                ListItem_t * const pxNewListItem  // 要插入的列表项
                   )
{
ListItem_t * const pxIndex = pxList->pxIndex;

    // 对列表和列表项进行完整性检查
    listTEST_LIST_INTEGRITY( pxList );
    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );

    pxNewListItem->pxNext = pxIndex;
    pxNewListItem->pxPrevious = pxIndex->pxPrevious;

    mtCOVERAGE_TEST_DELAY();

    pxIndex->pxPrevious->pxNext = pxNewListItem;
    pxIndex->pxPrevious = pxNewListItem;

    // 记录列表项属于该列表
    pxNewListItem->pvContainer = ( void * ) pxList;
    ( pxList->uxNumberOfItems )++;
}

注:这里说的在末尾不是指xListEnd表示的末尾,而是要根据pxIndex来确定的。

7.列表项的删除
列表项的删除要通过函数uxListRemove()来实现的,定义如下:

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
// 获取该列表项所属的列表
List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;

    // 删除列表项
    pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
    pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
    mtCOVERAGE_TEST_DELAY();

    // 确保pxIndex不为空
    if( pxList->pxIndex == pxItemToRemove )
    {
        pxList->pxIndex = pxItemToRemove->pxPrevious;
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    // 清空pvContainer
    pxItemToRemove->pvContainer = NULL;
    ( pxList->uxNumberOfItems )--;

    // 返回列表中列表项的数目
    return pxList->uxNumberOfItems;
}

8.列表的遍历
将列表的遍历前,再说明一下:列表List_t中的成员变量pxIndex是用来遍历列表的。
FreeRTOS提供了一个函数来完成列表的遍历,这个函数是listGET_OWNER_OF_NEXT_ENTRY()。
每调用一次这个函数,则列表的pxIndex变量就会指向下一个项,并且返回这个列表项的pxOwner变量值,也就是TCB。
该函数本质上是一个宏,它的定义位于文件list.h文件中,如下:

#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )                                        \
{                                                                                           \
List_t * const pxConstList = ( pxList );                                                    \
    // pxIndex指向下一个列表项
    ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;                            \
    // 若pxIndex指向了列表的xListEnd,则跳过xListEnd,pxIndex再一次指向处于列表头的列表项
    if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) )  \
    {                                                                                       \
        ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;                        \
    }                                                                                       \
    // 将pxIndex指向的新列表项的pvOwner赋值给pxTCB
    ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;                                          \
}

此函数用于从多个同优先级的就绪任务中查找下一个要运行的任务。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值