實時作業系統的任務排程示例之優先順序反轉

2021-07-14 19:33:27 字數 4718 閱讀 4873

1 什麼是優先順序反轉?

目前市面流行的實時作業系統都是採用可搶占式的基於優先順序的排程方式,其保證了處於就緒狀態的優先順序高的任務可以先於優先順序低的任務而執行。但這並不是絕對的,優先順序反轉是實時系統中的乙個經典特例。其大體流程如下:

假設系統中有3個task,優先順序分別為高、中、低,以task_high,task_middle,task_low代稱

1) 一開始,task_high,task_middle處於掛起狀態,task_low在執行

2) task_low要訪問共享資源,獲取了乙個訊號量semaphore

3) 這之後task_high等待的事件到來,task_high搶占了task_low開始執行,在執行過程中,也需要訪問同一共享資源,所以也要去拿semaphore,由於它已經被task_low取得,所以task_high再次被掛起。

4)task_low繼續執行

5) task_middle等待的事件這時候到來,它從task_low手裡搶來了cpu,開始執行

6) task_middle執行結束或再次掛起,讓出cpu

7) task_low繼續執行,完成共享資源的訪問,釋放semaphore

8) task_high獲得semaphore,得以繼續執行。

task_middle的優先順序是低於task_high的,原則上應該是task_high優先獲得cpu得到執行。但是在這個過程中,由於共享資源的競爭訪問,導致了

高優先順序的任務並不能被優先排程

執行的順序看起來好像是高優先順序任務task_high和中

優先順序任務task_middle,兩者之間的優先順序調換了,反轉

了一樣。

2 優先順序反轉會有什麼危害?

乍一看上面的這個執行流程,很多同學可能覺得,不就是有個task本來想讓它早點執行,結果因為乙個意外,晚一點點執行了嗎,能有什麼後果?

是的,如果上面案例中task_middle占用的時間較短,可能問題還不大,但是如果運氣不好,task_middle執行的是乙個耗時的操作,或者task_middle不是乙個任務,而是一堆優先順序處於task_low和task_high之間,正好在這期間需要執行呢?那那task_low始終無法釋放訊號量,task_high也要被delay很久才能獲得cpu。

嵌入式實時作業系統,最重要的指標就是:確保重要的任務執行時間是可**的,有乙個不能容忍的deadline,要確保任何時刻都不能超過某個時間。考慮一下汽車的電子系統裡控制受撞擊後彈出安全氣囊的task,如果執行時間被delay會有什麼後果。

在有些場景下,會導致整個系統崩潰,有乙個高大上的典型案例,美國的

火星探路者號在一次執行任務時,就是因為重要

任務被有delay,結果導致了系統復位、資料丟失。有

興趣的朋友可以google一下 

3 優先順序反轉的示例

本節參考第一節的描述設計了乙個簡單的實驗,來演示優先順序反轉的執行過程,使用的rtos是nucleus。

應用建立了3個task,其優先順序分別為149,150,151,對應的task名分別為task_high,task_middle和task_low

建立2個訊息佇列,讓task_high和task_middle可以阻塞在上面並可在適當時間將它們喚醒

還有乙個訊號量,模擬task_high和task_low訪問共享資源時的互斥

nu_create_queue(&

sync_queue1

,"queue1",

queue_buf1,

queue_size,

nu_fixed_size,

1, nu_fifo);

nu_create_queue(&sync_queue2,"queue2",

queue_buf2,

queue_size,

nu_fixed_size,

1, nu_fifo);

nu_create_semaphore(&sync_sema,"sync_sema",

1,nu_priority);

nu_create_task(&hightask,"",

task_high,

0,nu_null,

task_stack1,

stack_size,

149,

0,nu_preempt, nu_start);

nu_create_task(&middletask, "" ,task_middle,  0,  nu_null, task_stack2,stack_size, 150, 0,nu_preempt, nu_start);

nu_create_task(&lowtask, "",task_low,  0,  nu_null, task_stack3,  stack_size, 151, 0,nu_preempt, nu_start);

