FreeRTOS掛起和恢復任務相關函數解析

来源:https://www.cnblogs.com/RAM-YAO/p/18348072
-Advertisement-
Play Games

最近在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管理的中斷中進行調用。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、背景 在公司軟體的實際開發中,當一個版本的客戶端安裝包本地調試、測試驗證都沒問題後外發,到用戶實際機器上出問題了,怎麼辦? 很多人說,讓客戶給出復現步驟,我來試試!但是按照步驟操作之後還是沒效果。這時你又想到了是不是環境的差異,但是又說不上來是哪裡出問題。提供兩個辦法:1.寫日誌,編一個相近版本 ...
  • 1、用戶操作 阿裡雲預設是 root 用戶,我們一般要自己創建一個用戶,然後給該用戶 sudo 許可權 添加用戶 sudo adduser newUserName 賦予sudo許可權 sudo usermod -aG sudo newUserName 刪除用戶 sudo deluser --remove ...
  • 1、Docker 基本概念 什麼是 Docker? Docker 是一個開源的容器化平臺,允許開發者封裝他們的應用程式及其所有依賴項到一個標準化的單元中,這個單元被稱為“容器”。容器可以在任何支持 Docker 的環境中運行,從而確保應用程式的可移植性和一致性。 Docker 的優勢 一致性和可移植 ...
  • 大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家分享的是i.MXRT1050在GPIO上增加RC延時電路後導致邊沿中斷誤觸發問題探析。 前段時間有一個 RT1052 客戶反饋了一個有趣的問題,他們設計得是一個帶 LCD 屏交互的應用,應用以官方 SDK 里的 lvgl_demo_widget ...
  • 家裡的機頂盒淘汰下來,博主想要物盡其用,看看是否能將其改造為一臺Linux"開發機",為其安裝Ubuntu系統,故開始倒騰 ...
  • 寫在前面 本隨筆是非常菜的菜雞寫的。如有問題請及時提出。 可以聯繫:[email protected] GitHhub:https://github.com/WindDevil (目前啥也沒有 本節重點 主要是對 任務 的概念進行進一步擴展和延伸:形成 任務運行狀態:任務從開始到結束執行過程中所處的 ...
  • 第十八章 machine.Timer類實驗 1)實驗平臺:正點原子DNK210開發板 2)章節摘自【正點原子】DNK210使用指南 - CanMV版 V1.0 3)購買鏈接:https://detail.tmall.com/item.htm?&id=782801398750 4)全套實驗源碼+手冊+ ...
  • 導讀 還是要先看官方手冊. 學過DMA的同志可能比較好理解,一句話, 釋放CPU匯流排 : 如果把應用程式執行的整個過程進行進一步分析,可以看到,當程式訪問 I/O 外設或睡眠時,其實是不需要占用處理器的,於是我們可以把應用程式在不同時間段的執行過程分為兩類,占用處理器執行有效任務的計算階段和不必占用 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...