1. 程式人生 > 實用技巧 >FreeRTOS List簡易分析

FreeRTOS List簡易分析

FreeRTOS從簡單的List入手

  • 前言:開啟List.c檔案來看,200多行,並不是很多,詳細看內容,其實就是一個雙向連結串列的增和刪

1.結構體List_t

typedef struct xLIST  
{  
   listFIRST_LIST_INTEGRITY_CHECK_VALUE		/*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */  
   configLIST_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; 
  • 能力有限只能挑幾個來講C
變數名 作用
listFIRST_LIST_INTEGRITY_CHECK_VALUE 檢查資料是否完整
uxNumberOfItems 掛接在這個連結串列的連結串列項數目
xListEnd 指向連結串列的尾部
我個人理解這個**MiniListItem_t xListEnd**,其實它就是mini的ListItem,僅僅是因為要節省記憶體,把它作為尾結點。 MiniListItem.xItemValue在32位的平臺下,它的值預設為0xffffffff

2.結構體xLIST_ITEM

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 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). */
	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
pvContainer 指向屬於的連結串列

正如我開頭所說,結構體ListItem就是連結串列結點,結構體List就是連結串列,ListItem鏈到List上。

3.連結串列的操作函式

3.1連結串列的初始化

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;

	/* 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 );
}
->連結串列的初始化流程
  1. pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ) 指向尾結點
  2. pxList->xListEnd.xItemValue = portMAX_DELAY在32位架構下 portMAX_DELAY為0xFFFFFFFF
  3. pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ) next指向尾結點
  4. pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd ) previous指向尾結點
  5. pxList->uxNumberOfItems = ( UBaseType_t ) 0U 設定當前掛載結點為0

3.2連結串列項的初始化

void vListInitialiseItem( ListItem_t * const pxItem )
{
	/* Make sure the list item is not recorded as being on a list. */
	pxItem->pvContainer = NULL;

	/* Write known values into the list item if
	configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
	listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
	listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
->連結串列項的初始化流程

只需要pxItem->pvContainer = NULL,把這個連結串列項指向的連結串列置空即可 ,表示連結串列項不屬於任何連結串列


3.3向連結串列增加連結串列項:

3.3.1. 尾插
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t * const pxIndex = pxList->pxIndex;

	/* 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 a new list item into pxList, but rather than sort the list,
	makes the new list item the last item to be removed by a call to
	listGET_OWNER_OF_NEXT_ENTRY(). */
	pxNewListItem->pxNext = pxIndex;
	pxNewListItem->pxPrevious = pxIndex->pxPrevious;

	/* Only used during decision coverage testing. */
	mtCOVERAGE_TEST_DELAY();

	pxIndex->pxPrevious->pxNext = pxNewListItem;
	pxIndex->pxPrevious = pxNewListItem;

	/* Remember which list the item is in. */
	pxNewListItem->pvContainer = ( void * ) pxList;

	( pxList->uxNumberOfItems )++;
}
->尾插流程
  1. ListItem_t * const pxIndex = pxList->pxIndex 獲取最後一個結點
  2. pxNewListItem->pxNext = pxIndex 將新結點的next指向最後一個結點
  3. pxNewListItem->pxPrevious = pxIndex->pxPrevious 將新結點的previous指向最後結點的前一項
  4. pxIndex->pxPrevious->pxNext = pxNewListItem 將最後結點的next指向新結點
  5. pxIndex->pxPrevious = pxNewListItem 將最後結點的previous指向新結點
  6. pxNewListItem->pvContainer = ( void * ) pxList 新結點屬於pxList下的結點
  7. ( pxList->uxNumberOfItems )++ pxList結點的總數目加1
3.3.2.升序插入
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 )++;
}
->升序插入流程
  1. const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; 獲取插入的結點值

  2. for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) 遍歷到小於 xValueOfInsertion 的值

pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;

做的就是把新結點插入到遍歷後pxIterator的Next

  1. pxNewListItem->pvContainer = ( void * ) pxList 該結點屬於List

  2. List的結點數目加1


3.4 刪除連結串列項

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 = ( List_t * ) pxItemToRemove->pvContainer;

	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. */
	if( pxList->pxIndex == pxItemToRemove )
	{
		pxList->pxIndex = pxItemToRemove->pxPrevious;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

	pxItemToRemove->pvContainer = NULL;
	( pxList->uxNumberOfItems )--;

	return pxList->uxNumberOfItems;
}

->刪除流程
  1. List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer; 找到該連結串列項的所在連結串列

  2. pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
    pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
    

    A_Item -> B_Item -> C_Item,假設B為要刪除的Item,結果為A_Item - > CItem

  3. if( pxList->pxIndex == pxItemToRemove )
    {
    	pxList->pxIndex = pxItemToRemove->pxPrevious;
    }
    

    如果該Item為最後,A_Item -> Last_Item, 那麼A就會成為Lash_Item

  4. pxItemToRemove->pvContainer = NULL;
    ( pxList->uxNumberOfItems )--;
    

    該Item不屬於任何List,並把原List的Item數目減1