FreeRTOS_列表和列表项

内容主要来源于正点原子的FreeRTOS开发手册
本文主要是做笔记,以及日后复习



1 什么是列表和列表项

1.1 列表

列表是FreeRTOS中的一种数据类型,和数据结构中的链表有一些相似。列表用来跟踪FreeRTOS的任务。(在list.h中定义了

列表的数据类型结构:

/*
 * Definition of the type of queue used by the scheduler.
 */
typedef struct xLIST
{
	listFIRST_LIST_INTEGRITY_CHECK_VALUE				/*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
	configLIST_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;


变量描述
uxNumberOfItems用来记录列表项的数量
pxIndex用来记录当前的列表项索引号
xListEnd列表的最后一个列表项,用来表示列表的结束。

其他的两个结构体成员不用管,用来检查列表完整性。所以一个列表如下图所示。
在这里插入图片描述

1.2 列表项

列表项就是存放在列表中。FreeRTOS提供了两种列表项:列表项以及迷你列表项(在list.h中定义了

1.2.1 列表项

/*
 * Definition of the only type of object that a list can contain.
 */
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. */

变量描述
xItemValue列表项值
pxNext指向下一个列表项
pxPrevious指向那个上一个列表项(和pxNext配合,可以构成双向链表)
pvOwner记录此列表项归谁所有(一般是任务控制块)
pvContainer记录此列表项归哪个列表所有 ( pvcontainer用来记录此列表项归哪个列表。注意和 pvowner 的区别,在前面讲解任务控制块TCBt的时候说了在TCBt中有两个变量 x Statelistitem和 Xeventlistitem,这两个变量的类型就是 Listitem_t,也就是说这两个成员变量都是列表项。以 xstate Listitem为例,当创建个任务以后 xstate Listitem的 pvowner变量就指向这个任务的任务控制块,表示 x Satelistltem属于此任务。当任务就绪态以后 Xstate Listitem的变量 container就指向就绪列表,表明此列表项在就绪列表中。

所以一个列表项如下图所示。

在这里插入图片描述

1.2.2 迷你列表项

列表项有些功能在实际开发中可能不需要,所以为了避免内存浪费,有了迷你列表项

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;
变量描述
xItemValue列表项值
pxNext指向下一个列表项
pxPrevious指向上一个列表项

所以一个迷你列表项如下图所示。
在这里插入图片描述

2 列表和列表项的初始化

2.1 列表初始化

在list.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 );			/*lint !e826 !e740 The mini list structure is used as the list end to save RAM.  This is checked and valid. */

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

	/* 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 );	/*lint !e826 !e740 The mini list structure is used as the list end to save RAM.  This is checked and valid. */
	pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );/*lint !e826 !e740 The mini list structure is used as the list end to save RAM.  This is checked and valid. */

	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 列表项初始化

void vListInitialiseItem( ListItem_t * const pxItem )
{
	/* Make sure the list item is not recorded as being on a list. */
	pxItem->pvContainer = NULL;

	/* Write known values into the list item if
	configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
	listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
	listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}

3 列表项的插入

void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
  • pxList:列表项要插入的列表
  • pxNewListItem:要插入的列表项

该函数是通过升序插入的,所以插入位置由列表项的xItemValue决定


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

		/*将新列表项插入到按xItemValue顺序排序的列表中。  
		
		如果列表已经包含具有相同项值的列表项,则  
		新的列表项应该放在它之后。 这确保了TCB的  
		存储在就绪列表中(所有这些列表都具有相同的xItemValue值)  
		CPU的共享。 但是,如果xItemValue与后置标记相同  
		下面的迭代循环不会结束。 因此该值被检查  
		首先,如果有必要,对算法进行了稍微的修改。 */  
	if( xValueOfInsertion == portMAX_DELAY )
	{
		pxIterator = pxList->xListEnd.pxPrevious;
	}
	else
	{

		for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 The mini list structure is used as the list end to save RAM.  This is checked and valid. */
		{
			/* 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->pvContainer = ( void * ) pxList;

	( pxList->uxNumberOfItems )++;
}

3.1 列表项插入图示

1、插入值为40的列表项

在这里插入图片描述

  • 列表List中的uxNumberofItems变为了1(表示列表中有一个列表项)
  • 列表项中pvContainer变成了List(表示此列表项属于列表List)
  • 从图中可以看出该裂变属于环形列表

2、插入值为60的列表项

在这里插入图片描述

  • 通过升序插入的,所以ListItem2插入到ListItem1后面,又在xLitEnd前面
  • 同样列表List中的uxNumberofItems变为了2
  • 列表项中pvContainer变成了List(表示此列表项属于列表List)

3、插入值为50的列表项

在这里插入图片描述
按照升序方式应该是在40和60中间

4 列表项末尾插入


void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t * const pxIndex = pxList->pxIndex;

	/* Only effective when configASSERT() is also defined, these tests may catch
	the list data structures being overwritten in memory.  They will not catch
	data errors caused by incorrect configuration or use of FreeRTOS. */
	listTEST_LIST_INTEGRITY( pxList );
	listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );

	/* Insert a new list item into pxList, but rather than sort the list,
	makes the new list item the last item to be removed by a call to
	listGET_OWNER_OF_NEXT_ENTRY(). */
	pxNewListItem->pxNext = pxIndex;
	pxNewListItem->pxPrevious = pxIndex->pxPrevious;

	/* Only used during decision coverage testing. */
	mtCOVERAGE_TEST_DELAY();

	pxIndex->pxPrevious->pxNext = pxNewListItem;
	pxIndex->pxPrevious = pxNewListItem;

	/* Remember which list the item is in. */
	pxNewListItem->pvContainer = ( void * ) pxList;

	( pxList->uxNumberOfItems )++;
}

默认列表

在这里插入图片描述

插入值为50的列表项

在这里插入图片描述

5 列表项的删除

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/* The list item knows which list it is in.  Obtain the list from the list
item. */
List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;1)

	pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;2)
	pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;3/* Only used during decision coverage testing. */
	mtCOVERAGE_TEST_DELAY();

	/* Make sure the index is left pointing to a valid item. */
	if( pxList->pxIndex == pxItemToRemove )
	{
		pxList->pxIndex = pxItemToRemove->pxPrevious;4}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

	pxItemToRemove->pvContainer = NULL;5( pxList->uxNumberOfItems )--;								

	return pxList->uxNumberOfItems;6}