static int a = 100,b = 50,tmp;

void swap_tmp()  //純粹是為了演示延遲的函式,因為如果就寫for迴圈空轉可能會被編譯器優化掉

void task_high(unsigned argc, void* argv)

void task_middle(unsigned argc, void* argv)

void task_low(unsigned argc, void* argv)

輸出的log結果如下所示,很顯然task_high的do works比task_middle的do works要晚得到執行:

[0:0:16:373]  task_high start: priority 149

[0:0:16:383]  task_middle start: priority 150

[0:0:16:388]  task_low start: priority 151,obtain semaphore

[0:0:17:033]  task_low. do 1-round works

[0:0:18:323]  task_low. send message to wakeup task_high

[0:0:18:323]  task_high. obtain semaphore: 149

[0:0:18:968]  task_low. do 2-round works

[0:0:20:257]  task_low. send message to wakeup task_middle

[0:0:20:902]  task_middle. do works

[0:0:22:837]  task_low. do 3-round things

[0:0:24:127]  task_low. release semaphore

[0:0:24:773]  task_high. do works 

4 怎麼避免優先順序反轉?

在訊號量的結構體sm_scb裡增加乙個欄位tc_tcb* sm_own_task。用來儲存當前擁有訊號量的task指標

nu_obtain_semaphore的函式實現裡,如果能成功獲得訊號的分支,加上一句

semaphore->sm_own_task = (tc_tcb*)tct_current_thread();

task_high函式修改如下,在獲取訊號量之前,將task_low的優先順序暫時提公升至與task_high一樣,獲得訊號量之後再將其恢復。

void task_high(unsigned argc, void* argv)

nu_obtain_semaphore(&sync_sema,nu_suspend);

if (ptcb)

for (i=0;i<0xffffff;i++)

swap_tmp();

printf("task_high. do works");

} 執行起來輸出的log如下:

[0:0:16:389]  task_high start: priority149

[0:0:16:399]  task_middle start: priority 150

[0:0:16:405]  task_low start: priority 151,obtain semaphone

[0:0:17:050]  task_low. do 1-round works

[0:0:18:339]  task_low. send message to wakeup task_high

[0:0:18:339]  task_high. obtain semaphone: 149

[0:0:18:339]  task_high. improve temporary priority //臨時提公升了task_low的優先順序

[0:0:18:984]  task_low. do 2-round works

[0:0:20:274]  task_low. send message to wakeup task_middle

[0:0:20:918]  task_low. do 3-round things

[0:0:22:208]  task_low. release semaphore

[0:0:22:208]  task_high. restore temporary priority  //task_low的優先順序恢復

[0:0:22:853]  task_high. do works 

[0:0:26:080]  task_middle. do works  //task_middle的執行順序排在了task_high之後

實時作業系統的任務排程示例之搶占

什麼是任務搶占?實時作業系統大多都是基於優先順序排程的搶占式的核心,這句話每本關於rtos的資料都有。樓主最怕這種每個字都認識,但連到一起就很不好理解的書面語言。直白點說,就是 1 每個任務都有自己的優先順序,一般都是在create task的時候以函式入參的形式確定,有的rtos也提供api允許應...

ucosii實時作業系統的任務排程

嵌入式作業系統的任務排程演算法好壞在很大程度上決定了該系統的執行效率,由於其執行的頻率極高,所以在任務排程函式的實現上,對於效率的要求可以用苛刻來形容。基於優先順序的任務排程總結下來就是做了兩件事 1 找到優先順序最高的就緒態任務 2 切換任務上下文並開始執行該任務 第二步的切換上下文對個各個rto...

實時作業系統之任務

任務可以以下列狀態之一存在 有效的任務狀態轉換 任務優先順序 每個任務都分配了乙個從0到 configmax priorities 1 的優先順序,其中configmax priorities在freertosconfig.h中定義。如果正在使用的埠實現了使用 計數前導零 型別指令 用於在單條指令中...