您的位置:首页 > 编程语言

FreeRTOS代码剖析之5:链表管理list.c

2014-11-20 21:28 169 查看
链表是操作系统中常用的数据结构,其结构比较简单,因此在剖析FreeRTOS的内核时先从这里开始入手。
链表是由众多链表节点组成的,在FreeRTOS中,链表节点有两种定义,分别是xLIST_ITEM和xMINI_LIST_ITEM。这两种有什么区别呢?可以说,xMINI_LIST_ITEM是xLIST_ITEM的浓缩版。看看两者的定义。

struct xLIST_ITEM
{
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). */
};
typedef struct xLIST_ITEM ListItem_t;	/* For some reason lint wants this as two separate definitions. */

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;


首先看看xLIST_ITEM里的成员。
第一个成员是xItemValue,根据注释可以看出这个成员是存放链表节点的实际内容,是一个16位或32位的无符号整数,具体位数在portmacro.h里定义,同时这个成员可以用configLIST_VOLATILE修饰,这是一个类似于volatile的关键字,用户自己定义。
第二个成员pxNext和第三个成员pxPrevious是一个指针,分别指向当前节点的下一个和上一个节点。
第四个成员是pvOwner,根据注释说明,这是一个指向链表拥有者的指针,通常是指向TCB(Task Control Block)。
最后一个成员是pvContainer,根据注释说明,这是一个指向拥有当前节点的链表的指针。不过个人感觉这个成员挺别扭的。
而xMINI_LIST_ITEM则是只保留xLIST_ITEM中的前三个成员,其它的都去掉。
接下来要介绍的是链表组织结构xLIST。根据注释说明,这是给调度程序使用的链表定义。这个结构有三个成员。第一个是uxNumberOfItems,这是一个long类型整数,用于记录链表中的节点数目。第二个成员是pxIndex,是一个xLIST_ITEM类型的指针,用于遍历链表中的所有节点。最后一个成员是xListEnd,是一个xMINI_LIST_ITEM类型的数据,用于记录结束节点标志。
那实际上整条链表是如何组织的呢?可以看一下链表初始化函数vListInitialise.

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

从整段代码中可以看出,初始化后的链表是一个循环链表,以xListEnd为尾。整个链表只有一个节点(就是xListEnd)。而头节点则是xListEnd的下一个节点。另外,尾节点的节点值为portMAX_DELAY。

感觉再用代码和文字说明会比较吃力,还是上图描述比较好。可以说,整条链表是按以下方式进行管理的。首先是初始化状态,如下图所示:



P指针为前一节点指针,N为下一节点指针。接下来是插入操作。插入节点有两种类型,第一种是添加新节点到尾部。根据代码描述,这种类型的插入结果如下图所示。可以看出整条链表是一条循环链表,用链表中的xListEnd来指示链表结尾,并用来给出链表头入口。









上图的添加节点New4是属于第二种节点插入的情况,根据代码描述,是按值的顺序来决定插入位置的,只要遇到链表中的其中一个节点B的值比新节点A的值要大,那么A就插入到B的前面。例如,节点值排序为NEW2<NEW1<NEW4<NEW3,NEW4为新节点,那么NEW4就插入到NEW3的前面。
至于移除的话就是很普通的链表操作,注意一下链表指针没有指错,以及保持链表为循环链表就行了。根据代码注释的提示说明,感觉这个链表的管理只是用于为TCB服务,具体要这样管理的原因估计也要到后面的分析才能知道。这里就暂时只说明链表是这样管理就算了。

总结:偷懒了好久才把这个list部分剖析完,真是罪过罪过。之后要再好好加把劲才行,不能再这样懒下去了。不过从这里开始,感觉只用代码来说明真的非常困难,所以后来才想用画图的方式来解释,这样说明代码还是挺好的,估计后面会涉及更多的算法等,也会用画图的方法来说明。好方法,要好好记住。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: