FreeRTOS源码分析——列表与列表项
列表
列表是 FreeRTOS 中的一个数据结构,概念上和双向链表有点类似,列表被用来跟踪 FreeRTOS中的任务。
#结构体list_t
声明:
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE
configLIST_VOLATILE UBaseType_t uxNumberOfItems;
ListItem_t * configLIST_VOLATILE pxIndex;
MiniListItem_t xListEnd;
listSECOND_LIST_INTEGRITY_CHECK_VALUE
} List_t;
参数:
listFIRST_LIST_INTEGRITY_CHECK_VALUE | 完整性检查(不使用) |
---|---|
uxNumberOfItems | 列表中列表项的数量 |
pxIndex | 当前列表的索引 |
xListEnd | 列表中最后一个列表项,用来表示列表的结束 |
listSECOND_LIST_INTEGRITY_CHECK_VALUE | 完整性检查(不使用) |
列表项
#结构体ListItem_t
声明:
struct xLIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE
configLIST_VOLATILE TickType_t xItemValue;
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
void * pvOwner;
void * configLIST_VOLATILE pvContainer;
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE
};
typedef struct xLIST_ITEM ListItem_t;
参数:
xItemValue | 列表项值 |
---|---|
pxNext | 指向下一个列表项的指针 |
pxPrevious | 指向上一个列表项的指针 |
pvOwner | 记录此链表项归谁拥有,通常是任务控制块 |
pvContainer | 用来记录此列表项归哪个列表 |
注意:列表项的大部分参数要根据实际使用情况来初始化
pvOwner和pvContainer的区别?
任务控制块 TCB_t 中有两个变量 xStateListItem 和 xEventListItem,这两个变量的类型就是 ListItem_t,也就是说这两个成员变量都是列表项。以 xStateListItem 为例,当创建一个任务以后 xStateListItem 的 pvOwner 变量就指向这个任务的任务控制块,表示 xSateListItem属于此任务。当任务就绪态以后 xStateListItem 的变量 pvContainer 就指向就绪列表,表明此列表项在就绪列表中。
#结构体MiniListItem_t
声明:
struct xMINI_LIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE
configLIST_VOLATILE TickType_t xItemValue;
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
为什么需要Mini列表项呢?
有些情况下我们不需要列表项这么全的功能,可能只需要其中的某几个成员变量,如果此时用列表项的话会造成内存浪费!
相关的函数源码
#列表项插入函数vListInsert()
插入的位置由列表项中成员变量xItemValue 来决定。 列表项的插入根据 xItemValue 的值按照升序的方式排列!
声明:
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
参数:
pxList | 列表项要插入的列表 |
---|---|
pxNewListItem | 要插入的列表项 |
源码:带中文注释
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. */
/* 如果列表项的值等于portMAX_DELAY,即列表项为最大值,则插入末尾 */
if( xValueOfInsertion == portMAX_DELAY )
{
pxIterator = pxList->xListEnd.pxPrevious;
}
else
{
/* 找到插入的位置(按xItemValue升序排列) */
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 )++;
}
注意:如果调试时程序崩溃在找插入点的for循环附近,可能的问题是:
- 栈溢出
- 中断优先级分配不正确
- 从临界区内或在调度程序挂起时调用API函数,或者从中断调用不以“FromISR”结尾的API函数
- 在初始化队列或信号量之前或在调度程序启动之前使用队列或信号量(是否在调用vTaskStartScheduler()之前触发中断?)