FreeRTOS的列表和列表项 list.c文件详解

列表、列表项的定义以及初始化

列表相当于链表,列表项相当于节点,FreeRTOS中的列表相当于一个双向环形链表。
列表使用指针指向列表项。一个列表(list)下面可能有很多个列表项(list item),每个列表项都有一个指针指向列表。
与列表相关的全部东西都在文件list.c和list.h中。在list.h中定义了一个List_t的结构体。

列表的定义

在这里插入图片描述

typedef struct xLIST
{
	listFIRST_LIST_INTEGRITY_CHECK_VALUE				/*用于检测列表项数据是否完整*/
	configLIST_VOLATILE UBaseType_t uxNumberOfItems;	/*记录列表中列表项的数量*/
	ListItem_t * configLIST_VOLATILE pxIndex;			/*用于遍历列表*/
	MiniListItem_t xListEnd;							/*列表项*/
	listSECOND_LIST_INTEGRITY_CHECK_VALUE				/*用于检测列表项数据是否完整*/
} List_t;
  1. 第一句和最后一句用来检查列表的完整性。
    需要将宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 设置为1,开启以后会向这两个地方分别添加一个变量xListIntegrityValue1和xListIntegrityValue2,在初始化列表的时候会这两个变量中写入一个特殊的值,默认不开启这个功能。
  2. uxNumberOfItems 用来记录列表中列表项的数量。
  3. pxIndex 用来记录当前列表项索引号,用于遍历列表。
  4. 列表项中最后一个列表项,用来表示列表结束,此变量类型为MiniListItem_t,是一个迷你列表项。

列表项的定义

列表项就是存放在列表中的项目,FreeRTOS提供了两种列表项:列表项和迷你列表项。这两种都在文件list.h中有定义。

xLIST_ITEM列表项

在这里插入图片描述

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;
  1. 第一句和最后一句,用来检查列表项的完整性。
  2. xItemValue列表项的值。
  3. pxNext指向下一个列表项。
  4. pxPrevious指向前一个列表项,和pxNext配合起来实现类似双向链表的功能。
  5. pvOwner记录此列表项归谁拥有,通常是任务控制块。
  6. pvContainer用来记录此列表项归哪个列表。
    注意与pvOwner的区别,在TCB_t中有两个变量xStateListItem和xEventListItem,这两个变量的类型就是ListItem_t,也就是说这两个成员变量都是列表项,以xStateListItem为例,当创建一个任务以后,xStateListItem的pvOwner就指向这个任务的控制块,表示xStateListItem属于此任务。当任务就绪以后,xStateListItem的变量pvContainer就指向就绪列表,表明此列表项在就绪列表中。

xMINI_LIST_ITEM列表项

末尾列表项就是迷你列表项的一种,这里放末尾列表项的定义图。
在这里插入图片描述

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;
  1. 第一句检测列表项的完整性。
  2. xItemValue记录列表列表项的值。
  3. pxNext指向下一个列表项
  4. pxPrevious指向上一个列表项
    迷你列表项比列表项少几个成员变量,迷你列表项有的成员变量,列表项都有。为什么还要使用迷你列表项呢?因为有些情况下,我们不需要这么全的功能,可能只需要其中某几个成员变量,如果此时用列表的话会造成内存浪费。例如上面列表结构体List_t中表示最后一个列表项的成员变量xListEnd 就是MiniListItem_t类型。

列表的初始化

在这里插入图片描述

如上图所示,列表初始化,列表的pxindex指向列表末尾项,把末尾的列表项初始化为最大值(0xFFFFFFFF),列表末尾项的前指针和后指针都指向它本身。下面是具体的初始化代码:

void vListInitialise( List_t * const pxList )
{
	/*初始化时,列表中只有xListEnd,因此pxIndex指向xListEnd*/
	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(不包含xListEnd)*/
	pxList->uxNumberOfItems = ( UBaseType_t ) 0U;

	/*初始化用于检测列表数据完整性的校验值*/
	listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
	listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}

列表项的初始化

从xLIST_ITEM列表项可知,链表节点ListItem_t 总共有5个成员,但是初始化的时候只需将pvContainer初始化为空即可。
pvContainer初始化为空表示该节点还没有插入到任何链表。
在这里插入图片描述

void vListInitialiseItem( ListItem_t * const pxItem )
{
	/* 初始化时,列表项所在列表设为空。 */
	pxItem->pvContainer = NULL;

	/* 初始化用于检测列表数据完整性的校验值 */
	listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
	listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}

列表操作

1. 插入列表尾部

