《FreeRTOS列表和列表项篇》

FreeRTOS列表和列表项

  • [1. 什么是列表和列表项?](#1. 什么是列表和列表项?)
    • [1.1 列表list](#1.1 列表list)
    • [1.2 列表项list item](#1.2 列表项list item)
  • [2. 列表和列表项的初始化](#2. 列表和列表项的初始化)
    • [2.1 列表的初始化](#2.1 列表的初始化)
    • [2.2 列表项的初始化](#2.2 列表项的初始化)
  • [3. 列表项的插入](#3. 列表项的插入)
  • [4. 列表项末尾插入](#4. 列表项末尾插入)
  • [5. 列表项的删除](#5. 列表项的删除)
  • [6. 列表的遍历](#6. 列表的遍历)

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

1. 什么是列表和列表项?

1.1 列表list

  • 列表在概念上类似双向链表,用来追踪FreeRTOS中的任务
c 复制代码
/*
 * Definition of the type of queue used by the scheduler.
 * 任务调度器使用的队列的定义
 */
typedef struct xLIST
{
    listFIRST_LIST_INTEGRITY_CHECK_VALUE
    volatile UBaseType_t uxNumberOfItems;	  /* 用来记录列表中列表项的个数,其中不包含末尾列表项。 */
    ListItem_t * configLIST_VOLATILE pxIndex; /* 用来记录当前列表项的索引号,用于遍历列表。 */
    MiniListItem_t xListEnd;                  /* 末尾列表项(迷你列表项),用来表示列表的结束。 */
    listSECOND_LIST_INTEGRITY_CHECK_VALUE
} List_t;

1.2 列表项list item

  • 列表项指存放在列表中的对象,FreeRTOS提供了2个类型:列表项和迷你列表项。
c 复制代码
/*
 * Definition of the only type of object that a list can contain.
 * 定义列表可以包含的唯一对象类型
 */
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;                                     /* 指向包含列表项的对象(通常是TCB)的指针。*/
    struct xLIST * configLIST_VOLATILE pxContainer;     /* 用来记录此列表项属于哪个列表。 */
    listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE
};
typedef struct xLIST_ITEM ListItem_t;
c 复制代码
#if ( configUSE_MINI_LIST_ITEM == 1 ) /* 若为1,则为普通列表项,若为0,则为迷你列表项。 */
    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;
#else
    typedef struct xLIST_ITEM      MiniListItem_t;
#endif

2. 列表和列表项的初始化

2.1 列表的初始化

c 复制代码
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 ); /* 列表的列表项索引号指向末尾列表项。*/

    listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( &( pxList->xListEnd ) );

    /* 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; /* 0xFFFFFFFFUL */

    /* 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 );
    pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );

    /* Initialize the remaining fields of xListEnd when it is a proper ListItem_t 
	* 当末尾列表项是普通列表项时,初始化其剩余字段。
	*/
    #if ( configUSE_MINI_LIST_ITEM == 0 )
    {
        pxList->xListEnd.pvOwner = NULL;
        pxList->xListEnd.pxContainer = NULL;
        listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( &( pxList->xListEnd ) );
    }
    #endif

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

2.2 列表项的初始化

  • 列表项的初始化此处只对pxContainer设置了初始值,其他成员变量需要根据实际使用情况初始化。
c 复制代码
void vListInitialiseItem( ListItem_t * const pxItem )
{
    /* Make sure the list item is not recorded as being on a list. 
 	* 确保列表项未被记录为在列表中。
	*/
    pxItem->pxContainer = NULL;
    listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
    listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}

3. 列表项的插入

  • 注意,列表项的常规插入方式,指的是在待插入的列表List中找到比待插入的列表项ListItem的列表项值xItemValue大的列表项前边插入该列表项,列表中的所有列表项是按照其列表项值的大小手拉手链接在一起的。
c 复制代码
void vListInsert( List_t * const pxList, /* 决定列表项要插入到哪一个列表中。 */
				  ListItem_t * const pxNewListItem ) /* 准备插入列表中的列表项。要插入的位置由列表项中的成员变量xItemValue决定 */
{
    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;

    /* Remember which list the item is in.  This allows fast removal of the
     * item later. */
    pxNewListItem->pxContainer = pxList; /* 将列表项的成员变量pxContainer赋值为插入的列表。 */

    ( pxList->uxNumberOfItems )++; /* 列表的列表项总个数自增1 */
}



4. 列表项末尾插入

  • 注意,列表项末尾插入的方式,指的是在列表的成员变量pxIndex所指向位置的前边插入待插入的列表项,与列表项的列表项值没有关系。
c 复制代码
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;

    /* Remember which list the item is in. */
    pxNewListItem->pxContainer = pxList;

    ( pxList->uxNumberOfItems )++;
}



5. 列表项的删除

  • 如果这个列表项是动态分配内存的,列表项的删除只是将指定的列表项从列表中删除掉,并不会将这个列表项的内存释放掉。
c 复制代码
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;
    ( pxList->uxNumberOfItems )--;

    return pxList->uxNumberOfItems;
}

6. 列表的遍历

  • 列表结构体中的成员变量pxIndex是用来遍历列表的。
  • FreeRTOS提供了一个函数来完成列表的遍历,函数是listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )。每调用一次这个函数,列表的pxIndex变量就会指向下一个列表项,并且返回这个列表项的pvOwner变量值。
c 复制代码
#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;                                         \
    }
相关推荐
Krysdon8 天前
Freertos任务切换
stm32·freertos
硝酯蒽唑Ti镓9 天前
TMS320C55x DSP芯片结构和CPU外围电路
笔记·dsp开发
云空16 天前
《芯片:科技之核,未来之路》
arm开发·科技·fpga开发·硬件工程·dsp开发·材料工程
ADI_OP19 天前
用System Workbench for STM32做MCU控制ADI DSP的开发,如何导入工程
stm32·单片机·嵌入式硬件·dsp开发·adi dsp中文资料·adi音频dsp·adi dsp开发教程
李解4920 天前
裸机和跑系统差异的无脑问题
c语言·数据库·freertos
WPG大大通22 天前
关于音频 DSP 的接口种类以及其应用场景介绍
经验分享·音频·dsp开发·通信·车载·大大通
淡忘的江南1 个月前
《FreeRTOS任务删除篇》
freertos·dsp开发
艾思科蓝1 个月前
知识点归纳 | 信号处理入门
linux·卷积神经网络·实时音视频·信息与通信·信号处理·dsp开发·傅里叶分析
drinow1 个月前
【纸飞机串口调试工具】预设曲线名称
arm开发·驱动开发·单片机·mcu·dsp开发·fpga·iot
likkoliu1 个月前
FreeRTOS LIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 存在的意义以及高于它的中断不能调用 safe freertos api
freertos·中断·safe freertos api·library_max_syscall_interrupt_priority