嵌入式與裸機

優先順序反轉(priority inversion)與優先順序繼承

想像三個人共用一件工具,比方說一把梯子。一位低優先的工人借走梯子。在歸還之前,一位緊急的高優先工人到來、需要同一把梯子——於是他必須等低優先工人用完。到這裡只是單純的共享。但現在一波又一波的中優先工人不斷抓走那位低優先工人、把他擠到一旁去做別的事,於是低優先工人始終無法用完並歸還梯子。緊急的工人卡在那裡等,被一些「不如」自己重要的任務間接擋住。這個顛倒的局面就是優先順序反轉。

精確地說:優先順序反轉發生在一個高優先任務被擋住、等待一個由低優先任務持有的資源(通常是互斥鎖),而那個低優先任務自己又因中優先任務不斷搶占它而無法執行——因此無法釋放鎖。高優先任務實際上在等中優先任務,即使它的位階高於它們,這違反了優先順序的全部意義。在有上界的情況下,高任務只需等低任務做完其臨界區間;危險的是「無上界」優先順序反轉,不相干的中任務可把等待無限延長。經典的真實案例是 NASA 1997 年的火星探路者號漫遊車,它一再重置,正是因為一個高優先任務這樣被擋住。標準解法是優先順序繼承:當低優先任務持有高優先任務想要的鎖時,低任務「暫時」繼承高任務的優先順序,於是中任務再也無法搶占它;一旦它釋放鎖,就降回自己的優先順序。另一個替代是優先順序天花板協定(priority ceiling protocol),互斥鎖帶有一個優先天花板,任務在取得時被提升到該層級。

它之所以重要,是因為優先順序反轉可能無聲地摧毀即時系統的時序保證——一個高優先期限被錯過,即使 CPU 當時「有空」,而且它間歇出現、難以重現。誠實的重點:優先順序繼承為反轉設上界(把它限制在相關臨界區間的長度內)但「不」消除所有阻塞,所以你仍要讓臨界區間簡短;許多 RTOS 互斥鎖把優先順序繼承當成你必須開啟的選項;而繼承只解決互斥鎖造成的那種情況,並不解決諸如過長 ISR 或非優先化資源所致的延遲。開啟繼承並讓上鎖區間極小,是務實的防禦。

任務(H 高、M 中、L 低)共用一個互斥鎖: 1. L 取得互斥鎖,開始它的臨界區間。 2. H 醒來、想要互斥鎖 -> 阻塞,等待 L。 3. M 醒來並搶占 L(M > L)。L 無法完成。 4. H 現在間接等待 M:「無上界」反轉。 解法──優先順序繼承: 在步驟 2,L 被提升到 H 的優先順序,於是 M 無法搶占 L; L 快速完成、釋放互斥鎖,然後降回;H 執行。

沒有繼承時,中優先任務可永久餓死持鎖的低優先任務,擋住高任務。繼承會提升持鎖者,讓它快速完成。

優先順序繼承為優先順序反轉設上界,但不移除所有阻塞——無論如何都要讓臨界區間簡短。許多 RTOS 互斥鎖只在你開啟該選項時才繼承,而繼承對於過長 ISR 造成的延遲毫無幫助。

又稱
priority inversionpriority inheritanceunbounded priority inversion優先順序反轉優先順序繼承