在这里插入图片描述

  1. 首先获取列表pxList中指针pxIndex 指向的列表项,如上图,如果是一个新的列表,pxIndex 指向的应该是末尾列表项。
  2. 将待插入列表项pxNewListItem的pxNext指向末尾列表项, 将待插入列表项pxNewListItem的pxPrevious 指向末尾列表项的pxPrevious。
  3. 将列表中的末尾列表项的前指向pxPrevious指向插入列表项pxNewListItem。将原来列表项前指向的对象的后指向绑定为pxNewListItem。
  4. 将添加的新列表项归属到列表中。
  5. 更新列表中列表项的数量。
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
	/* 获取列表pxIndex指向的列表项 */
	ListItem_t * const pxIndex = pxList->pxIndex;

	/* 仅当同时定义了confgASSERT()时才有效,这些测试可能会捕获正在内存中覆盖的列表数据结构。它们不会捕获由于不正确配置或使用FreeRTOS而导致的数据错误. */
	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 )++;
}

2. 升序插入列表

在这里插入图片描述

  1. 获取要添加列表项的数值,在后面用来比较排列。
  2. 根据列表项的数值,找到列表项要插入的位置,按照升序排列。如果是列表项数值为最大值,指向末尾列表项。否则,迭代找到列表项要插入的位置。然后根据升序排列,插入列表项,再将列表项加到列表里,列表的数量再加1。
  3. 如上图,是按照升序排列,将节点插入到链表。假设将一个节点排序辅助值是2的节点插入到有两个节点的链表中,这两个现有的节点的排序辅助值分别是1和3,那么插入过程的示意图具体见图。
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 ) 
		{
			/* 没有事情可做,不断迭代只为了找到节点要插入的位置 */
		}
	}
	
	/* 根据升序排列,将节点插入 */
	pxNewListItem->pxNext = pxIterator->pxNext;
	pxNewListItem->pxNext->pxPrevious = pxNewListItem;
	pxNewListItem->pxPrevious = pxIterator;
	pxIterator->pxNext = pxNewListItem;

	/* 更新插入列表项的归属列表 */
	pxNewListItem->pvContainer = ( void * ) pxList;
	/* 列表中的列表项数+1 */
	( pxList->uxNumberOfItems )++;
}

3. 移除一个列表项

传入参数:pxItemToRemove是指向待移除的列表项的指针。
返回参数:移除后列表中列表项的数量。

  1. 首先通过指向列表项的指针获得列表项中所记录的列表信息,通过pvContainer结构体成员获得。
  2. 将(需要移除列表项)的(下一个列表项)的(前指针指向对象)指向(需要移除列表项)的(前指针指向对象)。将(需要移除列表项)的(前指针指向对象)的(后指针指向对象)指向(需要移除列表项)的(后指针指向对象)。简单地说,就是把需要移除的列表项在链表中解除指向。
  3. 如果列表正指向待移除的列表项,将其指向待移除的上一个列表项。(因为这个列表项我们要从列表中移除,所以列表不应该还能指向它。)
  4. 将待移除的列表项中的列表信息情况。(这样这个列表项就和列表没有任何关系了。)
  5. 更新列表中列表项的数目。
  6. 返回移除列表项后,列表中剩余列表项的数目。
    在这里插入图片描述
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
	/* 从列表项的pvContainer中获取列表项所在的列表 */
	List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;

	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->pvContainer = NULL;
	/* 列表的列表项数目-1 */
	( pxList->uxNumberOfItems )--;
	
	return pxList->uxNumberOfItems;
}
  • 30
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
FreeRTOS中封装一个文件为.a文件,可以按照以下步骤进行: 1.将需要封装的文件复制到一个单独的目录中,以便于管理。 2.在该目录下,创建一个名为Makefile的文件,并在其中定义编译选项和链接选项。 3.定义编译选项和链接选项时,需要使用arm-none-eabi-前缀的编译器和链接器。 4.在Makefile中定义编译和链接规则,确保生成的.a文件包含所有必需的符号和函数。 5.运行make命令,生成.a文件。 以下是一个简单的Makefile示例,将文件my_file.c封装为my_file.a: ``` TARGET=my_file.a CC=arm-none-eabi-gcc AR=arm-none-eabi-ar CFLAGS=-c -Wall -O2 LFLAGS= OBJS=my_file.o all: $(TARGET) $(TARGET): $(OBJS) $(AR) rcs $@ $^ %.o: %.c $(CC) $(CFLAGS) $< -o $@ clean: rm -f $(OBJS) $(TARGET) ``` 在这个示例中,我们首先定义了目标文件名(TARGET)、编译器(CC)和静态库打包程序(AR)。 然后,我们定义了编译选项(CFLAGS)和链接选项(LFLAGS)。 接下来,我们定义了一个目标all,它依赖于生成目标文件$(TARGET)。我们使用静态库打包程序$(AR)将目标文件打包为静态库。$@表示目标文件名,$^表示依赖文件列表。 接着,我们定义了一个模式规则,该规则使用编译器$(CC)编译所有.c文件,生成.o文件。$<表示第一个依赖文件名,$@表示目标文件名。 最后,我们定义了一个清理规则,用于删除所有生成的.o和.a文件

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值