FreeRTOS学习笔记——链表

主机环境:Windows

开发环境:MDK4.7.2

FreeRTOS版本:FreeRTOS8.1.2

目标环境:STM32F030C8T6

最近打算学习一下FreeRTOS的知识,在此作下笔记以便帮助自己理解,FreeRTOS与uCos-ii不同,在查询最高优先级时不是采用查表法来得知,而是通过链表来获取,因此链表在FreeRTOS中是比较重要的一个数据结构,因此先学习下FreeRTOS中有关链表的操作,查看源代码发现链表的代码貌似很精简,有三个结构分别是xLIST_ITEM、xMINI_LIST_ITEM、xLIST,其中xLIST_ITEM和xMINI_LIST_ITEM类似可以归为一类

/*
 * Definition of the only type of object that a list can contain.
 */
struct xLIST_ITEM
{
	configLIST_VOLATILE TickType_t xItemValue;			
	struct xLIST_ITEM * configLIST_VOLATILE pxNext;	
	struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;	
	void * pvOwner;										
	void * configLIST_VOLATILE pvContainer;				
};
typedef struct xLIST_ITEM ListItem_t;					

struct xMINI_LIST_ITEM
{
	configLIST_VOLATILE TickType_t xItemValue;
	struct xLIST_ITEM * configLIST_VOLATILE pxNext;
	struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;

/*
 * Definition of the type of queue used by the scheduler.
 */
typedef struct xLIST
{
	configLIST_VOLATILE UBaseType_t uxNumberOfItems;
	ListItem_t * configLIST_VOLATILE pxIndex;		
	MiniListItem_t xListEnd;						
} List_t;

xMINI_LIST_ITEM可以算是xLIST_ITEM的子类,两者可以相互转换,xLIST是我们需要关注的xLIST_ITEM和xMINI_LIST_ITEM都是基本结构都是有xLIST来管理,在list.h文件中以宏定义定义了一些链表的操作

#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner )		( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) )

#define listGET_LIST_ITEM_OWNER( pxListItem )	( ( pxListItem )->pvOwner )


#define listSET_LIST_ITEM_VALUE( pxListItem, xValue )	( ( pxListItem )->xItemValue = ( xValue ) )

#define listGET_LIST_ITEM_VALUE( pxListItem )	( ( pxListItem )->xItemValue )

#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList )	( ( ( pxList )->xListEnd ).pxNext->xItemValue )

#define listGET_HEAD_ENTRY( pxList )	( ( ( pxList )->xListEnd ).pxNext )

#define listGET_NEXT( pxListItem )	( ( pxListItem )->pxNext )

#define listGET_END_MARKER( pxList )	( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) )

#define listLIST_IS_EMPTY( pxList )	( ( BaseType_t ) ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) )

#define listCURRENT_LIST_LENGTH( pxList )	( ( pxList )->uxNumberOfItems )

#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;											\
}

#define listGET_OWNER_OF_HEAD_ENTRY( pxList )  ( (&( ( pxList )->xListEnd ))->pxNext->pvOwner )

#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( BaseType_t ) ( ( pxListItem )->pvContainer == ( void * ) ( pxList ) ) )


#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pvContainer )

#define listLIST_IS_INITIALISED( pxList ) ( ( pxList )->xListEnd.xItemValue == portMAX_DELAY )

void vListInitialise( List_t * const pxList );

void vListInitialiseItem( ListItem_t * const pxItem );

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

void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem );

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove );

这其中文档有提到xLIST_ITEM中pvOwner一般是指向TCB任务控制块的指针,而pvContainer一般是指向xLIST的指针,链表的排序都是以xItemValue为基准降序排列,xLIST中的xListEnd是链表的末尾,在链表中是不含有有效数据的,因此也不记录在uxNumberOfItems中整个链表的结构差不多是下面形式,关于xListEnd的处理可能不是很对

FreeRTOS学习笔记——链表_第1张图片

在list.c中还有五个链表的操作函数

1.链表初始化函数

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

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

	pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
}

链表在初始化时pxIndex指针是指向链表末尾即xListEnd结构且xListEnd.xItemValue的值为portMAX_DELAY是一个最大值,同时xListEnd.pxNext和xListEnd.pxPrevious都指向了自己,最后初始化uxNumberOfItems为0,即链表中没有元素xLIST是一个双向链表,知道结构后,对于它的操作就很容易理解了。xListEnd.pxNext是指向队首的,xListEnd.pxPrevious是指向队尾前一个元素。

2. 初始化队列中的元素

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

3.插入元素

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

	/* 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;
	pxIndex->pxPrevious->pxNext = pxNewListItem;
	pxIndex->pxPrevious = pxNewListItem;

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

	( pxList->uxNumberOfItems )++;
}
该函数在队列当前指针pxIndex之前插入了一个元素,同时设置了新元素的pvContainer属性指向所插入的链表pxList,同时更新链表中的元素数

4. 插入元素2

void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t *pxIterator;
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;

	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, we are 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 )++;
}

与之前的插入元素不同,该函数是按升序在链表中插入了新元素,用新插入元素的xItemValue依次与链表中的xItemValue比较,找到合适的位置插入该元素。

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;

	pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
	pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;

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

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

	return pxList->uxNumberOfItems;
}

由于所删除的元素中含有pvContainer属性并且该属性是有效的,因此在参数中不需要指定从哪个链表中删除该元素,需要指出的是在删除元素时如果链表的当前指针pxIndex指向了所要删除的元素,需要将该指针移向它前面一个元素,最后更新链表中的元素数。
在tasks.c中定义了几个链表

/* Lists for ready and blocked tasks. --------------------*/
PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];
PRIVILEGED_DATA static List_t xDelayedTaskList1;						
PRIVILEGED_DATA static List_t xDelayedTaskList2;						
PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList;				
PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList;		
PRIVILEGED_DATA static List_t xPendingReadyList;		

一个就绪链表数组,两个延时任务链表(一个用于正常延时,一个用于延时溢出),一个挂起的就绪任务链表,现在还不清楚两个延时任务链表的用法,关于就绪链表数组需要说明的是FreeRTOS的调度算法是机遇任务优先级调度的,总是运行就绪任务中优先级最高的任务,因此在FreeRTOS中按照优先级区分,为每一个优先级都建立了一个就绪链表,以便于查找就绪任务中优先级最高的任务。有关链表的知识先记录到此,有新东西再说吧。。。

PS:关于两个延时链表,FreeRTOS是有一个时钟计数器的,当任务需要延时,任务就会从就绪链表中移除转而进入延时链表中,由于任务延时完毕后还需要唤醒 ,因此需要一个唤醒时间,如果唤醒时间没有溢出那么任务就会插入pxDelayedTaskList指针指向的链表即xDelayedTaskList1链表,而如果唤醒时间溢出的话,那么任务就会插入pxOverflowDelayedTaskList指针指向的链表即xDelayedTaskList2,这个会在vTaskDelay()函数中提到。


你可能感兴趣的:(FreeRTOS学习笔记——链表)