FreeRTOS实时操作系统(六)列表与列表项

系列文章目录


文章目录

  • 系列文章目录
  • 简要概念
    • 列表
    • 列表项
    • 迷你列表项
  • 相关API函数
    • 初始化列表
    • 列表项初始化
    • 列表项插入(升序)
    • 末尾列表项插入
    • 列表项删除
  • 实战实验


简要概念

列表是 FreeRTOS 中的一个数据结构,概念上和链表有点类似,列表被用来跟踪 FreeRTOS中的任务。
列表项就是存放在列表中的项目

FreeRTOS实时操作系统(六)列表与列表项_第1张图片
FreeRTOS 中的列表是一个双向环形链表。

列表的特点:列表项间的地址非连续的,是人为的连接到一起的。列表项的数目是由后期添加的个数决定的,随时可以改变。

列表的相关文件全部都在list.c和list.h中:

列表

FreeRTOS实时操作系统(六)列表与列表项_第2张图片

从上到下代表着:校验值、列表中的列表项的个数、遍历列表项的指针、末尾列表项、校验值

其中,末尾列表项是一个迷你列表项,排在末尾。

原本代码及注释:

typedef struct xLIST
{
	listFIRST_LIST_INTEGRITY_CHECK_VALUE				/*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
	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;

列表项

FreeRTOS实时操作系统(六)列表与列表项_第3张图片
在xLIST_ITEM中,从上到下分别代表着:
校验列表项的数据完整性、用于降序排列的值、指向下一个链表项、指向上一个链表项、列表项的拥有者(通常是任务控制块)、指向列表项所在的列表、校验列表项的数据完整性。
(有点疑问,这里注释是说按照降序排列,而正点原子讲的是按照升序排列)

原本代码及注释:

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. */

迷你列表项

原本代码及注释:

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;

从上到下代表着:检测数据完整性、列表项的值、下一个列表、上一个列表

迷你列表项只用于标记列表的末尾和挂载其他插入列表中的列表项,因此不需要成员变量 pxOwner 和 pxContainer,以节省内存开销。

实例:
FreeRTOS实时操作系统(六)列表与列表项_第4张图片

相关API函数

初始化列表:vListInitialise()
初始化列表项:vListInitialiseItem()
列表末尾插入列表项:vListInsertEnd()
列表按照数值升序插入列表项:vListInsert()
列表移除列表项:uxListRemove()

初始化列表

由下图的初始化列表可知,xListEnd末尾列表项被定义为最大值,所以推测前面的注释意思应该是按照升序排列的。
FreeRTOS实时操作系统(六)列表与列表项_第5张图片
FreeRTOS实时操作系统(六)列表与列表项_第6张图片
FreeRTOS实时操作系统(六)列表与列表项_第7张图片

列表项初始化

FreeRTOS实时操作系统(六)列表与列表项_第8张图片
FreeRTOS实时操作系统(六)列表与列表项_第9张图片
列表项所属列表先不赋值。

列表项插入(升序)

void vListInsert  (  List_t * const pxList ,   ListItem_t * const pxNewListItem  )

将列表项按照其项值的升序方式插入列表

正点原子的视频中讲的很清除关于这一部分是如何插入的
FreeRTOS实时操作系统(六)列表与列表项_第10张图片
插入第一个列表项的过程:
FreeRTOS实时操作系统(六)列表与列表项_第11张图片

末尾列表项插入

这里并不是指最后一个列表项,是指插入到当前列表pxIndex指向的列表项的前一个位置
FreeRTOS实时操作系统(六)列表与列表项_第12张图片
这是一种无序插入

例如:如果pxIndex指向的是xListEnd时,插入的情况:
FreeRTOS实时操作系统(六)列表与列表项_第13张图片

列表项删除

FreeRTOS实时操作系统(六)列表与列表项_第14张图片

这里移除列表项的时候没有将内存释放,有文章说可能后续还要使用,所以没有释放,还不是很懂现在。

实战实验

实验代码:

List_t          TestList;           /* 定义测试列表 */
ListItem_t      ListItem1;          /* 定义测试列表项1 */
ListItem_t      ListItem2;          /* 定义测试列表项2 */
ListItem_t      ListItem3;          /* 定义测试列表项3 */

void vTaskCode( void * pvParameters )
 {
	 
	vListInitialise(&TestList);         /* 初始化列表 */
    vListInitialiseItem(&ListItem1);    /* 初始化列表项1 */
    vListInitialiseItem(&ListItem2);    /* 初始化列表项2 */
    vListInitialiseItem(&ListItem3);    /* 初始化列表项3 */
    ListItem1.xItemValue = 40;
    ListItem2.xItemValue = 60;
    ListItem3.xItemValue = 50;

    /* 第二步:打印列表和其他列表项的地址 */
    printf("/**************第二步:打印列表和列表项的地址**************/\r\n");
    printf("项目\t\t\t地址\r\n");
    printf("TestList\t\t0x%p\t\r\n", &TestList);
    printf("TestList->pxIndex\t0x%p\t\r\n", TestList.pxIndex);
    printf("TestList->xListEnd\t0x%p\t\r\n", (&TestList.xListEnd));
    printf("ListItem1\t\t0x%p\t\r\n", &ListItem1);
    printf("ListItem2\t\t0x%p\t\r\n", &ListItem2);
    printf("ListItem3\t\t0x%p\t\r\n", &ListItem3);
    printf("/**************************结束***************************/\r\n");
    
    printf("\r\n/*****************第三步:列表项1插入列表******************/\r\n");
    vListInsert((List_t*    )&TestList,         /* 列表 */
                (ListItem_t*)&ListItem1);       /* 列表项 */
    printf("项目\t\t\t\t地址\r\n");
    printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
    printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
    printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
    printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
    printf("/**************************结束***************************/\r\n");
    
    /* 第四步:列表项2插入列表 */
    printf("\r\n/*****************第四步:列表项2插入列表******************/\r\n");
    vListInsert((List_t*    )&TestList,         /* 列表 */
                (ListItem_t*)&ListItem2);       /* 列表项 */
    printf("项目\t\t\t\t地址\r\n");
    printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
    printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
    printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
    printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
    printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
    printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
    printf("/**************************结束***************************/\r\n");
    
    /* 第五步:列表项3插入列表 */
    printf("\r\n/*****************第五步:列表项3插入列表******************/\r\n");
    vListInsert((List_t*    )&TestList,         /* 列表 */
                (ListItem_t*)&ListItem3);       /* 列表项 */
    printf("项目\t\t\t\t地址\r\n");
    printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
    printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
    printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
    printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
    printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
    printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
    printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
    printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
    printf("/**************************结束***************************/\r\n");
    
    /* 第六步:移除列表项2 */
    printf("\r\n/*******************第六步:移除列表项2********************/\r\n");
    uxListRemove((ListItem_t*   )&ListItem2);   /* 移除列表项 */
    printf("项目\t\t\t\t地址\r\n");
    printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
    printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
    printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
    printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
    printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
    printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
    printf("/**************************结束***************************/\r\n");
    
    /* 第七步:列表末尾添加列表项2 */
    printf("\r\n/****************第七步:列表末尾添加列表项2****************/\r\n");
    TestList.pxIndex = &ListItem1;
    vListInsertEnd((List_t*     )&TestList,     /* 列表 */
                   (ListItem_t* )&ListItem2);   /* 列表项 */
    printf("项目\t\t\t\t地址\r\n");
    printf("TestList->pxIndex\t\t0x%p\r\n", TestList.pxIndex);
    printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
    printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
    printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
    printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
    printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
    printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
    printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
    printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
    printf("/************************实验结束***************************/\r\n");
    while(1)
    {
        vTaskDelay(1000);
    }
 }

首先,创建了三个列表项,每个列表项都有自己的地址,然后我们对各个函数进行测试,在第五步列表项3插入之后,列表的
pxIndex仍然指向的是末尾列表项,所以这个时候删除列表项后,pxIndex也还指向的是末尾列表项。在第七步中,我们指定了pxIndex指向列表项1,所以末尾列表项插入时,插入到列表项1前面。如果不指定的话,按照上一步操作后pxIndex指向的是末尾列表项,所以插入后会出现在末尾列表项前面。
实验结果:

FreeRTOS实时操作系统(六)列表与列表项_第15张图片
FreeRTOS实时操作系统(六)列表与列表项_第16张图片

你可能感兴趣的:(FreeRTOS,链表,数据结构)