嵌入式控制系統設計/即時作業系統
|
The Wikibook of
嵌入式控制系統設計
|
即時作業系統是即時執行的作業系統 (OS)。這意味著即時作業系統與通用作業系統或嵌入式作業系統 (EOS) 有不同的目的。通用作業系統旨在最大限度地提高任務(資料?)的平均吞吐量,而在即時作業系統中,關鍵是確定性。即時任務必須在特定的確定性截止日期之前完成。這要求對刺激的響應必須始終在恆定時間內執行。違反規定的時間約束(通常)被認為是災難性的。非即時系統被認為是正確的,如果某些輸入對映到某些輸出:系統(程式碼和硬體)在給定當前狀態和輸入的情況下必須始終執行正確的事情。即時系統必須實現相同的邏輯正確性,但必須使用恆定時間演算法實現這種邏輯正確性。由於對執行行為的這一附加要求,即時作業系統的實現與其他作業系統有很大不同,儘管一般原則相同。
實現相同邏輯行為但在恆定時間內使用不同演算法的示例:假設系統必須響應 25 種不同的輸入,並且對於每種輸入,都會生成一個特定的輸出。完全可以接受的非即時實現是使用一個迴圈來遍歷可能的輸入列表以確定正確的輸出。但是,這種迴圈的執行時間會有所不同:碰巧位於列表中的第一個輸入比碰巧位於列表中的第 25 個輸入識別得快得多。恆定時間方法是使用雜湊,這樣輸入本身就可以立即對映到正確的輸出。因此,即時解決方案將使用雜湊而不是迴圈來在多個輸入和輸出之間進行選擇。事實上,存在執行時間不恆定的迴圈肯定表明演算法不是即時演算法。
即時與非即時之間的另一個明顯區別是處理共享資源爭用方式。在即時系統中,資料爭用在最大程度上避免,而在發生爭用時,可以使用零個或一個訊號量,通常是零個訊號量,而不是使用共享爭用資源的那些之間的無鎖協議。為了確保程式碼更改不會意外影響此硬性且絕對的限制,在持有訊號量時不允許進行過程呼叫。在非即時系統中,可以使用任意數量的巢狀訊號量,只要它們按獲取的順序釋放即可。
有些人區分軟即時作業系統和硬即時作業系統。從某種意義上說,這些系統是相同的:在這兩種情況下,都有必要證明在任何截止日期之前始終有空閒時間,因此即時截止日期將得到滿足。但是,方法從根本上不同:軟即時意味著開發人員希望他們的計算平臺足夠快以跟上;硬即時意味著系統是在僅使用即時演算法的情況下構建、設計和實現的。
即時作業系統有 4 個主要職責
- 任務管理和排程;
- (延遲)中斷服務;
- 程序間通訊和同步;以及
- 記憶體管理。
由於“高階”程式語言和作業系統構造中的確定性常常受到影響,因此即時設計人員比“普通”應用程式開發人員更直接地面對作業系統級別的概念、計時、記憶體和效率問題。
任務管理的職責包括對多個任務進行排程。由於排程純粹是開銷,因此即時作業系統實現的排程演算法並不複雜。
演算法越簡單,執行任務的速度就越快,佔用記憶體就越少,可預測性就越強。
解決排程問題最簡單的方法是為所有任務分配(靜態/動態)優先順序。例如,很明顯,在 RT 任務期間不應該進行任務的建立和刪除。不幸的是,基於優先順序的排程有一些明顯的問題(“飢餓”,“死鎖”)以及一些更微妙的問題(“活鎖”,“優先順序反轉”)。
使用優先順序意味著使用 **搶佔**:正在執行的低優先順序任務應該被中斷,以便執行更高優先順序的任務。但是,基於優先順序的排程對於應用程式程式設計師來說很困難:他們必須嘗試將應用程式中不同執行緒之間通常複雜的同步相互依賴關係對映到優先順序提供的線性尺度上。這種排程方法的問題在於,它是一種間接的方式來指定如何應對時間和同步約束:這些約束被轉換為優先順序。
在隨著時間推移而增長的 RT 應用程式中,經常觀察到一種現象,即程式設計師傾向於提高某些執行緒的優先順序,每當他們注意到新功能的引入會擾亂現有執行緒的同步時。
實際上,系統設計人員必須決定哪些任務獲得哪些優先順序,因為許多程式設計師都渴望優先考慮自己的任務。在這種情況下,重要的是要看到只有一個任務應該獲得最高優先順序。可以說,該任務是在硬/軟即時上下文中“最難”的任務。
在排程程式中適當平衡延遲、吞吐量和公平性是一個未解決的問題。[1]
RT 任務的時間約束通常以微秒而不是毫秒為單位指定。因此,用於儲存時間的資料結構應該適應這種更高的速率,以避免溢位。如果作業系統以納秒為單位計數,則 32 位計數器將很快溢位(例如(2^32)/(10^9) = 4 秒到溢位)。在這種情況下,使用 64 位計數器會更合適。這些計時器被稱為高解析度計時器。
- 64 位計數器的原子操作問題?
ISR 包含一個或多個看門狗計時器。 如果當前正在執行的任務花費了太多時間,而實際上什麼也沒有發生,這樣的計時器會觸發恢復任務。透過這種方式,可以覆蓋可能的故障(例如掛起)。如果看門狗為此而設計,它可以提供除錯資訊。
看門狗計時器還可以觸發控制系統進入安全狀態,例如關閉電機、高壓電源輸出和其他可能危險的子系統,直到故障清除。
大多數現代作業系統都是中斷驅動的。這意味著如果沒有等待執行的程序、沒有請求服務的 I/O 裝置,也沒有等待回覆的使用者,作業系統就會等待發生某些事情。
事件幾乎總是由 中斷 或 陷阱 訊號發出。陷阱是由軟體產生的中斷,要麼是錯誤發生後,要麼是使用者程式請求執行系統服務後的結果。對於每種型別的中斷,作業系統中都有單獨的程式碼段可用。這些程式碼段決定了作業系統如何對特定事件做出反應。外設硬體的特殊之處在於它們可以非同步地請求作業系統的注意,例如,當它們想要使用作業系統的服務時,作業系統必須確保它已準備好為請求提供服務。這些請求由中斷髮出訊號。在即時任務中,其他任務(磁碟讀寫、訪問 USB 等)應該被推遲,這是很明顯的。
原則上,作業系統不參與硬體中斷觸發的程式碼執行:這是由 CPU 在沒有軟體干擾的情況下完成的。作業系統確實會影響
- 將記憶體地址連線到每條中斷線,以及
- 中斷服務完成後必須執行的操作。
這對 RTOS 意味著什麼?中斷必須由所謂的 ISR(中斷服務例程)處理。ISR 完成其工作越快,RTOS 的即時效能就越好,因為其他任務的延遲就越少。讓我們看一下中斷驅動的系統的硬體和軟體方面(許多 RTOS 和 EOS 都是),以及它們的典型元件。
- 中斷向量與非向量中斷處理
- 中斷向量用於具有多個硬體中斷線的系統,所有這些中斷線都被組裝在一箇中斷向量中。中斷向量是一個指向中斷服務例程的指標陣列。在非向量系統中,當發生中斷時,控制權將轉移到單個例程,該例程決定如何處理中斷。對於 RT 系統,中斷向量是可行的,因為它降低了中斷的處理時間。
- 邊沿觸發和電平觸發中斷
- 外設可以透過兩種不同的方式傳輸其中斷訊號。邊沿觸發中斷存在硬體或軟體丟失中斷的風險,通常不是有效的解決方案。對於 RTOS 和 EOS,其中確定性是一個重要因素,電平觸發中斷是有利的。
- 中斷控制器
- 這是一塊硬體,它將作業系統與中斷線的電子細節隔離開來。一些能夠排隊中斷,以確保沒有中斷丟失(確定性!),一些允許為不同的中斷分配優先順序。例如 PIC 或 APIC。在 RTOS 和 EOS 中使用 APIC 是有利的。
中斷服務例程
當在中斷向量中為其註冊了 ISR 的中斷線上發生中斷時,將呼叫此軟體例程。作業系統不會干預 ISR 的啟動,所有操作都由 CPU 完成。當前任務的上下文儲存在該任務的堆疊上,因此每個任務必須獲得足夠的堆疊空間來應對 ISR 開銷。ISR 應該儘可能短,因為它是在中斷停用的情況下執行的,這將阻止其他中斷被服務和任務繼續(RTOS!)。進一步處理是 DSR 的目標。從 ISR 到 DSR 的資料獲取應該以非阻塞的方式進行。
陷阱處理程式/服務請求
軟體中斷由處理器本身呼叫,例如在暫存器溢位、錯誤等情況下。它們類似於硬體中斷,但它們是在硬體中斷啟用的情況下執行的。
中斷延遲
這是硬體中斷到達和執行相應 ISR 之間的時間。這是一個統計量,在 RTOS 中,重要的是嘗試使此數字儘可能低,並儘可能確定性。具有多級快取和管道的現代處理器容易出現不確定性
中斷優先順序
在中斷處理中增加了複雜性和已知的問題/機會,與任務排程有關。在 RTOS/EOS 中不需要。
中斷巢狀
增加了程式碼的複雜性。在確定性環境中,這是不需要的。
中斷共享
許多系統允許不同的外設連結到同一個硬體中斷。服務此中斷的 ISR 必須能夠找出哪個裝置生成了中斷。RTOS 不支援此功能;它們只允許每個 IRQ 一個 ISR,以便儘可能確定性。因此,RT 系統設計人員在將介面卡放入計算機時應該小心,因為所有您想要安裝即時驅動程式的介面卡都必須連線到不同的中斷線!
由於存在必須相互通訊的任務,因此需要對不同任務進行同步,以及在它們之間進行資料交換。RTOS 應該提供簡單安全的 IPC 原語,程式設計師可以使用它們來構建他們的軟體系統。這些原語可以對任務排程產生不同的影響(阻塞、非阻塞、條件阻塞、帶有超時時間的阻塞),可以使用不同程度的耦合(命名連線、廣播、黑板、物件請求代理)和緩衝。
多工和多處理器系統中大多數資源共享(或分配)問題根源在於,對資源的操作通常不能原子地執行,即,不能像執行單個、不可中斷的指令一樣執行,該指令在零時間內完成。實際上,與資源互動的任務可以在任何時刻被搶佔,因此,當它再次被重新排程時,它不能簡單地認為它現在使用的資料與搶佔之前處於相同狀態。程式碼的某些部分被稱為“臨界區”,因為對於程式碼的有效性,用於特定語句的資料訪問必須原子地執行:不能被其他任何事物中斷。(大多數)給定處理器的機器程式碼指令原子地執行,但高階程式語言中的指令通常被轉換為一系列機器程式碼指令。
主要問題被稱為“競爭條件”:兩個或多個任務互相競爭以獲取相同共享資源的訪問權。這些競爭條件的示例包括:死鎖、活鎖、飢餓。存在避免這些競爭條件的演算法,有些比其他演算法更復雜。
同步型別很多:屏障,訊號量,互斥鎖,自旋鎖,讀寫鎖(以及用於資料交換的無鎖)。
當任務到達其所謂的臨界區時,它請求一個鎖。現在,如果另一個任務沒有獲取鎖,它就可以獲取鎖並進入臨界區,或者它必須等待(“阻塞”,“睡眠”)直到另一個任務在退出其臨界區時釋放鎖。被阻塞的任務無法排程執行,因此在 RT 應用程式中要謹慎使用鎖!應用程式程式設計師應該確定由於其他任務持有的鎖導致任務可能被延遲的最長時間;並且此最大值應小於系統指定的時間約束。鎖的概念很容易導致任務排程中出現不可預測的延遲。它不直接保護資料,而是同步訪問資料的程式碼。與排程優先順序一樣,鎖為紀律嚴明的程式設計師提供了一種實現確定性效能指標的方法。但紀律不足以保證大型系統的一致性。
使用鎖的問題在於,它們使應用程式容易受到 優先順序反轉問題 的影響。這應該在設計階段加以預防。另一個問題是,當持有鎖的任務執行的 CPU 突然發生故障,或者當該任務進入陷阱和/或異常時,因為在這種情況下鎖不會被釋放,或者,充其量,其釋放會延遲。
訊號量 - 自旋鎖
訊號量是一種鎖,其鎖定任務的正常行為是進入睡眠狀態。因此,這會涉及上下文切換的開銷,所以不要將訊號量用於應該只花費很少時間的臨界區;在這種情況下,自旋鎖是更合適的選擇。
訊號量 - 互斥鎖
許多程式設計師也傾向於認為訊號量一定是比互斥鎖更基本的 RTOS 函式。事實並非總是如此,因為可以用互斥鎖和條件變數來實現計數訊號量。
自旋鎖
如果程式設計師足夠自律,可以謹慎使用自旋鎖,即用於保證非常短的臨界區,那麼自旋鎖就可以正常工作。原則上,自旋鎖引起的延遲不是確定的(非即時)。但如果排程和上下文切換時間大於保護自旋鎖的臨界區執行所需的時間,它們提供了一個很好的解決方案。
所有資料 IPC 交換機制都非常相似;作業系統為要交換的資料保留了一些記憶體空間,並使用一些同步 IPC 原語來讀取或寫入該記憶體空間。不同形式的資料交換之間的主要區別在於它們的策略。兩個或多個任務通常具有一些共享記憶體的形式。可能出現的問題是 RAM 中的可用空間,以及對共享資料新鮮度的控制。您指的是共享資料的新鮮度嗎?。共享記憶體具有塊裝置的屬性;程式可以按任意順序訪問裝置上的任意塊。字元裝置只能按線性順序訪問資料。FIFO就是這樣一個裝置。在即時系統中,即時端不需要鎖,因為沒有使用者程式可以中斷即時端。另一種 IPC 資料交換形式是使用訊息和郵箱。同樣對於即時系統,有一些需要注意的地方。使用動態記憶體分配的 IPC 方法不適用於即時系統。迴圈緩衝區是另一種 IPC 的可能性。不過,資料丟失可能會出現一些問題,因此即時系統使用一些特定選項。這些是:記憶體鎖定和緩衝區半滿。一個更好的解決方案是使用擺動緩衝區。這是一種高階迴圈緩衝區,也是一種無死鎖鎖。擺動緩衝區是非阻塞的,並且可能導致資料丟失。
作業系統的任務是記憶體分配和記憶體對映,以及在任務使用未分配的記憶體時採取行動(記憶體保護)。在即時系統中,記憶體管理中需要注意的一些事項是
快速且確定的記憶體管理
最簡單的方法是根本沒有記憶體管理。一些 RTOS/EOS 提供了一些基本的記憶體管理;透過系統呼叫進行記憶體分配和刪除。
頁面鎖定
MMU(記憶體管理單元)必須將即時記憶體任務的頁面鎖定在物理 RAM 中,以避免將頁面從機械磁碟驅動器交換到 RAM 的分頁開銷。
動態分配
任務的記憶體可能在任務的生命週期內發生變化,因此它會向作業系統請求更多記憶體。為了以確定性方式執行此操作,記憶體頁面需要一個鎖定的空閒頁面池。在即時任務中應謹慎使用動態分配。這也意味著應避免使用動態分配需求的 IPC。
記憶體對映
對映是一項配置活動,不應在即時環境中進行。
記憶體共享
這是兩個任務進行通訊的一種有效方式。作業系統負責 1) 共享記憶體的(去)分配,以及 2) 同步不同任務對該記憶體的訪問。使用共享記憶體本身並沒有什麼特別即時的地方,但分配它確實如此。共享記憶體池是在啟動時預留的物理記憶體塊,因此作業系統不會將其用於其程序。
RAM 磁碟
為了避免訪問硬碟的非確定性開銷,可用 RAM 的一部分可以用來模擬硬碟。為了獲得某種確定性的非易失性記憶體,設計人員可以選擇使用快閃記憶體盤。
一些現代記憶體管理系統非常複雜,以至於已經寫了一整本書來介紹它們——記憶體管理。
即時系統的目標是確定性地執行。確定性意味著兩個方面。首先,如果程序請求 CPU、RAM 或通訊,它應該從協調中收到它。其次,如果發生故障,系統應該知道該怎麼辦。對於系統設計者來說,即時應用程式最重要的特徵是排程任務、應對故障和使用可用資源。
系統設計者必須確保(至少是明確標識的小部分)系統的程序以可預測的方式進行排程,因為與定時相比,程序的順序是應用程式的重要組成部分。例如,可以透過排程程式來排程程序。順序以確定性方式確定至關重要,但排程程式可能不足。例如,如果兩個程序具有相同的優先順序,則可能不清楚哪個程序將先來。系統設計者不應該期望知道程序的持續時間,即使該程序獲得了處理器的全部容量。因此,設計者必須確保排程正常工作,即使在最壞的情況下也是如此。
系統應該在內部或外部故障期間可靠地執行。即時系統應該知道可能發生的故障。如果程序無法從故障中恢復,系統應該進入“安全故障/正常模式”。如果系統無法滿足任務的定時約束,因此也無法滿足質量需求,它應該透過觸發錯誤來採取行動。在這種情況下,重要的是檢查是否可以刪除某些約束。內部故障可能是系統中的硬體或軟體故障。為了應對軟體故障,應設計任務以保護錯誤條件。硬體故障可能是處理器、板或鏈路故障。為了檢測故障,系統設計者應該實現看門狗系統。如果主程式忽略了定期服務看門狗,它可以觸發系統重置。
在資源和服務的上下文中,服務質量 (QoS) 一詞很重要。這意味著任務必須每時間單位獲得固定數量的“服務”。此服務包括硬體、處理時間和通訊,並且由系統以確定性方式知曉。與通用作業系統相比,硬體需要為每個程序專門分配。在將服務分配給任務時,程式設計師應該考慮“最壞情況”:如果多個任務可能需要一項服務,那麼遲早它們會同時想要它。然後順序必須最大限度地提高服務質量。
系統複雜度可以分為三個級別。第一級,C1,具有集中式硬體和集中式狀態。所有內容都是確定的,外部因素對過程沒有影響。這種系統可以由一個人設計。最簡單的機器人屬於這一類。最後一級,C3,具有分散式硬體和分散式狀態。系統在必要時會根據外部因素調整過程。這種系統需要更多(大約 100 個)設計師。RoboCup 團隊就是一個例子。C2 是一箇中間級別,它具有分散式硬體和集中式狀態,例如工業機器人。這種系統需要大約 10 個設計師。系統中互動(同步和通訊)的複雜度越高,就越難使其確定性,因為需要考慮更多方面。
- ↑ Malte Skarupke. "測量互斥鎖、自旋鎖以及 Linux 排程程式有多糟糕".