RTOS(Real-time Operating System,實時作業系統)是以任務運行時間管理作為核心框架的作業系統。
RTOS常常以嵌入到微機(微控制器/微處理器/其他微機晶片)的形式存在,用於幫助微機完成實時性要求嚴格的工作環境。
我們常用的電腦、手機所使用的作業系統均為「分時作業系統」,優先保證每個應用程式的功能可被完整實現,進而保證程序的完整性、數據的準確性以及用戶交互的實時性。分時作業系統允許用戶在硬體性能範圍內,實現高負荷軟體順利運行(比如玩遊戲、渲染視頻或設計)以及海量程序或服務的並發運行(比如網頁瀏覽、辦公軟體和社交軟體一起運行)。
實時作業系統更多用於對程序響應週期提出嚴格要求的運行環境,比如微機(常見為MCU微控制器)、人身安全保護處理器(自動生產線上的急停按鈕或者門鎖傳感器)、重要功能模塊(比如汽車的制動監測系統)等。為了儘可能削減多餘幹擾因素,在一個大型系統中,實時作業系統一般都以嵌入到多枚微機晶片的形式出現。
分時作業系統以及實時系統,都以處理器高速計算的形式達成對人機界面的構建、程序的運行以及功能的實現;不論是哪種作業系統,每個程序在單位運行時間都存在著允許活動的時間段(亦稱「時間片」),這些活動時間經過作業系統以及用戶的聯合控制,保證每個程序都能高效執行,呈現出計算機以及微機能處理多個程序以及功能的效果。
由用戶編寫並且要求處理器如實進行的步驟序列就被稱為「程序」。
為區分用戶程序與RTOS系統專用程序,我們常說的「用戶程序」一般都被稱為「任務」。
不同類型的任務具有不同的編程形式,並且支持用戶程序管理工作模式和優先級,有時候程式設計師還需要根據任務狀況分配內存,防止其它程序運行效率遭到降低。
在裸機環境下的子函數不需要添加跳轉指令,因為處理器常常會在調用子函數之後,在編譯後的「裸機程序」自動回到調用前的父函數。
編譯器會在每個函數的末尾段,自動填充程序跳轉指令到最終的機器碼文件(待燒錄程序)中,因為每個函數運行結束之前的判斷策略以及結束之後的動作都是固定不變的,
正所謂「善戰者無赫赫之功」,這種「傻瓜式」函數切換策略容易讓RTOS新手忘卻了編譯器自動添加的必要步驟。
然而在RTOS中,儘管RTOS自動保存了函數的起始位置,但任務/函數的切換順序是隨時可變的,因此不能依賴編譯器自動給定的程序跳轉指令去跳轉其他任務,需要用戶在任務中提供「任務的終點」讓RTOS確認任務切換時機。
不能確認任務切換時機,對於RTOS而言是最為致命的,因為RTOS不能獲取編譯器給定「任務跳轉」的位置。如果一個任務不給RTOS設立一個任務結束的跳轉位置,就容易因為跳轉到編譯器自動添加的指令,跑飛到其它程序。
如果存在需要動態內存分配的任務、燒錄前加密、指定絕對內存位置等多次更改函數位置的步驟,可能會陷入比「跑飛」後果還嚴重的「堆棧溢出」,輕則導致處理器停機,重則導致系統結構解體。
RTOS通常會為用戶留下用於任務管理的API函數,常用的任務管理功能如下:
不同的RTOS會有不同的優先級仲裁算法,有些RTOS的優先級數字越大、等級越低,有些則截然相反。
在同屬「就緒」狀態的任務隊列中,優先級高的任務會被搶先運行,運行順序從優先級高的任務到優先級低的任務。
高優先級任務執行完成或超時切換後,將從任務列表中尋找次高優先級任務,直到最低優先級任務執行完畢或之前執行過的高優先級任務解除阻塞。
系統空閒進程固定為最低優先級;系統任務管理器、任務切換服務以及系統心跳中斷服務固定為RTOS的最高優先級。
不同的RTOS會區分不同的任務狀態,常見的任務狀態如下:
熟悉Arduino編程環境的程式設計師容易把用戶程序分為兩種,一種是「單步程序」,一種是「循環程序」。在使用RTOS時,程式設計師可以劃分不定式的任務形式,而常用的任務形式有以下四種:「有限次數任務」、「無限循環任務」、「先處理協作任務」和「後處理協作任務」。
編寫有限次數任務時,需要確定任務循環次數(一般為1次),抵達任務循環次數之後,為了避免RTOS進入任務切換時機後,再次執行跑飛到其它程序,需要在任務切換指令之前,將該任務註銷(刪除)。
一般情況下,循環程序按照單獨執行的形式以一個大的無限循環語句包裹起來,不過要注意的是,還需要在循環語句的最後一句中添加「任務跳轉位置」或者「阻塞條件」,以此趕在RTOS超時檢測前完成任務切換,避免對RTOS的實時性造成破壞。
循環程序的一種特殊情形,通過協調信號與動作之間的先後順序,以及需要聯動協作的任務以完成同步處理。
RTOS會為用戶提供專用的內存工具,這些內存工具由RTOS自動管理,同時會綁定等候狀態變化的「阻塞中」任務列表。
用戶既可以通過API添加內存工具,也可以在任務中添加面向指定內存工具的「阻塞條件」,由RTOS在運行其他任務的過程中,代為等候內存工具的變化。
內存工具狀態發生改變時,RTOS會根據任務阻塞條件,將符合「阻塞條件」的任務升級為「準備就緒」狀態。
一般情況下,這些內存工具用於任務之間的相互協調,因為狀態變化往往伴隨著一些任務需要搶先執行,或者阻止某些高優先級任務突然運行,用於實現「線程安全」,避免任務發生共用資源的衝突。
常見的內存工具有「信號量」、「事件標誌組」、「數據隊列」和「即時信箱」。
信號量(Semaphore)是實現基本任務協調的內存工具。相比於裸機系統,任務對信號量的讀寫嚴格按照RTOS給定的任務優先級順序,並且允許任務等候對應信號量抵達「已用狀態」。
適用場合:軟體狀態開關(二值信號量)、硬體使用狀況指示器(互斥信號量)、緩存用量指示器(計數器信號量)、公共數據緩存空間(互斥計數信號量)
FreeRTOS動畫演示信號量操作過程 |
---|
|
任務可以對信號量進行以下操作:
🛠️創建 🗑️刪除 ➕「給出」(計數遞增) ➖「取走」(計數遞減)
⏳等信號量被給出後,解除阻塞並取走信號量
🔍查詢信號量數據 🔍👤查詢佔有者(僅限「互斥」) 🔒鎖定/解鎖信號量(禁止「互斥」)
信號量在RTOS中會出現四種模式,分別是「二進制信號量」、「計數式信號量」、「互斥二值信號量」、「互斥遞歸信號量」。
事件標誌組 是「二值信號量」的進階版本。RTOS既能保證各任務對事件標誌的讀寫操作有序可控,又能通過事件標誌實現各種條件下的自動處理(自動化)。
適用場合:狀態機、並發事件呼叫器
任務可以對事件標誌組執行以下操作:
🛠️創建一組事件標誌 🗑️刪除一組事件標誌 ➕置位1個事件標誌 ➖復位1個事件標誌 🔍⊙ 檢查事件標誌/標誌組狀態
⏳等分組中的一個/多個事件標誌被置位(可選與/或邏輯)
⏏️等事件標誌後自動清除標誌位
✏️⏩批處理事件標誌組 🔐管理事件標誌組可用標誌位長度
數據隊列(Queue)是用於解決數據緩衝的內存工具。相比於裸機系統自行編寫的「FIFO」隊列,RTOS也會嚴格調整任務的讀寫順序,避免數據操作順序不可控的問題。
適用場合:軟體通信/函數傳遞緩衝區(順序隊列)、操作歷史記錄器(逆序隊列)
FreeRTOS動畫演示RTOS隊列操作過程 |
---|
|
任務可以對數據隊列執行以下操作:
🛠️創建 🗑️刪除 ✏️▶️排隊寫入數據(FIFO) ▶️📬讀出數據 ✏️⏩插隊寫入數據
⏳等隊列有數據被寫入
🔍% 查詢隊列已用量/可用量 🔍? 查詢隊列是否為空/為滿 ✏️◀️排隊寫入數據(LIFO) 🗑️🗞️清空/重置隊列
✏️🔄️覆寫即將讀出的數據 📬👁️🗨️偷瞄數據(Peek,讀出但保留數據)
即時信箱(Mailbox)亦稱「通知」、「簡訊」,是直接向任務發送一行數據的內存工具。RTOS會把任務請求的數據發送在目標任務的任務管理塊,不僅大幅減少操作步驟,而且不需要用戶額外創建內存工具,因為即時信箱是跟隨任務創建而共生的。
適用場合:代替傳入函數將數據傳到任務中、嚴格控制內存大小的應用場合、上述內存工具替代品
任務可以進行與即時信箱相關的以下操作:
📤向任務的信箱發送/替換通知 📥接收來自信箱的通知
📥⏳等候信箱郵件並進入阻塞狀態 📤🔔發送郵件並解除其他任務的阻塞狀態
🪄對任務信箱進行類信號量管理 🗑️清除任務信箱 📤📚發送通知並構建通知歷史 🔍 讀取其他任務的信箱內容
不使用上述工具所創建的內存均屬於「非RTOS管轄內存」。