FreeRTos链表操作分析 注释文档

FreeRTOS 使用双向链表来描述链表结构,FreeRTOS 中定义了 3 个相关的结构:

ListItem_t:用来表示链表中的一个元素;

MiniListItem_t:用来表示链表中初始的那个元素;

List_t:用来表示一个链表;

1.ListItem_t

struct xLIST_ITEM
{
    configLIST_VOLATILE TickType_t xItemValue;      //数据   
    struct xLIST_ITEM * configLIST_VOLATILE pxNext;     //next 指针
    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;  //prv指针
    void * pvOwner;       // 指向拥有这个 Item 成员的结构体,通常是描述进程 TCB 的指针
    struct xLIST * configLIST_VOLATILE pxContainer;   //记录该链表项属于哪个链表  
};
typedef struct xLIST_ITEM ListItem_t;

2.MiniListItem_t:用来表示链表中初始的那个元素;

struct xMINI_LIST_ITEM
{
    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.表示链表

typedef struct xLIST
{
    volatile UBaseType_t uxNumberOfItems;       //记录链表里有多少成员
    ListItem_t * configLIST_VOLATILE pxIndex;   //指向item的结构体,用来遍历链表
    MiniListItem_t xListEnd;                    //链表尾部
} List_t;

MiniListItem_t xListEnd的含义:
由于在链表的尾部(头部),其链表项一般不需要有具体的内容,只需要提供下一个或前一个链表项的地址即可,因此为了节省RAM,定义了一个Mini链表项MiniListItem_t  作为链表的尾。

链表记录了有多少个节点,遍历节点的指针,链表尾部成员可以找到下一个或上一个节点。

链表之间的关系用图示如下:

二,链表的相关操作:list.c

1. 链表项初始化:

void vListInitialiseItem( ListItem_t * const pxItem )
{
    pxItem->pxContainer = NULL;
    listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );   //初始化校验值
    listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
//链表的初始化函数
void vListInitialise( List_t * const pxList )
{
    //当前指针指向链表尾部
    pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); 
    //设置排序值最大,确保xListEnd放在尾部
    pxList->xListEnd.xItemValue = portMAX_DELAY;

    //xListEnd的前一项和下一项指向尾部,表示链表为空
    pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );     
    pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
     //链表数为0
    pxList->uxNumberOfItems = ( UBaseType_t ) 0U;

     //写入校验
    listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
    listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}

 2. 链表的插入
 链表的插入操作有两个函数,分别是vListInsertEnd()和vListInsert()。
 其中vListInsertEnd()直接在链表pxIndex指向的前一位置插入一个链表项,
 而vListInsert()是有序的插入方法,链表项将会按xItemValue的值进行升序插入到链表中去。

2.1 

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 );

    //新链表插入到pxIndex指向的链表前
    pxNewListItem->pxNext = pxIndex;
    pxNewListItem->pxPrevious = pxIndex->pxPrevious;

    mtCOVERAGE_TEST_DELAY();

    //原来的链表指向新的链表项
    pxIndex->pxPrevious->pxNext = pxNewListItem;
    pxIndex->pxPrevious = pxNewListItem;

    //记录链表项属于pxList链表
    pxNewListItem->pxContainer = pxList;
    //链表项数目增加
    ( pxList->uxNumberOfItems )++;
}

2.2 按值插入
 

void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
    ListItem_t * pxIterator;
    const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;

     //校验表和链表
    listTEST_LIST_INTEGRITY( pxList );
    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );

    //寻找插入位置
    if( xValueOfInsertion == portMAX_DELAY )
    {
        pxIterator = pxList->xListEnd.pxPrevious;
    }
    else
    {
       //寻找位置
        for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) 
        {
            /* 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;

    //记录链表项属于pxList链表
    pxNewListItem->pxContainer = pxList;
    //链表项数目增加
    ( pxList->uxNumberOfItems )++;
}

3.删除操作

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
    List_t * const pxList = pxItemToRemove->pxContainer;

    //调整前后指针
    pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
    pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;

    mtCOVERAGE_TEST_DELAY();

     //当前链表遍历指针等于删除项,则指向前一个,保证遍历指针的有效性
    if( pxList->pxIndex == pxItemToRemove )
    {
        pxList->pxIndex = pxItemToRemove->pxPrevious;
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }
    //移除的链表项不在被链表拥有
    pxItemToRemove->pxContainer = NULL;
    //链表项数目减1
    ( pxList->uxNumberOfItems )--;

    return pxList->uxNumberOfItems;
}

4.遍历
使用宏定义

#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )                                           
    {                                                                                          
        List_t * const pxConstList = ( pxList );                                               
             
         //遍历链表的指针指向下一个             
        ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; 
         //遍历到表尾         
        if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) 
        {                                                                                      
            ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;                       
        }                                                                                      
        ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;                                         
    }


   这里定义的变量名是pxTCB(Task Control Block 任务控制块),这个遍历操作主要是给任务调度管理用的。
   通过多次调用这一宏可以对链表进行遍历操作。
   

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值