在这里看源码,最好结合上面的图示

  • (1):通过列表项的pvContainer可以知道他属于哪个列表
  • (2):将要删除的节点的pxNext(就是要删除节点的下一个节点),将这个节点的pxPrevious指向 要删除这个节点的pxPrevious
  • (3)将要删除的这个节点的前一个节点的pxNext指向要删除的这个节点的pxNext
  • (2)和(3)就是将要删除的这个节点的前后节点连接在一起。
  • (4)如果列表的pxIndex指向要删除的这个列表项,那么在删除之后就要给pxIndex重新分配列表项,这个新的列表项就是被删除这个列表项的前一个列表项。
  • (5)将被删除的这个列表项的pvContainer(指向前一个列表项的指针)
  • (6)返回列表中列表项数目

6 和列表项的遍历

介绍列表结构体的时候说过用列表 List_t中的成员变量 pindex是用来遍历列表的, Freertos提供了一个函数来完成列表的,这个函数是listGET_OWNER_OF_NEXT_ENTRY()。每调用一次这个函数列表的 pandex变量就会指向下一个列表项,并且返回这个列表项的 towner变量值。这个函数本质上是一个宏,这个宏在文件 list. h中如下定义:

#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )										\(1
{																							\
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;							\(2if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) )	\(3{																						\
		( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;						\(4}																						\
	( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;											\(5}
  • (1)pxTCB用来保存 pindex所指向的列表项的 pvowner变量值,也就是这个列表项属于谁的?通常是一个任务的任务控制块。 plist表示要遍历的列表
  • (2)列表的 pindex变量指向下一个列表项
  • (3)如果 pindex指向了列表的 xlistend成员变量,表示到了列表末尾
  • (4)如果到了列表末尾的话就跳过 xListEnd, pxIndex再一次重新指向处于列表头的列表项,这样就完成了一次对列表的遍历
  • (5)将 pindex所指向的新列表项的pvOwner赋值给 PXTCB。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值