写在开始
freertos中的任务涉及到3个很重要的结构体,也就是struct xLIST,struct xMINI_LIST_ITEM与struct xLIST_ITEM。
为什么会形成类似struct xLIST这种结构呢?
由于它需要计算item的个数,所以它首先要包含uxNumberOfItems,另外它需要指向item的位置,所以有一个ListItem_t * pxIndex。这样就形成了struct xLIST。这是第一步,称为结构体A。
而需要定义一个全局变量,struct xMINI_LIST_ITEM,这个节点用来表示整个item的结束(作为全局变量的时候完全可以改写为struct xLIST_ITEM)。这样就相当于定义了一个所有内容都是struct xLIST_ITEM的双链表结构,其中只不过多了一个标志位结束的链表。这个链表由于与上面的A都是独立一份的,所以将这个struct xLIST_ITEM集成到A里面,而pvOwner和pxContainer是多余的,可以略去,所以就做了一个struct xMINI_LIST_ITEM结构体。
在freertos中,可以看到信号量,互斥量,邮箱也都是集中在一个结构体中,这都是freertos的内存节省的操作。
确定了这些,那么初始化struct xLIST就好理解了(将struct xMINI_LIST_ITEM拆开)。
代码分析
初始化代码如下:
<code>void vListInitialise( List_t * const pxList )
{
\t/* The list structure contains a list item which is used to mark the
\tend of the list. To initialise the list the list end is inserted
\tas the only list entry. */
\tpxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );\t\t\t/*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */
\t/* The list end value is the highest possible value in the list to
\tensure it remains at the end of the list. */
\tpxList->xListEnd.xItemValue = portMAX_DELAY;
\t/* The list end next and previous pointers point to itself so we know
\twhen the list is empty. */
\tpxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );\t/*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */
\tpxList->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. */
\tpxList->uxNumberOfItems = ( UBaseType_t ) 0U;
\t/* Write known values into the list if
\tconfigUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
\tlistSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
\tlistSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}/<code>
对于单个的item,在程序需要将它插入list之前会重新做初始化,所以在程序运行后,执行的初始化很简单:
<code>void vListInitialiseItem( ListItem_t * const pxItem )
{
\t/* Make sure the list item is not recorded as being on a list. */
\tpxItem->pxContainer = NULL;
\t/* Write known values into the list item if
\tconfigUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
\tlistSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
\tlistSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}/<code>
建立了链表结构,就是为了增删改查的。那么插入链表分为从尾部插入与从中间插入。现在定义的结构由于相当于定义了头(list),所以分为从头之后插入与在头的最后一个位置插入两种情况。
先说从尾部插入。
exist是假想的一个链表,这样理解尾部插入的代码就会很容易。
<code>void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t * const pxIndex = pxList->pxIndex;
\t/* Only effective when configASSERT() is also defined, these tests may catch
\tthe list data structures being overwritten in memory. They will not catch
\tdata errors caused by incorrect configuration or use of FreeRTOS. */
\tlistTEST_LIST_INTEGRITY( pxList );
\tlistTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
\t/* Insert a new list item into pxList, but rather than sort the list,
\tmakes the new list item the last item to be removed by a call to
\tlistGET_OWNER_OF_NEXT_ENTRY(). */
\tpxNewListItem->pxNext = pxIndex;
\tpxNewListItem->pxPrevious = pxIndex->pxPrevious;
\t/* Only used during decision coverage testing. */
\tmtCOVERAGE_TEST_DELAY();
\tpxIndex->pxPrevious->pxNext = pxNewListItem;
\tpxIndex->pxPrevious = pxNewListItem;
\t/* Remember which list the item is in. */
\tpxNewListItem->pxContainer = pxList;
\t( pxList->uxNumberOfItems )++;
}/<code>
可以看到,在链表最后插入的代码里,会将pxContainer值赋为这个指定的pxList。最后会将item的个数增加。
再说一下从pxList后插入。
其代码为:
<code>void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t *pxIterator;
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
\t/* Only effective when configASSERT() is also defined, these tests may catch
\tthe list data structures being overwritten in memory. They will not catch
\tdata errors caused by incorrect configuration or use of FreeRTOS. */
\tlistTEST_LIST_INTEGRITY( pxList );
\tlistTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
\t/* Insert the new list item into the list, sorted in xItemValue order.
\tIf the list already contains a list item with the same item value then the
\tnew list item should be placed after it. This ensures that TCBs which are
\tstored in ready lists (all of which have the same xItemValue value) get a
\tshare of the CPU. However, if the xItemValue is the same as the back marker
\tthe iteration loop below will not end. Therefore the value is checked
\tfirst, and the algorithm slightly modified if necessary. */
\tif( xValueOfInsertion == portMAX_DELAY )
\t{
\t\tpxIterator = pxList->xListEnd.pxPrevious;
\t}
\telse
\t{
\t\t/* *** NOTE ***********************************************************
\t\tIf you find your application is crashing here then likely causes are
\t\tlisted below. In addition see https://www.freertos.org/FAQHelp.html for
\t\tmore tips, and ensure configASSERT() is defined!
\t\thttps://www.freertos.org/a00110.html#configASSERT
\t\t\t1) Stack overflow -
\t\t\t see https://www.freertos.org/Stacks-and-stack-overflow-checking.html
\t\t\t2) Incorrect interrupt priority assignment, especially on Cortex-M
\t\t\t parts where numerically high priority values denote low actual
\t\t\t interrupt priorities, which can seem counter intuitive. See
\t\t\t https://www.freertos.org/RTOS-Cortex-M3-M4.html and the definition
\t\t\t of configMAX_SYSCALL_INTERRUPT_PRIORITY on
\t\t\t https://www.freertos.org/a00110.html
\t\t\t3) Calling an API function from within a critical section or when
\t\t\t the scheduler is suspended, or calling an API function that does
\t\t\t not end in "FromISR" from an interrupt.
\t\t\t4) Using a queue or semaphore before it has been initialised or
\t\t\t before the scheduler has been started (are interrupts firing
\t\t\t before vTaskStartScheduler() has been called?).
\t\t**********************************************************************/
\t\tfor( 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. */
\t\t{
\t\t\t/* There is nothing to do here, just iterating to the wanted
\t\t\tinsertion position. */
\t\t}
\t}
\tpxNewListItem->pxNext = pxIterator->pxNext;
\tpxNewListItem->pxNext->pxPrevious = pxNewListItem;
\tpxNewListItem->pxPrevious = pxIterator;
\tpxIterator->pxNext = pxNewListItem;
\t/* Remember which list the item is in. This allows fast removal of the
\titem later. */
\tpxNewListItem->pxContainer = pxList;
\t( pxList->uxNumberOfItems )++;
}/<code>
从pxList插入涉及到在哪个链表位置插入,哪个位置则可以引用结构体中的xItemValue,利用这个值来进行排序插入。
在值为portMAX_DELAY的时候,那就在链表的末尾插入,这个前面说过。而从中间插入跟最后插入其实代码的区别不大。
再说一下链表的删除。
删除主要就是将链表中某一项去掉的过程。需要注意的是如果删除的是 pxList->pxIndex,那则需要将指针移动到前一个链表。代码如下:
<code>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;
\tpxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
\tpxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
\t/* Only used during decision coverage testing. */
\tmtCOVERAGE_TEST_DELAY();
\t/* Make sure the index is left pointing to a valid item. */
\tif( pxList->pxIndex == pxItemToRemove )
\t{
\t\tpxList->pxIndex = pxItemToRemove->pxPrevious;
\t}
\telse
\t{
\t\tmtCOVERAGE_TEST_MARKER();
\t}
\tpxItemToRemove->pxContainer = NULL;
\t( pxList->uxNumberOfItems )--;
\treturn pxList->uxNumberOfItems;
}/<code>
閱讀更多 wityuan 的文章