FreeRTOS學習筆記-1-任務管理

在 FreeRTOS 中,每個執(zhí)行線程都被稱為”任務”。

本章的目的

  • FreeRTOS 如何為各任務分配處理時間。
  • FreeRTOS 如何選擇任務投入運行。
  • 任務優(yōu)先級如何影響系統(tǒng)行為。
  • 任務存在哪些狀態(tài)。

以及

  • 如何實現一個任務。
  • 如何創(chuàng)建一個或多個任務的實例。
  • 如何使用任務參數。
  • 如何改變一個已創(chuàng)建任務的優(yōu)先級。
  • 如何刪除任務。
  • 如何實現周期性處理。
  • 空閑任務何時運行,可以用來干什么。

1.任務函數

任務是由 C 語言函數實現的。唯一特別的只是任務的函數原型,其必須返回 void,
而且?guī)в幸粋€ void 指針參數。

void ATaskFunction( void *pvParameters );

FreeRTOS 任務不允許以任何方式從實現函數中返回——它們絕不能有一條”return”語句,也==不能執(zhí)行到函數末尾==。如果一個任務不再需要,可以顯式地將其刪除。

一個任務函數可以用來創(chuàng)建若干個任務——創(chuàng)建出的任務均是獨立的執(zhí)行實例,擁有屬于自己的棧空間,以及屬于自己的自動變量(棧變量),即任務函數本身定義的變量。

void ATaskFunction( void *pvParameters )
{
    /* 可以像普通函數一樣定義變量。用這個函數創(chuàng)建的每個任務實例都有一個屬于自己的iVarialbleExample變量。但如果iVariableExample被定義為static,這一點則不成立 – 這種情況下只存在一個變量,所有的任務實例將會共享這個變量。 */
    int iVariableExample = 0;
    /* 任務通常實現在一個死循環(huán)中。 */
    for( ;; )
    {
        /* 完成任務功能的代碼將放在這里。 */
    }
    /* 如果任務的具體實現會跳出上面的死循環(huán),則此任務必須在函數運行完之前刪除。傳入NULL參數表示刪除的是當前任務 */
    vTaskDelete( NULL );
}

2.任務狀態(tài)

  1. 運行狀態(tài)
  2. 非運行狀態(tài)

當某個任務處于運行態(tài)時,處理器就正在執(zhí)行它的代碼。當一個任務處于非運行態(tài)
時,該任務進行休眠,它的所有狀態(tài)都被妥善保存,以便在下一次調試器決定讓它進入
運行態(tài)時可以恢復執(zhí)行。

3.創(chuàng)建任務

創(chuàng)建任務使用 FreeRTOS 的 API 函數 xTaskCreate()。

portBASE_TYPE xTaskCreate(  pdTASK_CODE pvTaskCode,     //指向任務的實現函數的指針
                            const signed portCHAR * const pcName,   //具有描述性的任務名
                            unsigned portSHORT usStackDepth,   //usStackDepth 值用于告訴內核為它分配多大的??臻g,棧空間可以保存多少個字(word)
                            void *pvParameters,         //pvParameters 的值即是傳遞到任務中的值。
                            unsigned portBASE_TYPE uxPriority,  //任務執(zhí)行的優(yōu)先級。優(yōu)先級的取值范圍可以從最低優(yōu)先級 0 到最高優(yōu)先級(configMAX_PRIORITIES – 1)。
                            xTaskHandle *pxCreatedTask );   //pxCreatedTask 用于傳出任務的句柄。比如改變任務優(yōu)先級,或者刪除任務。
                                                            //1. pdTRUE:表明任務創(chuàng)建成功
                                                            //2. errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY由于內存堆空間不足, FreeRTOS 無法分配足夠的空間來保存任務結構數據和任務棧,因此無法創(chuàng)建任務。
                        

創(chuàng)建任務示例

本例演示了創(chuàng)建并啟動兩個任務的必要步驟。這兩個任務只是周期性地打印輸出字
符串,采用原始的空循環(huán)方式來產生周期延遲。兩者在創(chuàng)建時指定了相同的優(yōu)先級,并
且在實現上除輸出的字符串外完全一樣。

