FreeRTOS里的数据结构主要就是列表和列表项。和任务关闭密切相关。
列表用来记录FreeRTOS中的任务。相关文件是list.c和list.h。代码量非常少。
List_t:
/*
* Definition of the type of queue used by the scheduler.
*/
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE /**< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
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;
listFIRST_LIST_INTEGRITY_CHECK_VALUElistSECOND_LIST_INTEGRITY_CHECK_VALUE用来检测列表完整性。
受configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES宏影响。只有宏打开才会生效。
#define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t x ListItemIntegrityValue1;
#define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t x ListItemIntegrityValue2;
TickType_t是整形。所以也就是向列表头尾插入一个数值。
uxNumberOfItems用来记录列表里有多少个列表项。
pxIndex记录当前列表项索引号。用于遍历列表使用。
xListEnd列表的最后一个列表项,用于表示列表的结尾。这是一个迷你列表项。
ListItem_t
列表里面是列表项。定义如下:
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 ascending 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. */
struct xLIST * configLIST_VOLATILE pxContainer; /**< 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. */
};
首尾也有检查项,和列表一样。
xItemValue:列表项的值。
pxNext:指向下一个列表
pxPrevious:指向上一个列表
pvOwner:表示列表项属于哪一个TCB。
pxContainer:记录列表项归于哪个列表。从类型就可以看出xLIST *,指向的是列表。
迷你列表项只是少了几个成员而已。用于不需要那么多成员的场景。
列表
初始化
函数实现:
void vListInitialise( List_t * const pxList )
{
traceENTER_vListInitialise( 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 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */
//检车列表完整性
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( &( 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. */
//因为此时列表还没有内容,所以下一个和上一个都执行xListEnd
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 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 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */
/* Initialize the remaining fields of xListEnd when it is a proper ListItem_t */
#if ( configUSE_MINI_LIST_ITEM == 0 )
{
pxList->xListEnd.pvOwner = NULL;
pxList->xListEnd.pxContainer = NULL;
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( &( pxList->xListEnd ) );
}
#endif
//列表项个数为0
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
/* Write known values into the list if
* configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
traceRETURN_vListInitialise();
}
插入
void vListInsert( List_t * const pxList,
ListItem_t * const pxNewListItem )
{
ListItem_t * pxIterator;
//要插入列表项的列表值。
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
traceENTER_vListInsert( pxList, pxNewListItem );
/* 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 TCBs 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. */
//正常来说只有xListEnd值才会使portMAX_DELAY.假设列表项的值也是最大值,那么就放到xListEnd的前面。因为xListEnd肯定是最后一项。
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 https://www.FreeRTOS.org/FAQHelp.html for
* more tips, and ensure configASSERT() is defined!
* https://www.FreeRTOS.org/a00110.html#configASSERT
*
* 1) Stack overflow -
* see https://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
* https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html and the definition
* of configMAX_SYSCALL_INTERRUPT_PRIORITY on
* https://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?).
* 5) If the FreeRTOS port supports interrupt nesting then ensure that
* the priority of the tick interrupt is at or below
* configMAX_SYSCALL_INTERRUPT_PRIORITY.
**********************************************************************/
//从列表的尾部开始查询,一直找到要插入的列表值需要插入的位置。升序排序。
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. *//*lint !e440 The iterator moves to a different value, not xValueOfInsertion. */
{
/* 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. */
//列表项的container执行该列表
pxNewListItem->pxContainer = pxList;
//列表项个数加1
( pxList->uxNumberOfItems )++;
traceRETURN_vListInsert();
}
需要注意的是此时列表的pxIndex还是指向xListEnd。
vListInsertEnd()是向列表的尾部插入,注意尾部不是xListEnd,而是根据pxIndex。因为默认pxIndex是指向xListEnd的。假设pxIndex不是指向xListEnd了,那么就在pxIndex指向的而列表前插入。
删除
列表项的删除函数参数只有列表项,不需要列表的原因是:列表项里的pxContainer指向的是列表。
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 = pxItemToRemove->pxContainer;
traceENTER_uxListRemove( pxItemToRemove );
//移除列表
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
/* Only used during decision coverage testing. */
mtCOVERAGE_TEST_DELAY();
/* Make sure the index is left pointing to a valid item. */
//假设移除的是列表里的pxIndex,那么要对pxIndex重新赋值,使用删除列表项的前一个。
if( pxList->pxIndex == pxItemToRemove )
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//列表项不属于任何列表了。
pxItemToRemove->pxContainer = NULL;
//列表项个数减1
( pxList->uxNumberOfItems )--;
traceRETURN_uxListRemove( pxList->uxNumberOfItems );
return pxList->uxNumberOfItems;
}
遍历
遍历是list.h里的一个宏。
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \
do { \
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 )->xListEnd.pxNext; \
} \
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \
} while( 0 )
返回pxIndex指向的owner,这里也要判断是不是xListEnd,不能返回xListEnd。
然后返回列表项的pvOwner。上面说过pcOwner一般指向TCB,所以宏的第一个参数就是TCB。
这个函数的作用就是从多个相同优先级的任务中寻找下一个要执行的任务。