- 前言
- POSIX簡介
- RTOS對POSIX的實現情況
- Zephyr
- FreeRTOS
- RTOS提供的POSIX介面實時嗎?
- nanosleep
- Timer-不同linux版本和xenomai的實現差異
- PREEMPT-RT Timer實現原理
- Xenomai Timer實現原理
- 總結
- 參考
前言
在開發實時應用時,我們希望軟體具備良好的跨平臺和可移植,既能在實時linux也能在RTOS上工作,為實現這個目的,我們會選擇使用POSIX API來設計實時應用,實現在不同的作業系統和平臺上的可移植性。這樣只要有POSIX支援,應用程式從一個作業系統移植到另一個作業系統,無需對原始碼進行較大的修改,可以減少很多重複工作(想象很美好)。
但是,實時應用除關注介面基本功能一致外,還關注介面的實時性(執行時間確定性),那不同的實時作業系統的POSIX底層實現/行為實不實時呢?
以精確定時為例,精確定時是我們常用的作業系統服務之一,比如工業乙太網(ECAT、PN...)中的通訊週期,通訊週期的準確控制離不開精確定時;在無線基站和終端的slot排程中,精確定時確保了資料傳輸的有序與高效;在實時模擬中,模擬步長的精確控制更是模擬真實場景的關鍵等等,這些都離不開POSIX定時介面。
本文首先簡要概述POSIX標準,隨後深入剖析POSIX定時介面在常用開放核心原始碼的RTOS(實時作業系統)上的上的實現原理做簡單介紹。透過本文的介紹,希望能為讀者在實際應用中提供有價值的參考,避免在RTOS中使用POSIX遇到不必要的困擾。
POSIX簡介
POSIX是 IEEE(Institue of Electrical and Electronics Engineers,電氣和電子工程師學會)為了規範各種 UNIX 作業系統提供的 API 介面而定義的一系列互相關聯標準的總稱,其正式稱呼為 IEEE1003,國際標準名稱為 ISO / IEC9945。此標準源於一個大約開始於 1985 年的專案。
POSIX 這個名稱是由理查德 · 斯托曼(Richard Stallman)應 IEEE 的要求而提議的一個易於記憶的名稱。它是 Portable Operating System Interface(可移植作業系統介面)的縮寫,而 X 則表明其對 Unix API 的傳承。POSIX更能正確表示這一系列相關標準,所以術語POSIX最初被用作IEEE Std 1003.1-1988的同義詞,即POSIX.1(n=1)。這保持了符號“POSIX”可讀性的優點,而不會與POSIX系列標準產生歧義。
關於有關POSIX. 1的背景、受眾和目的的更多資訊,參看該連結https://www.opengroup.org/austin/papers/backgrounder.html
https://www.opengroup.org/austin/papers/posix_faq.html
POSIX標準涉及作業系統方方面面,我們主要關注以下兩個標準:
- IEEE Std 1003.1 "行動式作業系統介面 (POSIX™)"--最初主要針對 UNIX 系統,1988 年第一版,2018 年最新版
- IEEE Std 1003.13 "標準化應用環境配置檔案(AEP)--POSIX™ 實時和嵌入式應用支援"
IEEE Std 1003.1標準,最初只用於UNIX系統。POSIX定義了一系列應用程式程式設計介面(API),用於在原始碼級別實現可移植性。POSIX最早釋出於1988年,最新版本於2018年釋出。它提供了一種標準化的應用程式環境配置檔案(AEP),用於實現實時和嵌入式應用程式的支援。透過遵循POSIX標準,開發人員可以編寫與POSIX相容的原始碼,從而實現在不同的作業系統和平臺上的可移植性。這使得開發人員能夠更輕鬆地將應用程式從一個作業系統移植到另一個作業系統,而無需對原始碼進行較大的修改。
POSIX.13(IEEE Std 1003.13)對實時和嵌入式應用程式的支援:IEEE Std 1003.13-2003是一個針對實時系統的標準,提供了對嵌入式應用程式的支援,實時配置檔案可總結如下。該標準定義了幾個實時系統Profiles,所有Profiles都包括部分或全部POSIX.1、.1b和.1c,以及開發平臺的POSIX.2和.2a的部分內容,這些Profiles分別為PSE51、PSE52、PSE53和PSE54。
- PSE51:最小實時配置檔案(Minimal real-time)適用於資源受限的嵌入式系統。它提供了最基本的實時功能,包括任務排程、中斷處理和簡單的同步機制,這個子集不需要多程序(執行緒)和檔案系統。
- PSE52:實時控制器配置檔案(Realtime controller)適用於需要嚴格的實時控制和排程的系統。它在PSE51的基礎上增加了簡單檔案系統和訊息佇列等功能,提供了精確的任務排程、硬實時響應和實時資料處理能力。
- PSE53:專用實時配置檔案(Dedicated real-time)適用於需要高度可預測性和可靠性的實時系統。它提供了多程序、網路支援,更強大的實時功能,包括任務優先順序管理、嚴格的時間限制和實時資源管理。
- PSE54:多用途實時配置檔案(Multi-purpose real-time)適用於需要同時支援多種實時應用的系統。它提供了完整的檔案系統、shell,靈活的任務排程、多工併發和多種實時通訊機制。
綜上,PSOXI標準不僅包含應用開發時常說的POSIX API,還包括檔案系統、網路、shell等整個應用程式的執行環境。
目前符合 POSIX 標準協議的作業系統有:UNIX、BSD、Linux、iOS、Android、SylixOS、VxWorks、RTEMS、LynxOS 等,由於這些OS對 POSIX 的支援,其他相容 POSIX 系統上的應用程式可以非常方便地移植到這些系統上。
RTOS對POSIX的實現情況
這裡簡單介紹幾個常用RTOS的POSIX支援情況:
RTOS | POSIX支援情況 |
---|---|
PREEMPT-RT | 完全相容 |
Xenomai | 完全相容,實時部分xenomai核心和libcobalt提供,非實時部分linux核心和glibc提供 |
VxWorks | User space(RTP):POSIX.13 PSE52 (少數介面存在限制)Kernel space:POSIX.1部分介面和POSIX.1可選功能中的一些實時介面 |
RTEMS | POSIX 1003.1b-1993. POSIX 1003.1b-1993。POSIX 1003.1h/D3.Open Group Single UNIX Specification.單程序; |
SylixOS | 相容IEEE 1003(ISO/IEC 9945)作業系統介面規範相容POSIX 1003.1b(ISO/IEC 9945-1)實時程式設計的標準 |
Zephry | PSE54 |
EcOS 3.0 | ISO/IEC 9945—1(少部分省略) |
FreeRTOS | 實驗性,僅實現了約 20% 的 POSIX API |
RT-Thread | 未找到明確說明,從原始碼上看相容大部分POSIX |
Zephyr
詳見Zephyr開發文件https://docs.zephyrproject.org/latest/samples/posix/philosophers/README.html
Zephyr支援路線圖 https://static.sched.com/hosted_files/eoss24/40/ZDS-2024-POSIX-Roadmap-for-Zephyr-LTSv3.pdf
FreeRTOS
FreeRTOS-Plus-POSIX 可實現 POSIX 執行緒 API 的小子集。藉助此子集,熟悉 POSIX API 的應用程式開發者可使用類似執行緒原語的 POSIX 開發 FreeRTOS 應用程式。FreeRTOS-Plus-POSIX 僅實現了約 20% 的 POSIX API。因此,無法僅使用此包裝器將現有的 POSIX 相容應用程式或 POSIX 相容庫移植到 FreeRTOS 核心上執行。
FreeRTOS-Plus-POSIX 實現了部分 IEEE Std 1003.1-2017 版《開放組技術標準基礎規範》,第 7 期。FreeRTOS-Plus-POSIX 包括以下 POSIX 執行緒標標頭檔案的實現,詳細資訊請參閱 FreeRTOS-Plus-POSIX API 文件瞭解。
需要注意的是:FreeRTOS posix介面不支援動態建立任務,即開始排程後不能再建立新的任務。
RTOS提供的POSIX介面實時嗎?
在實時應用場景中,精確定時是我們常用的介面,比如工業乙太網ECAT、PN中的通訊週期,無線基站/終端中的slot排程,實時模擬中的模擬步長等等。
POSIX中常用的定時介面有nanosleep()、clock_nanosleep()、timer_create()/timer_settime()等,但這些RTOS實現的POSIX實時嗎?或者說定時精度如何?
nanosleep
在POSIX標準中,對nanosleep()
睡眠時間解析度的定義為1/HZ,即作業系統的Tick週期時間,意味著實際睡眠解析度與系統時鐘滴答週期近似,下表總結了常見RTOS的nanosleep()
底層實現及精度,從標準上看,這些RTOS都符合POSIX標準,但HZ不足以支撐很多us級定時應用場景。
RTOS | 實現原理 | 精度 |
---|---|---|
PREEMPT-RT | 高精度hrtimer,每次定時最終由硬體timer中斷handler中喚醒 | ns |
Xenomai | 高精度hrtimer,每次定時最終由硬體timer中斷handler中喚醒 | ns |
VxWorks | 睡眠時間轉換對齊到Tick | Tick |
RTEMS | ||
SylixOS | 未啟用LW_CFG_TIME_HIGH_RESOLUTION_EN時,睡眠時間轉換為Tick,每個Tick中統一處理;啟用LW_CFG_TIME_HIGH_RESOLUTION_EN後,CPU不足一個Tcik的nanosleep轉換為while(1)死等時間到達。 | Tick或ns |
Zephry | 睡眠時間轉換為Tick | Tick |
EcOS 3.0 | 睡眠時間轉換為Tick | Tick |
FreeRTOS | 睡眠時間轉換為Tick | Tick |
RT-Thread | 5.x版本雖然實現了hrtimer邏輯,但是你用時會發現是個半成品,可以選擇一個硬體Timer來實現hrtimer底層定時,但是這樣每次只能有一個任務處於定時狀態(用全域性變數來維護timer的引數,且ctime層 hrtime、cputime介面職責隔離不清晰,無可擴充套件性,可以看出"能跑就行",質量堪憂);預設情況下還是基於Tick。 | Tick或ns |
這裡不得不提一下,一些作業系統所宣傳的ns級延時,基本指的是邏輯如下,對於用慣了xenomai/Peempt-RT的hrtimer來說,不明白CPU死等的方式有什麼值得宣傳的(這讓我想起了郭天祥老師...)。
udeley(){
do {
從硬體讀取當前時間;
} while (當前時間未到達指定時間);
}
Timer-不同linux版本和xenomai的實現差異
計時器管理器提供的服務包括:
- timer_create - 建立每程序定時器
- timer_delete - 刪除每程序定時器
- timer_settime - 設定下一個定時器的到期時間
- timer_gettime - 獲取定時器的剩餘時間
- timer_getoverrun - 獲取定時器超時計數
在xenomai中,我們通常會透過如下方式來定時觸發週期任務:
sev.sigev_notify = SIGEV_THREAD_ID | SIGEV_SIGNAL;
sev.sigev_signo = SIGUSR1;
sev.sigev_value.sival_ptr = &timerid;
sev.sigev_notify_thread_id = get_thread_pid();
/*timer create*/
timer_create(CLOCK_MONOTONIC, &sev, &timerid);
/* Start the timer */
timer_overrun = timer_getoverrun(timerid);
/*get current time*/
err = clock_gettime(CLOCK_MONOTONIC, &now);
tspec.it_value = now;
tspec.it_value.tv_sec += 0;
tspec.it_value.tv_nsec += 2000000;
tsnorm(&tspec.it_value);
tspec.it_interval.tv_sec = 0;
tspec.it_interval.tv_nsec = 1000000; /**/
timer_settime(timerid, TIMER_ABSTIME, &tspec, NULL);
while (1)
s = sigwait(&GlobalSignalMaskSet, &sig);
/*.............*/
}
但是,PREEMPT-RT和xenomai中關於timer的介面底層實現有區別:
PREEMPT-RT Timer實現原理
大家都知道,linux中斷分為上半部和下半部,未啟用PREEMPT-RT時,上半部表示硬體中斷上下文,即響應中斷就直接執行中斷上半部;
啟用PREEMPT-RT後,有所不同,通常上半部由中斷執行緒來處理,即中斷產生後,喚醒中斷執行緒來處理中斷上半部,此時中斷上半部線上程上下文執行。並不是所有中斷都是中斷執行緒來執行,比如系統Timer中斷就是不能中斷執行緒化的,還是在硬體中斷上下執行。對於中斷下半部,基本沒有變化,還是由softirq、workqueue執行緒等執行。
linux核心中,程序建立的每個timer都會對應核心中高精度timer的一個物件,這些hrtimer用紅黑樹組織,所有timer最後由硬體timer中斷驅動執行,執行原理如下:
- 未啟用PREEMPT-RT時,hrtimer由Hrtimer softirq驅動,即硬體定時超時,喚醒軟中斷處理,軟中斷執行緒檢查hrtimer超時情況,並呼叫對應的超時處理函式,超時處理函式中會喚醒對應的執行緒;
- 啟用PREEMPT-RT後,情況有所有不同,在PREEMPT-RT中,hrtimer分為兩類,一類是在硬中斷上下執行超時回撥(HRTIMER_MODE_HARD),一類在軟中斷上下文執行超時回撥(HRTIMER_MODE_SOFT);
- 對於HRTIMER_MODE_HARD因為在中斷上下文執行的超時回撥,時延低,任務定時精度高;
- 對於HRTIMER_MODE_SOFT timer,軟中斷到期模式的高精度定時器,到期的時候在型別為HRTIMER_SOFTIRQ的軟中斷裡面執行定時器回撥函式。在實時核心中,軟中斷由軟中斷執行緒執行,或者在程序開啟軟中斷的時候執行,任務時延高定時不精確。
- 如果沒有指定到期模式,那麼在實時核心中預設使用軟中斷到期模式。
那PREEMPT-RT中,POSIX API底層用的是HRTIMER_MODE_HARD還是HRTIMER_MODE_SOFT?
API | timer型別 |
---|---|
nanosleep()clock_nanosleep() | HRTIMER_MODE_HARD |
timer_create()+timer_settime() | 4.16版本以前高精度定時器總是在硬中斷裡面執行定時器回撥函式,所以timer相關介面定時精確,詳見https://www.spinics.net/lists/kernel/msg3208465.html4.16版本及以後版本增加HRTIMER_MODE_SOFT,timer系列介面定時不再精確(不實時,抖動大)。 |
Xenomai Timer實現原理
原理,詳見本部落格博文xenomai時間子系統。
xenomai核心提供的所有POSIX介面都是實時的。
總結
我們僅比較了兩個實時應用常見API在不同RTOS中的實現,應該明白,POSIX只是一個API標準,不同的系統底層實現不同,我們在將我們的實時任務移植適配到不同的RTOS的時候,需要事先評估用到的POSIX介面在這些RTOS上的實時行為與我們的期待是否相符。
參考
https://www.baeldung.com/linux/posix
https://unix.stackexchange.com/questions/11983/what-exactly-is-posix
https://static.sched.com/hosted_files/eoss2023/2e/Zephyr RTOS - Posix.pdf
VxWorks_7_programmers_guide.pdf
https://docs.rtems.org/branches/master/posix-users/index.html
https://ecos.sourceware.org/docs-latest/ref/posix-standard-support.html