void vTask1( void *pvParameters )
{
    const char *pcTaskName = "Task 1 is running\r\n";
    volatile unsigned long ul;
    /* 和大多數任務一樣,該任務處于一個死循環(huán)中。 */
    for( ;; )
    {
        /* Print out the name of this task. */
        vPrintString( pcTaskName );
        /* 延遲,以產生一個周期 */
        for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
        {
            /* 這個空循環(huán)是最原始的延遲實現方式。在循環(huán)中不做任何事情。后面的示例程序將采用
            delay/sleep函數代替這個原始空循環(huán)。 */
        }
    }
}

void vTask2( void *pvParameters )
{
    const char *pcTaskName = "Task 2 is running\r\n";
    volatile unsigned long ul;
    /* 和大多數任務一樣,該任務處于一個死循環(huán)中。 */
    for( ;; )
    {
        /* Print out the name of this task. */
        vPrintString( pcTaskName );
        /* 延遲,以產生一個周期 */
        for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
        {
            /* 這個空循環(huán)是最原始的延遲實現方式。在循環(huán)中不做任何事情。后面的示例程序將采用
            delay/sleep函數代替這個原始空循環(huán)。 */
        }
    }
}

int main( void )
{
    /* 創(chuàng)建第一個任務。 需要說明的是一個實用的應用程序中應當檢測函數xTaskCreate()的返回值,以確保任務創(chuàng)建成功。 */
    xTaskCreate( vTask1, /* 指向任務函數的指針 */
                "Task 1", /* 任務的文本名字,只會在調試中用到 */
                1000, /* 棧深度 – 大多數小型微控制器會使用的值會比此值小得多 */
                NULL, /* 沒有任務參數 */
                1, /* 此任務運行在優(yōu)先級1上. */
                NULL ); /* 不會用到任務句柄 */
    /* Create the other task in exactly the same way and at the same priority. */
    xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );
    /* 啟動調度器,任務開始執(zhí)行 */
    vTaskStartScheduler();
    /* 如果一切正常, main()函數不應該會執(zhí)行到這里。但如果執(zhí)行到這里,很可能是內存堆空間不足導致空閑任務無法創(chuàng)建。第五章有講述更多關于內存管理方面的信息 */
    for( ;; );
}

使用任務參數示例

與上例中創(chuàng)建的兩個任務幾乎完全相同,唯一的區(qū)別就是打印輸出的字符串。

void vTaskFunction( void *pvParameters )
{
    char *pcTaskName;
    volatile unsigned long ul;
    /* 需要打印輸出的字符串從入口參數傳入。強制轉換為字符指針。 */
    pcTaskName = ( char * ) pvParameters;
    /* As per most tasks, this task is implemented in an infinite loop. */
    for( ;; )
    {
        /* Print out the name of this task. */
        vPrintString( pcTaskName );
        /* Delay for a period. */
        for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
        {
            /* This loop is just a very crude delay implementation. There is
            nothing to do in here. Later exercises will replace this crude
            loop with a proper delay/sleep function. */
        }
    }
}

/* 定義將要通過任務參數傳遞的字符串。定義為const,且不是在??臻g上,以保證任務執(zhí)行時也有效。 */
static const char *pcTextForTask1 = “Task 1 is running\r\n”;
static const char *pcTextForTask2 = “Task 2 is running\t\n”;
int main( void )
{
    /* Create one of the two tasks. */
    xTaskCreate( vTaskFunction, /* 指向任務函數的指針. */
                "Task 1", /* 任務名. */
                1000, /* 棧深度. */
                (void*)pcTextForTask1, /* 通過任務參數傳入需要打印輸出的文本. */
                1, /* 此任務運行在優(yōu)先級1上. */
                NULL ); /* 不會用到此任務的句柄. */
    /* 同樣的方法創(chuàng)建另一個任務。至此,由相同的任務代碼(vTaskFunction)創(chuàng)建了多個任務,僅僅是傳入的參數不同。同一個任務創(chuàng)建了兩個實例。 */
    xTaskCreate( vTaskFunction, "Task 2", 1000, (void*)pcTextForTask2, 1, NULL );
    /* Start the scheduler so our tasks start executing. */
    vTaskStartScheduler();
    /* If all is well then main() will never reach here as the scheduler will
    now be running the tasks. If main() does reach here then it is likely that
    there was insufficient heap memory available for the idle task to be created.
    CHAPTER 5 provides more information on memory management. */
    for( ;; );
}

4.任務優(yōu)先級

xTaskCreate() API 函數的參數 uxPriority 為創(chuàng)建的任務賦予了一個初始優(yōu)先級。這個侁先級可以在調度器啟動后調用 ==vTaskPrioritySet()== API 函數進行修改。

在 文 件 ==FreeRTOSConfig.h== 中 設 定 的 編 譯 時 配 置 常 量==configMAX_PRIORITIES== 的值,即是最多可具有的優(yōu)先級數目。

低優(yōu)先級號表示任務的優(yōu)先級低,優(yōu)先級號 0 表示最低優(yōu)先級。有效的優(yōu)先級號范圍從 0 到(configMAX_PRIORITES – 1)。

調度器基本工作原理

  • 調度器保證總是在所有可運行的任務中選擇具有最高優(yōu)先級的任務,并使其進入運行態(tài)。如果被選中的優(yōu)先級上具有不止一個任務,調度器會讓這些任務輪流執(zhí)行。(任務在時間片起始時刻進入運行態(tài),在時間片結束時刻又退出運行態(tài)。)

  • 要能夠選擇下一個運行的任務,調度器需要在每個時間片的結束時刻運行自己本身。一個稱為心跳(tick,有些地方被稱為時鐘滴答,本文中一律稱為時鐘心跳)中斷的周期性中斷用于此目的。==時間片的長度通過心跳中斷的頻率進行設定==,心跳中斷頻率由FreeRTOSConfig.h 中的編譯時配置常量 ==configTICK_RATE_HZ== 進行配置。比如說,如果 configTICK_RATE_HZ 設為 100(HZ),則時間片長度為 10ms。

  • 簡略程序執(zhí)行流程

    image

    注意:如果高優(yōu)先級任務不釋放資源,低優(yōu)先級任務是沒辦法執(zhí)行任務的。所以需要引入“非運行態(tài)”。

改變任務優(yōu)先級

API 函數 vTaskPriofitySet()可以用于在調度器啟動后改變任何任務的優(yōu)先級。

void vTaskPrioritySet( xTaskHandle pxTask,   //被修改優(yōu)先級的任務句柄(即目標任務),任務可以通過傳入 NULL 值來修改自己的優(yōu)先級。
                    unsigned portBASE_TYPE uxNewPriority ); //目標任務將被設置到哪個優(yōu)先級上。

uxTaskPriorityGet() API 函數用于查詢一個任務的優(yōu)先級。

unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask );//被查詢任務的句柄(目標任務) ,任務可以通過傳入 NULL 值來查詢自己的優(yōu)先級.

改變任務優(yōu)先級示例

void vTask1( void *pvParameters )
{
    unsigned portBASE_TYPE uxPriority;
    /* 本任務將會比任務2更先運行,因為本任務創(chuàng)建在更高的優(yōu)先級上。任務1和任務2都不會阻塞,所以兩者要么處于就緒態(tài),要么處于運行態(tài)。查詢本任務當前運行的優(yōu)先級 – 傳遞一個NULL值表示說“返回我自己的優(yōu)先級”。 */
    uxPriority = uxTaskPriorityGet( NULL );
    for( ;; )
    {
        /* Print out the name of this task. */
        vPrintString( "Task1 is running\r\n" );
        /* 把任務2的優(yōu)先級設置到高于任務1的優(yōu)先級,會使得任務2立即得到執(zhí)行(因為任務2現在是所有任務中具有最高優(yōu)先級的任務)。注意調用vTaskPrioritySet()時用到的任務2的句柄。程序清單24將展示如何得到這個句柄。 */
        vPrintString( "About to raise the Task2 priority\r\n" );
        vTaskPrioritySet( xTask2Handle, ( uxPriority + 1 ) );
        /* 本任務只會在其優(yōu)先級高于任務2時才會得到執(zhí)行。因此,當此任務運行到這里時,
        任務2必然已經執(zhí)行過了,并且將其自身的優(yōu)先級設置回比任務1更低的優(yōu)先級。 */
    }
}

void vTask2( void *pvParameters )
{
    unsigned portBASE_TYPE uxPriority;
    /* 任務1比本任務更先啟動,因為任務1創(chuàng)建在更高的優(yōu)先級。任務1和任務2都不會阻塞,
    所以兩者要么處于就緒態(tài),要么處于運行態(tài)。查詢本任務當前運行的優(yōu)先級 – 傳遞一個NULL值表示說“返回我自己的優(yōu)先級”。 */
    uxPriority = uxTaskPriorityGet( NULL );
    for( ;; )
    {
        /* 當任務運行到這里,任務1必然已經運行過了,并將本身務的優(yōu)先級設置到高于任務1本身。 */
        vPrintString( "Task2 is running\r\n" );
        /* 將自己的優(yōu)先級設置回原來的值。傳遞NULL句柄值意味“改變我己自的優(yōu)先級”。
        把優(yōu)先級設置到低于任務1使得任務1立即得到執(zhí)行 – 任務1搶占本任務。 */
        vPrintString( "About to lower the Task2 priority\r\n" );
        vTaskPrioritySet( NULL, ( uxPriority - 2 ) );
    }
}

/* 聲明變量用于保存任務2的句柄。 */
xTaskHandle xTask2Handle;

int main( void )
{
    /* 任務1創(chuàng)建在優(yōu)先級2上。任務參數沒有用到,設為NULL。任務句柄也不會用到,也設為NULL */
    xTaskCreate( vTask1, "Task 1", 1000, NULL, 2, NULL );
    /* The task is created at priority 2 ______^. */
    /* 任務2創(chuàng)建在優(yōu)先級1上 – 此優(yōu)先級低于任務1。任務參數沒有用到,設為NULL。但任務2的任務句柄會被
    用到,故將xTask2Handle的地址傳入。 */
    xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, &xTask2Handle );
    /* The task handle is the last parameter _____^^^^^^^^^^^^^ */
    /* Start the scheduler so the tasks start executing. */
    vTaskStartScheduler();
    /* If all is well then main() will never reach here as the scheduler will
    now be running the tasks. If main() does reach here then it is likely that
    there was insufficient heap memory available for the idle task to be created.
    CHAPTER 5 provides more information on memory management. */
    for( ;; );
}

?

5.非運行態(tài)

為了使我們的任務切實有用,我們需要通過某種方式來進行事件驅動。一個事件驅動任務只會在事件發(fā)生后觸發(fā)工作(處理),而在事件沒有發(fā)生時是不能進入運行態(tài)的。調度器總是選擇所有能夠進入運行態(tài)的任務中具有最高優(yōu)先級的任務。一個高優(yōu)先級但不能夠運行的任務意味著不會被調度器選中,而代之以另一個優(yōu)先級雖然更低但能夠運行的任務。因此,采用事件驅動任務的意義就在于任務可以被創(chuàng)建在許多不同的優(yōu)先級上,并且最高優(yōu)先級任務不會把所有的低優(yōu)先級任務餓死。

阻塞狀態(tài)

如果一個任務正在等待某個事件,則稱這個任務處于”阻塞態(tài)(blocked)”。阻塞態(tài)是非運行態(tài)的一個子狀態(tài)

任務可以進入阻塞態(tài)以等待以下兩種不同類型的事件:

  1. 定時(時間相關)事件,延遲到期或精準時間到點。比如說某個任務可以進入阻塞態(tài)以延遲 10ms。

    • 延遲到期
    void vTaskDelay( portTickType xTicksToDelay );
    //xTicksToDelay:延遲多少個心跳周期。調用該延遲函數的任務將進入阻塞態(tài),經延遲指定的心跳周期數后,再轉移到就緒態(tài)。
    //常量portTICK_RATE_MS可以用來在毫秒和心跳周期之間相換轉換。如:vTaskDelay( 250/portTICK_RATE_MS );
    
    • 精準時間到期

    vTaskDelayUntil()的參數就是用來指定任務離開阻塞態(tài)進入就緒態(tài)那一刻的精確心跳計數值??梢杂糜趯崿F一個固定執(zhí)行周期的需求.

    void vTaskDelayUntil( portTickType * pxPreviousWakeTime, portTickType xTimeIncrement );
    //pxPreviousWakeTime保存了任務上一次離開阻塞態(tài)(被喚醒)的時刻。這個時刻被用作一個參考點來計算該任務下一次離開阻塞態(tài)的時刻。
    //pxPreviousWakeTime 指 向 的 變 量 值 會 在 API 函數vTaskDelayUntil()調用過程中自動更新,應用程序除了該變量第一次初始化外,通常都不要修改它的值。
    
    //xTimeIncrement: 指定任務固定周期執(zhí)行的頻率
    
  1. 同步事件,源于其它任務或中斷的事件。
    例如:
    1. FreeRTOS 的隊列
    2. 二值信號量
    3. 計數信號量
    4. 互斥信號量
    5. 互斥量

掛起狀態(tài)

