本章將和大家分享Docker中如何實現數據的持久化。廢話不多說,下麵我們直接進入主題。 一、什麼是數據捲 我們都知道在Docker中,容器的數據讀寫預設發生在容器的存儲層,當容器被刪除時其上的數據將會丟失。如果想實現數據的持久化,就需要將容器和宿主機建立聯繫(將數據從宿主機掛載到容器內),通俗的說, ...
4.2.6 任務刪除--vTaskDelete
這個介面並不複雜,主要是在判斷是否要放到xTasksWaitingTermination列表裡,還是直接處理。
1 void vTaskDelete( TaskHandle_t xTaskToDelete ) 2 { 3 TCB_t * pxTCB; 4 BaseType_t xDeleteTCBInIdleTask = pdFALSE; 5 BaseType_t xTaskIsRunningOrYielding; 6 7 taskENTER_CRITICAL(); 8 { 9 /* If null is passed in here then it is the calling task that is 10 * being deleted. */ 11 /* 如果是NULL就是刪除自己,否則是刪除其他任務 */ 12 pxTCB = prvGetTCBFromHandle( xTaskToDelete ); 13 14 /* Remove task from the ready/delayed list. */ 15 if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) 16 { 17 taskRESET_READY_PRIORITY( pxTCB->uxPriority ); 18 } 19 else 20 { 21 mtCOVERAGE_TEST_MARKER(); 22 } 23 24 /* Is the task waiting on an event also? */ 25 if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) 26 { 27 ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); 28 } 29 else 30 { 31 mtCOVERAGE_TEST_MARKER(); 32 } 33 34 /* Increment the uxTaskNumber also so kernel aware debuggers can 35 * detect that the task lists need re-generating. This is done before 36 * portPRE_TASK_DELETE_HOOK() as in the Windows port that macro will 37 * not return. */ 38 /* 這個變數是用於TRACE相關的,調試用的,先不管,以後看trace的代碼再解析 */ 39 uxTaskNumber++; 40 41 /* Use temp variable as distinct sequence points for reading volatile 42 * variables prior to a logical operator to ensure compliance with 43 * MISRA C 2012 Rule 13.5. */ 44 /* 確定是否正在運行或正在調度到這個任務 */ 45 xTaskIsRunningOrYielding = taskTASK_IS_RUNNING_OR_SCHEDULED_TO_YIELD( pxTCB ); 46 47 /* If the task is running (or yielding), we must add it to the 48 * termination list so that an idle task can delete it when it is 49 * no longer running. */ 50 if( ( xSchedulerRunning != pdFALSE ) && ( xTaskIsRunningOrYielding != pdFALSE ) ) 51 { 52 /* A running task or a task which is scheduled to yield is being 53 * deleted. This cannot complete when the task is still running 54 * on a core, as a context switch to another task is required. 55 * Place the task in the termination list. The idle task will check 56 * the termination list and free up any memory allocated by the 57 * scheduler for the TCB and stack of the deleted task. */ 58 /* 看註釋,不放到終止列表中,無法刪除。需要依賴空閑任務來處理 59 * 具體原因暫時還不清楚,看來需要閱讀更多源碼才能明白。 */ 60 vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) ); 61 62 /* Increment the ucTasksDeleted variable so the idle task knows 63 * there is a task that has been deleted and that it should therefore 64 * check the xTasksWaitingTermination list. */ 65 ++uxDeletedTasksWaitingCleanUp; 66 67 /* Delete the task TCB in idle task. */ 68 xDeleteTCBInIdleTask = pdTRUE; 69 70 /* The pre-delete hook is primarily for the Windows simulator, 71 * in which Windows specific clean up operations are performed, 72 * after which it is not possible to yield away from this task - 73 * hence xYieldPending is used to latch that a context switch is 74 * required. */ 75 /* 只有在Windows下才有用,先不分析了 */ 76 #if ( configNUMBER_OF_CORES == 1 ) 77 portPRE_TASK_DELETE_HOOK( pxTCB, &( xYieldPendings[ 0 ] ) ); 78 #endif 79 } 80 else 81 { 82 /* 這裡是刪除阻塞任務(pxDelayedTaskList)的(xTaskIsRunningOrYielding = pdFALSE) */ 83 --uxCurrentNumberOfTasks; 84 85 /* Reset the next expected unblock time in case it referred to 86 * the task that has just been deleted. */ 87 /* 主要用於更新最久阻塞的剩餘阻塞時間,防止最近需要運行的就是刪除的這個任務 */ 88 prvResetNextTaskUnblockTime(); 89 } 90 } 91 taskEXIT_CRITICAL(); 92 93 /* If the task is not deleting itself, call prvDeleteTCB from outside of 94 * critical section. If a task deletes itself, prvDeleteTCB is called 95 * from prvCheckTasksWaitingTermination which is called from Idle task. */ 96 /* 如果刪除的是當前運行的任務在空閑任務中處理,阻塞任務在這裡處理 */ 97 if( xDeleteTCBInIdleTask != pdTRUE ) 98 { 99 prvDeleteTCB( pxTCB ); 100 } 101 102 /* Force a reschedule if it is the currently running task that has just 103 * been deleted. */ 104 /* 如果刪除的是當前運行的任務,需要調度,這很明顯。 */ 105 #if ( configNUMBER_OF_CORES == 1 ) 106 { 107 if( xSchedulerRunning != pdFALSE ) 108 { 109 if( pxTCB == pxCurrentTCB ) 110 { 111 configASSERT( uxSchedulerSuspended == 0 ); 112 taskYIELD_WITHIN_API(); 113 } 114 else 115 { 116 mtCOVERAGE_TEST_MARKER(); 117 } 118 } 119 } 120 #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ 121 }vTaskDelete
1 static void prvDeleteTCB( TCB_t * pxTCB ) 2 { 3 #if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( portUSING_MPU_WRAPPERS == 0 ) ) 4 { 5 /* The task can only have been allocated dynamically - free both 6 * the stack and TCB. */ 7 vPortFreeStack( pxTCB->pxStack ); 8 vPortFree( pxTCB ); 9 } 10 #elif ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) 11 { 12 /* The task could have been allocated statically or dynamically, so 13 * check what was statically allocated before trying to free the 14 * memory. */ 15 /* 按需釋放tcb和stack */ 16 if( pxTCB->ucStaticallyAllocated == tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ) 17 { 18 /* Both the stack and TCB were allocated dynamically, so both 19 * must be freed. */ 20 vPortFreeStack( pxTCB->pxStack ); 21 vPortFree( pxTCB ); 22 } 23 else if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY ) 24 { 25 /* Only the stack was statically allocated, so the TCB is the 26 * only memory that must be freed. */ 27 vPortFree( pxTCB ); 28 } 29 else 30 { 31 /* Neither the stack nor the TCB were allocated dynamically, so 32 * nothing needs to be freed. */ 33 configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB ); 34 mtCOVERAGE_TEST_MARKER(); 35 } 36 } 37 #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ 38 }prvDeleteTCB
4.2.7 獲取任務狀態--eTaskGetState
這個介面可以大致看出任務在哪些情況下會處於什麼狀態。
首先是返回值的說明:
1 typedef enum 2 { 3 eRunning = 0, /* 運行態. */ 4 eReady, /* 就緒態, 任務在 ready or pending ready 列表. */ 5 eBlocked, /* 阻塞態. */ 6 eSuspended, /* 掛起態, 任務在 掛起列表, 或處於無限延遲的阻塞中. */ 7 eDeleted, /* 等待刪除. */ 8 eInvalid /* Used as an 'invalid state' value. */ 9 } eTaskState;
1 eTaskState eTaskGetState( TaskHandle_t xTask ) 2 { 3 eTaskState eReturn; 4 List_t const * pxStateList; 5 List_t const * pxEventList; 6 List_t const * pxDelayedList; 7 List_t const * pxOverflowedDelayedList; 8 const TCB_t * const pxTCB = xTask; 9 10 configASSERT( pxTCB ); 11 12 #if ( configNUMBER_OF_CORES == 1 ) 13 if( pxTCB == pxCurrentTCB ) 14 { 15 /* The task calling this function is querying its own state. */ 16 /* 當前任務是查詢的任務,則處於運行態 */ 17 eReturn = eRunning; 18 } 19 else 20 #endif 21 { 22 taskENTER_CRITICAL(); 23 { 24 pxStateList = listLIST_ITEM_CONTAINER( &( pxTCB->xStateListItem ) ); 25 pxEventList = listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ); 26 pxDelayedList = pxDelayedTaskList; 27 pxOverflowedDelayedList = pxOverflowDelayedTaskList; 28 } 29 taskEXIT_CRITICAL(); 30 31 if( pxEventList == &xPendingReadyList ) 32 { 33 /* The task has been placed on the pending ready list, so its 34 * state is eReady regardless of what list the task's state list 35 * item is currently placed on. */ 36 /* 任務的事件項處於xPendingReadyList中就是就緒態 37 * 說明xPendingReadyList里放的是任務的xEventListItem 38 * 調度器掛起時,任務已ready,調度器運行後, 39 * 會將xPendingReadyList中的任務移至就緒列表中 */ 40 eReturn = eReady; 41 } 42 else if( ( pxStateList == pxDelayedList ) || ( pxStateList == pxOverflowedDelayedList ) ) 43 { 44 /* The task being queried is referenced from one of the Blocked 45 * lists. */ 46 /* pxDelayedTaskList和pxOverflowDelayedTaskList放的是阻塞任務,xStateListItem */ 47 eReturn = eBlocked; 48 } 49 50 #if ( INCLUDE_vTaskSuspend == 1 ) 51 /* xSuspendedTaskList放的是掛起的任務,xStateListItem */ 52 else if( pxStateList == &xSuspendedTaskList ) 53 { 54 /* The task being queried is referenced from the suspended 55 * list. Is it genuinely suspended or is it blocked 56 * indefinitely? */ 57 /* xEventListItem不在任何列表裡 */ 58 if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ) 59 { 60 #if ( configUSE_TASK_NOTIFICATIONS == 1 ) 61 { 62 BaseType_t x; 63 64 /* The task does not appear on the event list item of 65 * and of the RTOS objects, but could still be in the 66 * blocked state if it is waiting on its notification 67 * rather than waiting on an object. If not, is 68 * suspended. */ 69 /* 說明等通知不是把xEventListItem放到某個列表裡, 70 * 而且等通知會把任務放到掛起的列表裡 */ 71 eReturn = eSuspended; 72 73 for( x = ( BaseType_t ) 0; x < ( BaseType_t ) configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ ) 74 { 75 if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION ) 76 { 77 eReturn = eBlocked; 78 break; 79 } 80 } 81 } 82 #else /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ 83 { 84 eReturn = eSuspended; 85 } 86 #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ 87 } 88 else 89 { 90 /* 如果xStateListItem在掛起列表裡且xEventListItem處於某個列表裡,則任務會阻塞 */ 91 eReturn = eBlocked; 92 } 93 } 94 #endif /* if ( INCLUDE_vTaskSuspend == 1 ) */ 95 96 #if ( INCLUDE_vTaskDelete == 1 ) 97 else if( ( pxStateList == &xTasksWaitingTermination ) || ( pxStateList == NULL ) ) 98 { 99 /* The task being queried is referenced from the deleted 100 * tasks list, or it is not referenced from any lists at 101 * all. */ 102 /* 等著被刪除,這很明顯 */ 103 eReturn = eDeleted; 104 } 105 #endif 106 107 else 108 { 109 #if ( configNUMBER_OF_CORES == 1 ) 110 { 111 /* If the task is not in any other state, it must be in the 112 * Ready (including pending ready) state. */ 113 /* 剩下的就只能是在就緒列表裡(pxReadyTasksLists) 114 * pxReadyTasksLists是個列表數組,通過排除法,可以 115 * 免於進行遍歷操作。 */ 116 eReturn = eReady; 117 } 118 #else /* #if ( configNUMBER_OF_CORES == 1 ) */ 119 { 120 if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE ) 121 { 122 /* Is it actively running on a core? */ 123 eReturn = eRunning; 124 } 125 else 126 { 127 /* If the task is not in any other state, it must be in the 128 * Ready (including pending ready) state. */ 129 eReturn = eReady; 130 } 131 } 132 #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ 133 } 134 } 135 136 /* 簡單總結下:xStateListItem會在pxDelayedTaskList、pxOverflowDelayedTaskList、 137 * xSuspendedTaskList和xTasksWaitingTermination里 138 * xEventListItem會在xPendingReadyList里。 139 * 簡單分析就是事件、通知來了任務就放到xPendingReadyList里,其他情況就在其他列表裡 140 * 具體是不是這樣,看後面的源碼,會逐漸清晰 */ 141 142 return eReturn; 143 }eTaskGetState
4.2.8 任務延遲,釋放CPU--vTaskDelay
這個介面比較熟悉,至少是任務中最常用的介面了。但是正如介面的註釋所說的那樣,這個介面不提供準確的延遲時間,任務喚醒的周期會受中斷、其他任務的影響,並且計時起始於這個介面開始被調用的時候,且時間是大於等於給予的延遲時間。如果想要準確的周期性的任務,那就要調用xTaskDelayUntil介面,這個會在後面講到。
1 void vTaskDelay( const TickType_t xTicksToDelay ) 2 { 3 BaseType_t xAlreadyYielded = pdFALSE; 4 5 /* A delay time of zero just forces a reschedule. */ 6 /* 如果延遲tick為0, 則表示強制執行調度 */ 7 if( xTicksToDelay > ( TickType_t ) 0U ) 8 { 9 vTaskSuspendAll(); // 停止調度器 10 { 11 configASSERT( uxSchedulerSuspended == 1U ); 12 13 /* A task that is removed from the event list while the 14 * scheduler is suspended will not get placed in the ready 15 * list or removed from the blocked list until the scheduler 16 * is resumed. 17 * 18 * This task cannot be in an event list as it is the currently 19 * executing task. */ 20 /* 在調度器暫停時, 從事件列表中刪除的任務將不會被放置在就緒列表中 21 * 或從阻塞列表中刪除, 直到調度器恢復。此任務不可能在事件列表中, 22 * 因為它是當前正在執行的任務。 */ 23 prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE ); 24 } 25 xAlreadyYielded = xTaskResumeAll(); 26 } 27 else 28 { 29 mtCOVERAGE_TEST_MARKER(); 30 } 31 32 /* Force a reschedule if xTaskResumeAll has not already done so, we may 33 * have put ourselves to sleep. */ 34 /* xTaskResumeAll沒有調度, 那在這裡手動調度 */ 35 if( xAlreadyYielded == pdFALSE ) 36 { 37 taskYIELD_WITHIN_API(); 38 } 39 else 40 { 41 mtCOVERAGE_TEST_MARKER(); 42 } 43 } 44 45 void vTaskSuspendAll( void ) 46 { 47 #if ( configNUMBER_OF_CORES == 1 ) 48 { 49 /* A critical section is not required as the variable is of type 50 * BaseType_t. Please read Richard Barry's reply in the following link to a 51 * post in the FreeRTOS support forum before reporting this as a bug! - 52 * https://goo.gl/wu4acr */ 53 54 /* portSOFTWARE_BARRIER() is only implemented for emulated/simulated ports that 55 * do not otherwise exhibit real time behaviour. */ 56 portSOFTWARE_BARRIER(); 57 58 /* The scheduler is suspended if uxSchedulerSuspended is non-zero. An increment 59 * is used to allow calls to vTaskSuspendAll() to nest. */ 60 /* uxSchedulerSuspended非0, 則調度器暫停, 加1是為了嵌套調用(中斷也調用) */ 61 uxSchedulerSuspended = ( UBaseType_t ) ( uxSchedulerSuspended + 1U ); 62 63 /* Enforces ordering for ports and optimised compilers that may otherwise place 64 * the above increment elsewhere. */ 65 portMEMORY_BARRIER(); 66 } 67 #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ 68 } 69 70 static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, 71 const BaseType_t xCanBlockIndefinitely ) 72 { 73 TickType_t xTimeToWake; 74 const TickType_t xConstTickCount = xTickCount; 75 List_t * const pxDelayedList = pxDelayedTaskList; 76 List_t * const pxOverflowDelayedList = pxOverflowDelayedTaskList; 77 78 #if ( INCLUDE_xTaskAbortDelay == 1 ) 79 { 80 /* About to enter a delayed list, so ensure the ucDelayAborted flag is 81 * reset to pdFALSE so it can be detected as having been set to pdTRUE 82 * when the task leaves the Blocked state. */ 83 pxCurrentTCB->ucDelayAborted = ( uint8_t ) pdFALSE; 84 } 85 #endif 86 87 /* Remo