FreeRTOS 列表和列表项
列表和列表项是FreeRTOS 的一个数据结构,FreeRTOS 大量使用到了列表和列表项,它是FreeRTOS 的基石。
##什么是列表和列表项?
列表
列表是 FreeRTOS 中的一个数据结构,概念上和链表有点类似,列表被用来跟踪 FreeRTOS中的任务。与列表相关的全部东西都在文件 list.c 和 list.h 中。
1 | typedef struct xLIST |
(1)和(5)、这两个都是用来检查列表完整性的,需要将宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 设置为 1,开启以后会向这两个地方分别添加一个变量 xListIntegrityValue1 和xListIntegrityValue2,在初始化列表的时候会这两个变量中写入一个特殊的值,默认不开启这个功能。
(2)、 uxNumberOfItems 用来记录列表中列表项的数量。
(3)、 pxIndex 用来记录当前列表项索引号,用于遍历列表。
(4)、 列表中最后一个列表项,用来表示列表结束,此变量类型为 MiniListItem_t,这是一个
迷你列表项,关于列表项稍后讲解。
列表项
FreeRTOS提供了两种列表项:列表项和迷你列表项,两个都在文件 list.h 中有定义。
1 | struct xLIST_ITEM |
(1)和(7)、用法和列表一样,用来检查列表项完整性的。
(2)、 xItemValue 为列表项值。
(3)、 pxNext 指向下一个列表项。
(4)、 pxPrevious 指向前一个列表项,和 pxNext 配合起来实现类似双向链表的功能。
(5)、 pvOwner 记录此链表项归谁拥有,通常是任务控制块。
(6)、 pvContainer 用来记录此列表项归哪个列表。注意和 pvOwner 的区别,在前面讲解任务控制块 TCB_t 的时候说了在 TCB_t 中有两个变量 xStateListItem 和 xEventListItem,这两个变量的类型就是 ListItem_t,也就是说这两个成员变量都是列表项。以 xStateListItem 为例,当创建一个任务以后 xStateListItem 的 pvOwner 变量就指向这个任务的任务控制块,表示 xSateListItem属于此任务。当任务就绪态以后 xStateListItem 的变量pvContainer 就指向就绪列表,表明此列表项在就绪列表中。
列表项 |
---|
xItemValue |
pxNext |
pxPrevious |
pvOwner |
pvContainer |
迷你列表项
迷你列表项在文件 list.h 中有定义。
1 | struct xMINI_LIST_ITEM |
(1)、用于检查迷你列表项的完整性。
(2)、 xItemValue 记录列表列表项值。
(3)、 pxNext 指向下一个列表项。
(4)、 pxPrevious 指向上一个列表项。
可以看出迷你列表项只是比列表项少了几个成员变量,迷你列表项有的成员变量列表项都有的。
Mini 列表项 |
---|
xItemValue |
pxNext |
pxPrevious |
列表和列表项初始化
列表初始化
新创建或者定义的列表需要对其做初始化处理,列表的初始化其实就是初始化列表结构体List_t 中的各个成员变量,列表的初始化通过使函数 vListInitialise( )来完成,此函数在 list.c 中有定义。
1 | void vListInitialise( List_t * const pxList ) |
(1)、 xListEnd 用来表示列表的末尾,而 pxIndex 表示列表项的索引号,此时列表只有一个列表项,那就是 xListEnd,所以 pxIndex 指向 xListEnd。
(2)、 xListEnd 的列表项值初始化为 portMAX_DELAY, portMAX_DELAY 是个宏,在文件portmacro.h 中有定义。根据所使用的 MCU 的不同, portMAX_DELAY 值也不相同,可以为 0xffff或者 0xffffffffUL,本教程中为 0xffffffffUL。
(3)、初始化列表项 xListEnd 的 pxNext 变量,因为此时列表只有一个列表项 xListEnd,因此 pxNext 只能指向自身。
(4)、同(3)一样,初始化 xListEnd 的 pxPrevious 变量,指向 xListEnd 自身。
(5)、由于此时没有其他的列表项,因此 uxNumberOfItems 为 0,注意,这里没有算 xListEnd。
(6) 和 (7) 、 初始化列表项中用于完整性检查字段,只有宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 为 1 的时候才有效。同样的根据所选的MCU 不同其写入的值也不同,可以为 0x5a5a 或者 0x5a5a5a5aUL。STM32是32 位系统写入的是 0x5a5a5a5aUL。
列表项初始化
函数vListInitialiseItem()完成列表项初始化1
2
3
4
5
6
7
8void vListInitialiseItem( ListItem_t * const pxItem )
{
pxItem->pvContainer = NULL; //初始化 pvContainer 为 NULL
//初始化用于完整性检查的变量,如果开启了这个功能的话。
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
列表项的初始化很简单,只是将列表项成员变量 pvContainer 初始化为 NULL,并且给用于
完整性检查的变量赋值。
列表项插入
列表项插入函数分析
列表项的插入操作通过函数 vListInsert()来完成,函数原型如下:1
2void vListInsert( List_t * const pxList,
ListItem_t * const pxNewListItem )
参数
pxList: 列表项要插入的列表。
pxNewListItem: 要插入的列表项。
返回值:
无
函数 vListInsert()的参数 pxList 决定了列表项要插入到哪个列表中, pxNewListItem 决定了要插入的列表项,但是这个列表项具体插入到什么地方呢?要插入的位置由列表项中成员变量xItemValue 来决定。 列表项的插入根据 xItemValue 的值按照升序的方式排列!
列表项末尾插入
列表项末尾插入函数分析
列表末尾插入列表项的操作通过函数 vListInsertEnd ()来完成,函数原型如下:1
2void vListInsertEnd( List_t * const pxList,
ListItem_t * const pxNewListItem )
参数:
pxList: 列表项要插入的列表。
pxNewListItem: 要插入的列表项。
列表项的删除
有列表项的插入,那么必然有列表项的删除,列表项的删除通过函数 uxListRemove()来完
成,函数原型如下:
1 | UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ) |
参数:
pxItemToRemove: 要删除的列表项。
返回值:
返回删除列表项以后的列表剩余列表项数目。
注意,列表项的删除只是将指定的列表项从列表中删除掉,并不会将这个列表项的内存给释放掉!如果这个列表项是动态分配内存的话。
列表的遍历
介绍列表结构体的时候说过列表 List_t 中的成员变量 pxIndex 是用来遍历列表的,FreeRTOS提供了一个函数来完成列表的遍历,这个函数是 listGET_OWNER_OF_NEXT_ENTRY()。每调用一次这个函数列表的 pxIndex 变量就会指向下一个列表项,并且返回这个列表项的 pxOwner变量值。这个函数本质上是一个宏,这个宏在文件 list.h 中如下定义