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

本文详细解析了FreeRTOS操作系统中的链表数据结构,包括xLIST_ITEM和xMINI_LIST_ITEM两种链表节点的区别,以及链表组织结构xLIST的组成。通过初始化函数vListInitialise展示了链表如何形成循环链表并进行节点的插入与删除。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链表是操作系统中常用的数据结构,其结构比较简单,因此在剖析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的前面。< p="">

至于移除的话就是很普通的链表操作,注意一下链表指针没有指错,以及保持链表为循环链表就行了。根据代码注释的提示说明,感觉这个链表的管理只是用于为TCB服务,具体要这样管理的原因估计也要到后面的分析才能知道。这里就暂时只说明链表是这样管理就算了。

总结:偷懒了好久才把这个list部分剖析完,真是罪过罪过。之后要再好好加把劲才行,不能再这样懒下去了。不过从这里开始,感觉只用代码来说明真的非常困难,所以后来才想用画图的方式来解释,这样说明代码还是挺好的,估计后面会涉及更多的算法等,也会用画图的方法来说明。好方法,要好好记住。

stm32f407+lwip+FREERTOS+长连接,最后锁死问题,锁死到一下函数 void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ) { ListItem_t *pxIterator; const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; /* 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 the new list item into the list, sorted in xItemValue order. If the list already contains a list item with the same item value then the new list item should be placed after it. This ensures that TCB's which are stored in ready lists (all of which have the same xItemValue value) get a share of the CPU. However, if the xItemValue is the same as the back marker the iteration loop below will not end. Therefore the value is checked first, and the algorithm slightly modified if necessary. */ if( xValueOfInsertion == portMAX_DELAY ) { pxIterator = pxList->xListEnd.pxPrevious; } else { /* *** NOTE *********************************************************** If you find your application is crashing here then likely causes are listed below. In addition see http://www.freertos.org/FAQHelp.html for more tips, and ensure configASSERT() is defined! http://www.freertos.org/a00110.html#configASSERT 1) Stack overflow - see http://www.freertos.org/Stacks-and-stack-overflow-checking.html 2) Incorrect interrupt priority assignment, especially on Cortex-M parts where numerically high priority values denote low actual interrupt priorities, which can seem counter intuitive. See http://www.freertos.org/RTOS-Cortex-M3-M4.html and the definition of configMAX_SYSCALL_INTERRUPT_PRIORITY on http://www.freertos.org/a00110.html 3) Calling an API function from within a critical section or when the scheduler is suspended, or calling an API function that does not end in "FromISR" from an interrupt. 4) Using a queue or semaphore before it has been initialised or before the scheduler has been started (are interrupts firing before vTaskStartScheduler() has been called?). **********************************************************************/ 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 )++; }在这个函数中锁死了,程序不在运行,打断点也不会进入中断,但是点stop会停在以下这个判断里 if( xValueOfInsertion == portMAX_DELAY ) { pxIterator = pxList->xListEnd.pxPrevious; }
最新发布
08-05
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值