这一篇之所以讲list的内容,是为了后面分析其他代码用的,list是FreeRTOS的一种链式数据结构,许多变量均采用这种数据结构,比如Ready任务表,pending任务表和delayed任务表等。这种数据结构在FreeRTOS中被广泛使用,因此这里先分析这种数据结构的组成与实现原理。
首先看list.h这个文件,这个文件一开头就有一个宏定义
#ifndef configLIST_VOLATILE
#define configLIST_VOLATILE
#endif /* configSUPPORT_CROSS_MODULE_OPTIMISATION */
这里是判断configLIST_VOLATILE是否被定义,没有的话就定义为空,Demo默认是没定义的,所以后面出现这个宏的时候,是什么都不做的。这个宏如果被定义的话一般是被定义为
#define configLIST_VOLATILE volatile
之后又是一大块的宏定义:
#if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 0 )
/* Define the macros to do nothing. */
#define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE
#define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE
#define listFIRST_LIST_INTEGRITY_CHECK_VALUE
#define listSECOND_LIST_INTEGRITY_CHECK_VALUE
#define listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem )
#define listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem )
#define listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList )
#define listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList )
#define listTEST_LIST_ITEM_INTEGRITY( pxItem )
#define listTEST_LIST_INTEGRITY( pxList )
#else
/* Define macros that add new members into the list structures. */
#define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue1;
#define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue2;
#define listFIRST_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue1;
#define listSECOND_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue2;
/* Define macros that set the new structure members to known values. */
#define listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) ( pxItem )->xListItemIntegrityValue1 = pdINTEGRITY_CHECK_VALUE
#define listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) ( pxItem )->xListItemIntegrityValue2 = pdINTEGRITY_CHECK_VALUE
#define listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList ) ( pxList )->xListIntegrityValue1 = pdINTEGRITY_CHECK_VALUE
#define listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList ) ( pxList )->xListIntegrityValue2 = pdINTEGRITY_CHECK_VALUE
/* Define macros that will assert if one of the structure members does not
contain its expected value. */
#define listTEST_LIST_ITEM_INTEGRITY( pxItem ) configASSERT( ( ( pxItem )->xListItemIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxItem )->xListItemIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) )
#define listTEST_LIST_INTEGRITY( pxList ) configASSERT( ( ( pxList )->xListIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxList )->xListIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) )
#endif /* configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES */
因为configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 在Demo里也是默认的设置为0,这样实际这一连串的宏都是空的,不起作用。为简单起见,这里用默认设置,先不管这些各种Check宏,以后熟悉后需要时再分析即可。
/*
* 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. */
接下来就是有关xLIST_ITEM这个数据类型的定义了,这是一个结构体,注意最顶上的那一句话,这个数据类型是list数据结构里唯一存在的类型。开头与结尾的宏定在前面说过了是空的,无视掉,然后里面的configLIST_VOLATILE也是空的,也可以无视。通过旁边的英文写的注释也可以知道,第一个成员xItemValue是代表的是这个Item的值,一般这个Item在list里的位置,会由这个值来排序。以DelayedTaskList为例子,在这个list里,每一个Item代表一个被延迟的任务的控制块所对应的Item,其Item_Value则代表的就是delay的值了,以tick为单位。这个list为以这个Delay值作降序排列,越小的会越在后面。pxNext和pxPrevious都是Item的指针,看名字也知道分别代表指向同一list中下一个Item的指针和指向上一个Item的指针。pvOwner是反向指向这个Item所代表的目标,比如任务控制块(TCB)。最后的pvContainer则是指向这个Item所在的list。
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;
这个MiniItem其实就是一个精简的Item,去掉了pvOwner和pvContainer,一般用在list里的最后一个特殊的Item。
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;
再来就是list的结构体定义了,同样去除无效的宏之后,就只有三个成员,uxNumberOfItems,代表这个list的所有Item的数量(不包括list的最后一个特殊的Item);pxIndex是指向list这个列表里当前的Item,相当于数组里的下标;xListEnd是Mini_Item,这个是每个list都会有的一个特殊的Item,而且总是存在与list的末尾,并且这个endItem的下一个nextItem会指向list的首部第一个Item,这样就形成了一个环形list。
list.h文件下面的除了是函数申明之外就是一系列用宏来实现的功能函数,这些函数比较简单,用宏来实现也很容易,并且执行起来比函数要来得快。每一个在上方都有用英文写出注释,这里基本只用简单翻译一下就懂了。
#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) )
#define listGET_LIST_ITEM_OWNER( pxListItem ) ( ( pxListItem )->pvOwner )
这两个宏,一个是用来设置Item的所属Owner的,之前也说过,一般是指某个任务控制块TCB。另外一个看名字就知道是反过来获得Item所属的Owner指针的。
#define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( ( pxListItem )->xItemValue = ( xValue ) )
#define listGET_LIST_ITEM_VALUE( pxListItem ) ( ( pxListItem )->xItemValue )
这两个宏,一个是设置Item的xItemValue值的,一个是获取这个值的。
#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext->xItemValue )
#define listGET_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext )
这两个宏,下面的那个是获取list的首部Item,上面的则是获取这个首部Item所对应的Value值,实现方法很简单就是获取list的尾部那个特殊的Item的下一个,前面也说过尾部那个特殊的Item的下一个就是指向list的首部。
#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 )
这几个宏的功能看名字也知道,实现也非常简单,分别是获取Item的下一个指向,list的末尾特殊Item,通过list的uxNumberOfItems是否为0来判断list是否为空(注意,每个list都含有一个末尾特殊Item,这个是不包含在这个Number里的),以及获得这个Number值。
#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; \
}
这个宏要稍微复杂点,获取list的下一个Item,并且把这个Item所指向的Owner赋值给参数pxTCB,也就是说这个宏的主要作用是获取list的下一个Item的所属TCB,同时还有做了一件事,就是给Index重赋值。看实现代码可知道,list成员Index的下一个指向就是list里的下一个Item了,当调用这个宏函数来获取下一个Item时,会先把Index重赋值为下一个指向,然后再判断这个新指向是否是list里的末尾特殊Item,如果是的话再重新赋值一遍到下一个指向,这时得到的会是list的首部Item。然后把这个Item对应的Owner赋值给参数pxTCB。
#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 )
剩下的这四个宏,也是很简单的实现,看名字也能知道用途,简单说下,分别是 得到list的首部Item的所属(TCB),指定Item是否在指定list里,Item的所在list以及list是否被初始化(这个判断是看list的末尾特殊Item的value是否是portMAX_DELAY,这个在list初始化时会指定否则一般会是0)。
下面几行就是list.c里定义的几个实现函数的申明了,这在下一篇分析。