“掛起(suspended)”也是非運行狀態(tài)的子狀態(tài)。處于掛起狀態(tài)的任務對調度器而言是不可見的。

讓一個任務進入掛起狀態(tài)的唯一辦法就是調用 ==vTaskSuspend()== API 函數;而 把 一 個 掛 起 狀 態(tài) 的 任 務 喚 醒 的 唯 一 途 徑 就 是 調 用 ==vTaskResume()== 或==vTaskResumeFromISR()== API 函數。

就緒狀態(tài)

如果任務處于非運行狀態(tài),但既沒有阻塞也沒有掛起,則這個任務處于就緒(ready,準備或就緒)狀態(tài)。處于就緒態(tài)的任務能夠被運行。

完整的狀態(tài)轉移圖

image

延時任務示例

void vTaskFunction( void *pvParameters )
{
    char *pcTaskName;
    /* The string to print out is passed in via the parameter. Cast this to a
    character pointer. */
    pcTaskName = ( char * ) pvParameters;
    /* As per most tasks, this task is implemented in an infinite loop. */
    for( ;; )
    {
        /* Print out the name of this task. */
        vPrintString( pcTaskName );
        /* 延遲一個循環(huán)周期。 調用vTaskDelay()以讓任務在延遲期間保持在阻塞態(tài)。延遲時間以心跳周期為單位,常量portTICK_RATE_MS可以用來在毫秒和心跳周期之間相換轉換。本例設定250毫秒的循環(huán)周期。 */
        vTaskDelay( 250 / portTICK_RATE_MS );
    }
}

精準周期任務示例

void vTaskFunction( void *pvParameters )
{
    char *pcTaskName;
    portTickType xLastWakeTime;
    /* The string to print out is passed in via the parameter. Cast this to a
    character pointer. */
    pcTaskName = ( char * ) pvParameters;
    /* 變量xLastWakeTime需要被初始化為當前心跳計數值。說明一下,這是該變量唯一一次被顯式賦值。之后,xLastWakeTime將在函數vTaskDelayUntil()中自動更新。 */
    xLastWakeTime = xTaskGetTickCount();
    /* As per most tasks, this task is implemented in an infinite loop. */
    for( ;; )
    {
        /* Print out the name of this task. */
        vPrintString( pcTaskName );
        /* 本任務將精確的以250毫秒為周期執(zhí)行。同vTaskDelay()函數一樣,時間值是以心跳周期為單位的,可以使用常量portTICK_RATE_MS將毫秒轉換為心跳周期。變量xLastWakeTime會在vTaskDelayUntil()中自動更新,因此不需要應用程序進行顯示更新。 */
        vTaskDelayUntil( &xLastWakeTime, ( 250 / portTICK_RATE_MS ) );
    }
}

6.空閑任務與鉤子函數

當調用 vTaskStartScheduler()時,調度器會自動創(chuàng)建一個空閑任務??臻e任務是一個非常短小的循環(huán)??臻e任務擁有最低優(yōu)先級(優(yōu)先級 0)以保證其不會妨礙具有更高優(yōu)先級的應用任務進入運行態(tài)。

空閑任務鉤子函數

通過空閑任務鉤子函數(或稱回調, hook, or call-back),可以直接在空閑任務中添加應用程序相關的功能。==空閑任務鉤子函數會被空閑任務每循環(huán)一次就自動調用一次==。

//函數原型
void vApplicationIdleHook( void );
應用范圍
  • 執(zhí)行低優(yōu)先級,后臺或需要不停處理的功能代碼。
  • 測試系統(tǒng)處理裕量。
  • 將處理器配置到低功耗模式——提供一種自動省電方法。
實現限制
  • 絕不能阻塞或掛起。
  • 如果應用程序用到了 vTaskDelete() AP 函數,則空閑鉤子函數必須能夠盡快返回。讓空閑任務負責回收內核資源。

空閑任務鉤子函數使用示例

FreeRTOSConfig.h 中的配置常量 configUSE_IDLE_HOOK 必須定義為 1,這樣空閑任務鉤子函數才會被調用。

對應用任務實現函數進行了少量的修改,用以打印輸出變量 ulIdleCycleCount 的值。

/* Declare a variable that will be incremented by the hook function. */
unsigned long ulIdleCycleCount = 0UL;
/* 空閑鉤子函數必須命名為vApplicationIdleHook(),無參數也無返回值。 */
void vApplicationIdleHook( void )
{
    /* This hook function does nothing but increment a counter. */
    ulIdleCycleCount++;
}

