最近在Mac上想要遠程一臺Linux伺服器,結果不知怎麼的就不能使用以前的ssh登錄了 iot@ios-iMac ~ % ssh [email protected] Unable to negotiate with 192.168.1.230 port 22: no matching host k ...
目錄
以下FreeRTOS源碼函數使用的版本是9.0.0,不同版本的源碼會有部分不同如10.4.6,註意甑別。
掛起和恢復任務相關 API 函數有以下幾個:
函數 vTaskSuspend()
此函數用於掛起任務,若使用此函數,需要在 FreeRTOSConfig.h 文件中將巨集INCLUDE_vTaskSuspend 配置為 1。無論優先順序如何,被掛起的任務都將不再被執行,直到任務被恢復。此函數不支持嵌套,不論使用此函數重覆掛起任務多少次,只需調用一次恢復任務的函數,那麼任務就不再被掛起。傳遞空句柄將導致調用任務被掛起。
函數如下:
void vTaskSuspend(TaskHandle_t xTaskToSuspend)
{
TCB_t *pxTCB;
taskENTER_CRITICAL();
{
/* 獲取要掛起的任務的任務控制塊,傳入 NULL 則掛起任務自身 */
pxTCB = prvGetTCBFromHandle(xTaskToSuspend);
traceTASK_SUSPEND(pxTCB);
/* 將任務從任務所在任務狀態列表(就緒態任務列表或阻塞態任務列表)中移除 */
if (uxListRemove(&(pxTCB->xStateListItem)) == (UBaseType_t)0)
{
/* 成功移除掉了那就重新設置任務對應優先順序的就緒點陣圖,以確保其他任務能夠正確地繼續運行 */
taskRESET_READY_PRIORITY(pxTCB->uxPriority);
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 這個任務是否在等待某個事件,如果是,那就移除所在事件列表項,因為要掛起該任務了 */
if (listLIST_ITEM_CONTAINER(&(pxTCB->xEventListItem)) != NULL)
{
(void)uxListRemove(&(pxTCB->xEventListItem));
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 將TCB的狀態列表項插入掛起列表項末尾,實現掛起任務 */
vListInsertEnd(&xSuspendedTaskList, &(pxTCB->xStateListItem));
}
taskEXIT_CRITICAL();
/* 調度器在運行,則需要更新下一個任務的阻塞超時時間 */
if (xSchedulerRunning != pdFALSE)
{
/* Reset the next expected unblock time in case it referred to the
task that is now in the Suspended state. */
taskENTER_CRITICAL();
{
/* 更新下個任務解除阻塞時間 */
prvResetNextTaskUnblockTime();
}
taskEXIT_CRITICAL();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if (pxTCB == pxCurrentTCB)
{
/* 如果要掛起的是自己,且調度器在運行,那麼表示當前任務自身被掛起,需要進行任務調度 */
if (xSchedulerRunning != pdFALSE)
{
/* The current task has just been suspended. */
/* 首先進行斷言驗證確保調度器沒有被掛起 */
configASSERT(uxSchedulerSuspended == 0);
/* 調用 portYIELD_WITHIN_API 函數主動觸發任務切換,使其他就緒任務有機會被調度執行 */
portYIELD_WITHIN_API();
/* 主要用於任務級別的 API 函數內部,用於特定情況下主動觸發任務切換 */
}
/* 調度器沒運行 */
else
{
/* 調度器沒有運行,但是 pxCurrentTCB 所指向的任務剛剛掛起,
必須調整 pxCurrentTCB 以指向不同的任務
當任務被掛起後,如果沒有其他就緒任務,即掛起列表中的任務數等於當前任務數 */
if (listCURRENT_LIST_LENGTH(&xSuspendedTaskList) == uxCurrentNumberOfTasks)
{
/* 沒有就緒的任務,將 pxCurrentTCB 設置為 NULL。這樣,在下次創建任務時,
無論其相對優先順序如何,pxCurrentTCB 都會被設置為指向新任務*/
pxCurrentTCB = NULL;
}
else
{
/* 調用函數進行任務切換。這將導致上下文切換,並將執行權交給另一個就緒任務 */
vTaskSwitchContext();
}
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
1.該函數先獲取了待掛起的任務的任務控制塊,以便進行列表項移除、添加操作。
2.將待掛起任務從所在的狀態列表中移除,如果移除後列表項數量為0則需要更新任務優先順序記錄。
3.將任務插入至掛起任務列表當中。
4.更新下一個任務解除阻塞的時間,以防被掛起的任務就是下一個阻塞超時的任務。
5.如果要掛起的是自身,並且調度器在運行中,那就需要要求調度器進行任務調度。如果調度器沒有在運行,就需要調整當前任務控制塊的指向。更新 pxCurrentTCB 的操作,是通過調用函數 vTaskSwitchContext()實現的。
函數 vTaskSwitchContext()
void vTaskSwitchContext( void )
{
if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )
{
/* The scheduler is currently suspended - do not allow a context
switch. */
/* 調度程式當前掛起-不允許進行上下文切換,直接退出函數 */
xYieldPending = pdTRUE;
}
else
{
xYieldPending = pdFALSE;
traceTASK_SWITCHED_OUT();
#if ( configGENERATE_RUN_TIME_STATS == 1 )
{
#ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime );
#else
ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE();
#endif
/* Add the amount of time the task has been running to the
accumulated time so far. The time the task started running was
stored in ulTaskSwitchedInTime. Note that there is no overflow
protection here so count values are only valid until the timer
overflows. The guard against negative values is to protect
against suspect run time stat counter implementations - which
are provided by the application, not the kernel. */
if( ulTotalRunTime > ulTaskSwitchedInTime )
{
pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
ulTaskSwitchedInTime = ulTotalRunTime;
}
#endif /* configGENERATE_RUN_TIME_STATS */
/* Check for stack overflow, if configured. */
taskCHECK_FOR_STACK_OVERFLOW();
/* Select a new task to run using either the generic C or port
optimised asm code. */
/* 此函數用於將 pxCurrentTCB 更新為指向優先順序最高的就緒態任務 */
taskSELECT_HIGHEST_PRIORITY_TASK();
traceTASK_SWITCHED_IN();
#if ( configUSE_NEWLIB_REENTRANT == 1 )
{
/* Switch Newlib's _impure_ptr variable to point to the _reent
structure specific to this task. */
_impure_ptr = &( pxCurrentTCB->xNewLib_reent );
}
#endif /* configUSE_NEWLIB_REENTRANT */
}
}
此函數的重點在於調用了函數 taskSELETE_HIGHEST_PRIORITY_TASK() 更新pxCurrentTCB 指向優先順序最高的就緒態任務,函數 taskSELETE_HIGHEST_PRIORITY_TASK()實際上是一個巨集定義,task.c 文件中有定義。
#define taskSELECT_HIGHEST_PRIORITY_TASK() \
{ \
UBaseType_t uxTopPriority; \
\
/* Find the highest priority list that contains ready tasks. */ \
/* 查找就緒態任務列表中最高的任務優先順序 */ \
portGET_HIGHEST_PRIORITY(uxTopPriority, uxTopReadyPriority); \
/* 此任務優先順序不能是最低的任務優先順序 */ \
configASSERT(listCURRENT_LIST_LENGTH(&(pxReadyTasksLists[uxTopPriority])) > 0); \
/* 讓 pxCurrentTCB 指向該任務優先順序就緒態任務列表中的任務 */ \
listGET_OWNER_OF_NEXT_ENTRY(pxCurrentTCB, &(pxReadyTasksLists[uxTopPriority])); \
} /* taskSELECT_HIGHEST_PRIORITY_TASK() */
以上便是任務掛起函數的執行流程,如果掛起的是其他任務,就將其他任務添加至掛起列表當中,如果掛起的是自身,就需要觸發一次任務調度進行任務切換。註意:任務掛起後只能由其他任務進行恢復,如果一直不恢復任務將無法得到執行,如果其他任務也進行了掛起,那將永遠無法恢復。
函數 vTaskResume()
此函數用於在任務中恢復被掛起的任務,若使用此函數,需要在 FreeRTOSConfig.h 文件中將巨集 INCLUDE_vTaskSuspend 配置為 1。不論一個任務被函數 vTaskSuspend()掛起多少次,只需要使用函數 vTakResume()恢復一次,就可以繼續運行。
void vTaskResume(TaskHandle_t xTaskToResume)
{
TCB_t *const pxTCB = (TCB_t *)xTaskToResume;
/* It does not make sense to resume the calling task. */
configASSERT(xTaskToResume);
/* The parameter cannot be NULL as it is impossible to resume the
currently executing task. */
/* 恢復的任務不能是自己,恢復的是被掛起的任務 */
if ((pxTCB != NULL) && (pxTCB != pxCurrentTCB))
{
taskENTER_CRITICAL();
{
/* 判斷恢復的任務是否被掛起,只有被掛起才會恢復,即返回的是pdTRUE */
if (prvTaskIsTaskSuspended(pxTCB) != pdFALSE)
{
traceTASK_RESUME(pxTCB);
/* As we are in a critical section we can access the ready
lists even if the scheduler is suspended.
由於我們處於臨界區,即使調度器被掛起,我們也可以訪問就緒列表 */
/* 將任務從掛起狀態列表中移除,並添加到就緒列表中 */
(void)uxListRemove(&(pxTCB->xStateListItem));
prvAddTaskToReadyList(pxTCB);
/* We may have just resumed a higher priority task. */
/* 任務優先順序大於等於當前任務優先順序就進行任務切換 */
if (pxTCB->uxPriority >= pxCurrentTCB->uxPriority)
{
/* This yield may not cause the task just resumed to run,
but will leave the lists in the correct state for the
next yield. */
taskYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
該函數很簡單,首先判斷恢復的任務是不是任務自身,任務自身都在運行著了,談何恢復?隨後判斷待恢復的任務是不是正在被掛起的,被掛起的才會進行恢復。將待恢復任務從掛起列表當中移除,並添加至就緒列表等待調度器調度,如果恢復的任務優先順序大於當前正在運行的任務的優先順序則需要進行任務切換。
函數 xTaskResumeFromISR()
此函數用於在中斷中恢復被掛起的任務,若使用此函數,需要在 FreeRTOSConfig.h 文件中將巨集 INCLUDE_xTaskResumeFromISR 配置為 1。不論一個任務被函數 vTaskSuspend()掛起多少次,只需要使用函數 vTakResumeFromISR()恢復一次,就可以繼續運行。
BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume)
使用該函數需要判斷返回值,根據返回值進行任務切換。函數 xTaskResumeFromISR()的返回值,如下所示:
FreeRTOS官方:xTaskResumeFromISR()通常被視為危險函數,因為其操作未被鎖定。因此,如果中斷可能在任務被掛起之前到達,從而中斷丟失,則絕對不應使用該函數來同步任務與中斷。可使用信號量,或者最好是直達任務通知,來避免這種可能性。
這也就是說如果在中斷中不對任務狀態進行判斷是否處於掛起態就調用從中斷中恢復的函數,那麼任務在還沒掛起的時候就被恢復,這將導致恢復丟失,所以不應該用該函數來進行任務同步,推薦使用信號量、任務通知來進行。
BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume)
{
/* xYieldRequired返回的值指示是否需要進行上下文切換 */
BaseType_t xYieldRequired = pdFALSE;
TCB_t *const pxTCB = (TCB_t *)xTaskToResume;
/* 保存中斷當前狀態 */
UBaseType_t uxSavedInterruptStatus;
configASSERT(xTaskToResume);
/* RTOS ports that support interrupt nesting have the concept of a
maximum system call (or maximum API call) interrupt priority.
Interrupts that are above the maximum system call priority are keep
permanently enabled, even when the RTOS kernel is in a critical section,
but cannot make any calls to FreeRTOS API functions. If configASSERT()
is defined in FreeRTOSConfig.h then
portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion
failure if a FreeRTOS API function is called from an interrupt that has
been assigned a priority above the configured maximum system call
priority. Only FreeRTOS functions that end in FromISR can be called
from interrupts that have been assigned a priority at or (logically)
below the maximum system call interrupt priority. FreeRTOS maintains a
separate interrupt safe API to ensure interrupt entry is as fast and as
simple as possible. More information (albeit Cortex-M specific) is
provided on the following link:
http://www.freertos.org/RTOS-Cortex-M3-M4.html
由於FreeRTOS有中斷屏蔽的概念,但高於某個值的中斷優先順序無法屏蔽。如果定義了configASSERT(),
那麼如果一個API函數在一個不在FreeRTOS管理範圍之內的中斷服務常式中被調用,將導致斷言失敗。
只有以FromISR結尾的API函數可以從FreeRTOS可管理的中斷中進行調用,不受它管理的調用也會有問題 */
/* 驗證中斷的優先順序設置是否符合 FreeRTOS 的要求,是否在管理範圍內 */
portASSERT_IF_INTERRUPT_PRIORITY_INVALID();
/* 保存中斷當前狀態 */
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
/* 判斷任務是否被掛起 */
if (prvTaskIsTaskSuspended(pxTCB) != pdFALSE)
{
traceTASK_RESUME_FROM_ISR(pxTCB);
/* Check the ready lists can be accessed. */
/* 任務調度器沒被掛起,在運行 */
if (uxSchedulerSuspended == (UBaseType_t)pdFALSE)
{
/* Ready lists can be accessed so move the task from the
suspended list to the ready list directly.
任務調度器沒被掛起,可以訪問就緒列表,因此可以直接將任務從掛起列表移動到就緒列表 */
if (pxTCB->uxPriority >= pxCurrentTCB->uxPriority)
{
xYieldRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
(void)uxListRemove(&(pxTCB->xStateListItem));
prvAddTaskToReadyList(pxTCB);
}
/* 調度器沒運行 */
else
{
/* The delayed or ready lists cannot be accessed so the task
is held in the pending ready list until the scheduler is
unsuspended.
延遲列表或就緒列表無法訪問,因此任務將保持在掛起的就緒列表中,直到調度器解除掛起
*/
/* 因為調度器被掛起了,所以就將被恢復的任務插入到等待就緒列表當中
直到調度器被恢復才執行任務的處理,添加到就緒列表當中 */
vListInsertEnd(&(xPendingReadyList), &(pxTCB->xEventListItem));
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* 使用此巨集函數可以在中斷服務程式中恢復中斷屏蔽,以允許其他具有較高優先順序的中斷被處理 */
portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
return xYieldRequired;
}
該函數的邏輯與普通恢復函數差不多,只不過是在中斷中運行,判斷任務是否被掛起,判斷調度器是否被掛起,沒被掛起則從掛起列表移除添加到就緒列表。調度器被掛起了則先將該任務添加到等待就緒列表當中,待到調度器恢復之後將這些任務添加到就緒列表中等待執行。
該函數還有官方的一大串描述,大概說的是FreeRTOS某些函數在調用的時候會屏蔽中斷,以達到臨界區的訪問執行,但FreeRTOS屏蔽的中斷範圍是由一個閾值的,如果在一個中斷屏蔽的閾值之外的中斷服務常式中調用API函數這通常會觸發斷言,只有以FromISR結尾的API函數可以從FreeRTOS管理的中斷中進行調用。