void vTaskFunction( void *pvParameters )
{
    char *pcTaskName;
    /* The string to print out is passed in via the parameter. Cast this to a
    character pointer. */
    pcTaskName = ( char * ) pvParameters;
    /* As per most tasks, this task is implemented in an infinite loop. */
    for( ;; )
    {
        /* 打印輸出任務名,以及調用計數器ulIdleCycleCount的值。 */
        vPrintStringAndNumber( pcTaskName, ulIdleCycleCount );
        /* Delay for a period for 250 milliseconds. */
        vTaskDelay( 250 / portTICK_RATE_MS );
    }
}

//鉤子函數相當于空閑任務一個子函數,因此在mcu空閑時期,鉤子函數計數會一直增加。

7.刪除任務

任務可以使用 API 函數 vTaskDelete()刪除自己或其它任務。

空閑任務的責任是要將分配給已刪除任務的內存釋放掉。因此有一點很重要,那就
是使用 vTaskDelete() API 函數的任務千萬不能把空閑任務的執(zhí)行時間餓死。
(只有內核為任務分配的內存空間才會在任務被刪除后自動回收。
任務自己占用的內存或資源需要由應用程序自己顯式地釋放。)

void vTaskDelete( xTaskHandle pxTaskToDelete ); //被刪除任務的句柄(目標任務),任務可以通過傳入 NULL 值來刪除自己。

刪除任務示例

void vTask1( void *pvParameters )
{
    const portTickType xDelay100ms = 100 / portTICK_RATE_MS;
    for( ;; )
    {
        /* Print out the name of this task. */
        vPrintString( "Task1 is running\r\n" );
        /* 創(chuàng)建任務2為最高優(yōu)先級。 */
        xTaskCreate( vTask2, "Task 2", 1000, NULL, 2, &xTask2Handle );
        /* The task handle is the last parameter _____^^^^^^^^^^^^^ */
        /* 因為任務2具有最高優(yōu)先級,所以任務1運行到這里時,任務2已經完成執(zhí)行,刪除了自己。任務1得以執(zhí)行,延遲100ms */
        vTaskDelay( xDelay100ms );
    }
}

void vTask2( void *pvParameters )
{
    /* 任務2什么也沒做,只是刪除自己。刪除自己可以傳入NULL值,這里為了演示,還是傳入其自己的句柄。 */
    vPrintString( "Task2 is running and about to delete itself\r\n" );
    vTaskDelete( xTask2Handle );
}

int main( void )
{
    /* 任務1創(chuàng)建在優(yōu)先級1上 */
    xTaskCreate( vTask1, "Task 1", 1000, NULL, 1, NULL );
    /* The task is created at priority 1 ______^. */
    /* Start the scheduler so the tasks start executing. */
    vTaskStartScheduler();
    /* main() should never reach here as the scheduler has been started. */
    for( ;; );
}

8.調度算法

優(yōu)先級搶占式調度

  • 每個任務都賦予了一個優(yōu)先級。
  • 每個任務都可以存在于一個或多個狀態(tài)。
  • 在任何時候都只有一個任務可以處于運行狀態(tài)。
  • 調度器總是在所有處于就緒態(tài)的任務中選擇具有最高優(yōu)先級的任務來執(zhí)行。

這種類型的調度方案被稱為”固定優(yōu)先級搶占式調度”。每個任務都被賦予了一個優(yōu)先級,這個優(yōu)先級不能被內核本身改變(只能被任務修改)。

任務可以在阻塞狀態(tài)等待一個事件,當事件發(fā)生時其將自動回到就緒態(tài)。時間事件
發(fā)生在某個特定的時刻,比如阻塞超時。時間事件通常用于周期性或超時行為。任務或
中斷服務例程往隊列發(fā)送消息或發(fā)送任務一種信號量,都將觸發(fā)同步事件。同步事件通
常用于觸發(fā)同步行為,比如某個外圍的數據到達了。

協(xié)作式調度

采用一個純粹的協(xié)作式調度器,只可能在運行態(tài)任務進入阻塞態(tài)或是運行態(tài)任務顯
式調用 taskYIELD()時,才會進行上下文切換。任務永遠不會被搶占,而具有相同優(yōu)先
級的任務也不會自動共享處理器時間。協(xié)作式調度的這作工作方式雖然比較簡單,但可
能會導致系統(tǒng)響應不夠快。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容