• 正文
    • 硬實時是什么?
    • Linux為什么不硬實時?
    • Linux preempt-rt如何解決這些問題?
  • 推薦器件
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

在實時操作系統(tǒng)里面隨便怎么寫代碼都能硬實時嗎?

2024/04/21
2737
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

硬實時是什么?

眾所周知,硬實時的概念,其核心并非追求速度的極致,而是確保系統(tǒng)能在預(yù)定的、可重復(fù)的時間范圍內(nèi)給予確定的響應(yīng)。這意味著,實時系統(tǒng)的正確性不僅在于計算邏輯的正確,更在于結(jié)果的產(chǎn)生時間是否符合預(yù)期。以汽車為例,當發(fā)生碰撞時,安全氣囊必須在極短的時間內(nèi)彈開,否則可能無法起到應(yīng)有的保護作用。

在評估實時操作系統(tǒng)(RTOS)的性能時,我們通常會考慮其在最惡劣情況下的延遲。比如,當對Linux進行改造,以實現(xiàn)中斷或高優(yōu)先級任務(wù)在100微秒內(nèi)的確定性延遲時,我們還需要比較其他RTOS如RT-Thread的性能。RT-Thread可能無需改造就能達到微秒級別的延遲。因此,在選擇RTOS時,我們需要根據(jù)應(yīng)用的延遲要求來權(quán)衡。對于200微秒以內(nèi)的延遲要求,改造后的Linux可能是一個合適的選擇;但對于微秒級別的要求,Linux可能就不是最佳選擇。

另外,關(guān)于RTOS和Linux在實時性方面的差異,我們需要澄清一個誤解。并非在RTOS中隨意編寫代碼就能滿足硬實時的要求,同樣,在Linux中也并非無法實現(xiàn)實時性。RTOS由于其設(shè)計特點和調(diào)度機制,通常更容易實現(xiàn)硬實時,但這并不意味著在Linux中就無法實現(xiàn)。Linux通過特定的配置和優(yōu)化,也可以提供一定程度的實時性,盡管可能無法與專門的RTOS相媲美。

因此,在選擇操作系統(tǒng)時,我們需要根據(jù)應(yīng)用的具體需求和場景來權(quán)衡。對于需要高實時性的應(yīng)用,RTOS可能是更好的選擇;而對于一些對實時性要求不那么嚴格的應(yīng)用,Linux則可能是一個更經(jīng)濟、更靈活的選擇。

Linux為什么不硬實時?


我們首先看一下,Linux為什么不能提供硬實時能力。我們認為Linux主要有如下問題(你站在硬實時的角度看它是問題,你換個角度看,它就反而是正確的地方):

1. spinlock是一個隨處可見被內(nèi)核、驅(qū)動使用的API

Linux內(nèi)核與驅(qū)動開發(fā)人員對自旋鎖(spinlock)的運用可謂是熱衷至極。每當遇到無需睡眠且時間較短的臨界區(qū)保護場景時,他們幾乎都會優(yōu)先考慮使用自旋鎖。可以說,如果不了解自旋鎖,那么即便在內(nèi)核與驅(qū)動開發(fā)領(lǐng)域有所建樹,也稱不上是真正的英雄。

自旋鎖的魅力在于其高效性。當兩個或多個執(zhí)行單元(如線程、中斷等)競相獲取同一鎖時,自旋鎖允許失敗的執(zhí)行單元不是立即進行上下文切換,而是原地自旋等待。這種機制避免了因上下文切換而帶來的額外開銷,特別是在鎖持有時間較短的情況下,自旋等待的代價往往低于上下文切換的代價。

然而,自旋鎖也并非完美無缺。它有一個顯著的副作用,即當某個執(zhí)行單元持有鎖時,會禁止該CPU核上的搶占調(diào)度。這意味著即使存在更高優(yōu)先級的任務(wù)等待執(zhí)行,也必須等待當前持有鎖的任務(wù)釋放鎖后才能獲得執(zhí)行機會。

在Linux內(nèi)核中,自旋鎖的實現(xiàn)主要側(cè)重于核間自旋。當多個核上的執(zhí)行單元嘗試獲取同一鎖時,它們會在各自的核上進行自旋等待。而在核內(nèi),則是通過禁止搶占來實現(xiàn)臨界區(qū)的保護,確保在持有鎖期間不會有其他任務(wù)打斷當前任務(wù)的執(zhí)行。

綜上所述,自旋鎖在Linux內(nèi)核與驅(qū)動開發(fā)中扮演著重要角色,其高效性使得它在特定場景下成為首選的同步機制。然而,我們也需要認識到它帶來的副作用,并在使用時權(quán)衡其優(yōu)缺點。

假設(shè)T1、T2、T3和T4這四個任務(wù)都在同一個CPU核上運行。當T1成功獲取到一個自旋鎖(spinlock)時,該CPU核上的搶占調(diào)度機制就會被臨時禁用。這樣做的目的是為了保護臨界區(qū)內(nèi)的代碼和數(shù)據(jù),避免在T1執(zhí)行關(guān)鍵任務(wù)時被其他任務(wù)打斷。

然而,如果在T1持有自旋鎖的過程中,T2作為一個高優(yōu)先級的實時任務(wù)被喚醒并準備執(zhí)行,由于搶占調(diào)度被禁止,T2無法立即打斷T1的執(zhí)行。即使T2的優(yōu)先級高于T1,它也必須耐心地等待T1釋放自旋鎖。

這里的問題在于,我們無法精確預(yù)知T1將會持有自旋鎖多久。這完全取決于T1在臨界區(qū)內(nèi)執(zhí)行的具體任務(wù)(即“做xxxx”)的復(fù)雜性和耗時情況。由于這種不確定性,T2需要等待的具體時間也變得不可預(yù)測。這種不確定性對于實時任務(wù)來說是非常不利的,因為它破壞了實時系統(tǒng)所追求的決定性時延。

決定性時延是指在實時系統(tǒng)中,任務(wù)能夠在預(yù)定的、可預(yù)測的時間范圍內(nèi)完成。然而,由于T1持有自旋鎖的時間不可知,T2的執(zhí)行被延遲了多久也變得未知,這就破壞了實時系統(tǒng)的決定性時延特性。

因此,在使用自旋鎖時,需要仔細考慮其對實時任務(wù)調(diào)度和時延的影響。在實時性要求非常高的系統(tǒng)中,可能需要考慮其他同步機制或調(diào)度策略,以確保實時任務(wù)能夠得到及時的響應(yīng)和執(zhí)行。

2. Linux的中斷執(zhí)行時間可能過長且不可嵌套

眾所周知,早期的Linux版本有個標記叫IRQF_DISABLED,標記本中斷在執(zhí)行的時候,其他所有中斷都被禁止進入;而后Linux內(nèi)核實際去掉了這個申請flags,其實就是都是IRQF_DISABLED了,總體可認為Linux內(nèi)核不支持中斷的嵌套。

int request_irq(unsigned int irq, irq_handler_t handler,

unsigned long irqflags, const char *devname, void *dev_id);

中斷在執(zhí)行的時候,所有的中斷都進不來,這個設(shè)計本身簡化了內(nèi)核,但是對于硬實時的打擊是致命的,前面的中斷不執(zhí)行完成,優(yōu)先級再高的中斷也得給我等著。

比如中斷1在執(zhí)行的過程中,來了中斷2,而中斷2對應(yīng)的事情是必須要決定性時延的,由于IRQ1的中斷服務(wù)程序也是碼農(nóng)寫的,我們無法確定這個中斷服務(wù)程序要執(zhí)行多久。這顯然讓高優(yōu)先級中斷2的進入延遲不再具備可預(yù)期性。

3. 軟中斷(softirq)是一個比進程上下文優(yōu)先級更高的上下文

我們設(shè)想一個場景,哪怕Linux解決了問題2,就是Linux的中斷變地可嵌套,高優(yōu)先級的中斷可以打斷低優(yōu)先級的中斷,并且高優(yōu)先級的中斷2喚醒了一個用戶寫的實時線程。

IRQ2喚醒了實時任務(wù)T1,但是T1必須等待IRQ1喚起的軟中斷(也包括使用軟中斷上下文的tasklet等)被執(zhí)行完,T1才能投入執(zhí)行。IRQ1喚起的softirq的代碼是碼農(nóng)寫的,這個碼農(nóng)寫多久,鬼都不知道,這顯然破壞了實時任務(wù)T1得以調(diào)度執(zhí)行的確定性時延。

4. 內(nèi)核里面會屏蔽中斷的API如local_irq_disable、spin_lock_irqsave等

前文已經(jīng)多次指出,在驅(qū)動程序中調(diào)用local_irq_disable()函數(shù)往往被視為一個潛在的問題或者說是bug。原因在于這個函數(shù)會禁用本地CPU的中斷,但它并不能解決其他CPU核上運行的線程或中斷服務(wù)程序與當前核上線程之間的競態(tài)條件。盡管在只有一個CPU核的系統(tǒng)中調(diào)用此API通常是安全的,但我們在編寫Linux內(nèi)核代碼時,應(yīng)當始終假設(shè)我們是在多核環(huán)境下工作,這是Linux內(nèi)核編程跨平臺的基本常識。

大部分有經(jīng)驗的開發(fā)者都明白,在編寫驅(qū)動程序時應(yīng)當避免使用local_irq_disable()這樣的API。然而,spin_lock_irqsave()這樣的API在內(nèi)核編程中卻非常常見。它通常用于一個特定的場景,即當中斷服務(wù)程序與線程之間存在潛在的競態(tài)條件時。作為內(nèi)核程序員,我相信你已經(jīng)非常熟悉這樣的經(jīng)典用法了,這已經(jīng)成為了內(nèi)核編程中的常規(guī)操作,體現(xiàn)出了內(nèi)核編程的嚴謹性和技巧性。

它把T1、T2、T3、T4、IRQ1、IRQ2這6者之間的競爭消滅于無形。T1如果持有了spin_lock_irqsave,本核上的T2、IRQ1顯然進不來,CPU1上面的T3、T4、IRQ2想訪問T1訪問的臨界資源必須spin。IRQ1如果持有了spin_lock, CPU1上面的T3、T4、IRQ2想訪問IRQ1訪問的臨界資源必須spin。

那么,問題又來了,spin_lock_irqsave既屏蔽了搶占,又屏蔽了中斷,這會導(dǎo)致中斷和實時任務(wù)的確定性時延造成不可預(yù)期的破壞。因為spin_lock_irqsave和spin_lock_irqrestore是碼農(nóng)寫的,鬼都不知道它要多久。

當然,歷史上,粗獷的大內(nèi)核鎖(Big Kernel Lock,BKL)也是一個問題。由于晶晶姑娘不喜歡內(nèi)核粗獷的一面,BKL在如今的內(nèi)核里面已經(jīng)煙消云散。

在Linux的世界里,這些鎖當然都沒有一個鎖牛逼,就是RCU,尤其是面對這個世界符合阿姆達爾定律(Amdahl's law)定律的情況下,我們既要保證臨界資源訪問的被保護,又要盡一切可能地讓多個線程同時狂奔。關(guān)于RCU的細節(jié),謝神醫(yī)已經(jīng)有多篇文章論述。

Linux的世界大概是這樣的:中斷、軟中斷、線程(包括ksoftirqd線程)。我們都清楚地知道,軟中斷大量陷入的情況下,內(nèi)核會將后續(xù)的軟中斷投入ksoftirqd內(nèi)核線程執(zhí)行,所以軟中斷還有一個可能的執(zhí)行時機是在內(nèi)核線程里面。

5. Linux用戶空間內(nèi)存的lazy分配機制與交換swap

對于喜歡在RTOS寫程序的童鞋來說,Linux的世界一時半會難以理解,但是對于寫Linux的童鞋來說,絕大多數(shù)的RTOS簡直就是在裸奔。

我們都知道,在Linux里面,用戶空間的內(nèi)存都執(zhí)行l(wèi)azy的分配機制。比如你malloc一個內(nèi)存

char *p = malloc(1024*1024);

這個時候Linux忽悠你說拿到了內(nèi)存并且p獲得了地址,但是實際的拿到卻是在你寫的時候,以page fault缺頁中斷的形式獲得的。比如你寫p[0]=1就拿到了第一頁,你寫p[4096]就拿到了第2頁。這個lazy的分配機制,也同樣適用于棧、代碼段等。

你是一個實時的線程,你被喚醒得以執(zhí)行,你執(zhí)行的時候,發(fā)現(xiàn)你訪問的臨時變量還沒有獲得內(nèi)存,你的代碼段可能還特馬在硬盤里,請問你實時個什么鬼?你執(zhí)行到函數(shù)b的時候,去訪問d[1000],結(jié)果發(fā)現(xiàn)這個棧的這頁內(nèi)存還要通過page fault來通過內(nèi)核buddy去申請,你的確定性延遲還如何滿足?

main()

{

a();

}

a()

{

b();

}

b()

{

int d[1024];

d[1000]=100;

c();

}

當然,已經(jīng)進入內(nèi)存的東西,也由于內(nèi)核的swap機制,會與磁盤進行交換。

絕大多數(shù)的RTOS都沒有這個“問題”,這也恰恰是他們不夠“牛逼”的地方。對于手機、電腦這種富應(yīng)用的系統(tǒng)而言,你不能用資源已經(jīng)被確定性分配的思維模式來思考。

Linux preempt-rt如何解決這些問題?

前段時間,這篇文章刷屏了:《Linux實時補丁即將合并進Linux 5.3》?,許多童鞋都說活久見,實際是活久了也特么沒見到。我進內(nèi)核搜索,發(fā)現(xiàn)沒有一個體系架構(gòu)到目前真地使能了支持。

到今天為止,ARCH_SUPPORTS_RT誰他么都不是真:

barry@barryUbuntu:~/develop/linux$ git grep ARCH_SUPPORTS_RT

arch/Kconfig:config ARCH_SUPPORTS_RT

kernel/Kconfig.preempt: depends on EXPERT && ARCH_SUPPORTS_RT

所以,你要真地在mainline見到PREEMPT_RT開花結(jié)果,還必須活地更久一點。

當你提到preempt-rt補丁時,強調(diào)了Linux的特性和它在實時性方面的考量,這是非常準確的。Linux作為一個功能豐富的操作系統(tǒng),其設(shè)計初衷是支持多樣化的應(yīng)用和場景,包括用戶空間的各種進程和線程。

preempt-rt補丁是Linux內(nèi)核的一個實時性增強補丁,它旨在提升Linux在實時任務(wù)調(diào)度方面的性能。通過改進內(nèi)核的調(diào)度策略和中斷處理機制,preempt-rt使得Linux能夠更好地滿足實時應(yīng)用的需求。

相對于其他RTOS,Linux在處理實時任務(wù)時確實有其獨特之處。RTOS通常更強調(diào)高優(yōu)先級中斷的確定性時延,因為它們通常將整個系統(tǒng)編譯在一起,可以在中斷處理程序中直接嵌入策略。然而,Linux作為一個通用的操作系統(tǒng),其內(nèi)核與用戶空間之間有著明確的分離。用戶空間的應(yīng)用無法直接訪問或修改內(nèi)核代碼,只能通過系統(tǒng)調(diào)用等接口與內(nèi)核進行交互。

因此,在Linux中,實現(xiàn)實時任務(wù)的確定性調(diào)度時延就顯得尤為重要。通過preempt-rt補丁,Linux內(nèi)核提供了更好的實時調(diào)度能力,使得高優(yōu)先級的RT線程能夠得到及時的處理和調(diào)度。同時,由于Linux內(nèi)核提供了豐富的操作接口,開發(fā)者可以在用戶空間編寫應(yīng)用,通過調(diào)用這些接口來利用內(nèi)核提供的實時功能。

總的來說,Linux不是一個簡單的裸機操作系統(tǒng),它有著復(fù)雜的內(nèi)核架構(gòu)和用戶空間應(yīng)用。在實現(xiàn)實時性時,需要充分考慮到這種架構(gòu)的特點,并通過適當?shù)难a丁和配置來優(yōu)化實時性能。而preempt-rt補丁正是為了提升Linux在實時任務(wù)調(diào)度方面的能力而設(shè)計的。

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風(fēng)險等級 參考價格 更多信息
SFH203FA 1 OSRAM GmbH FIBER OPTIC PHOTODIODE DETECTOR, PLASTIC PACKAGE-2

ECAD模型

下載ECAD模型
$0.64 查看
CB3LV-3I-50M0000 1 CTS Corporation HCMOS/TTL Output Clock Oscillator, 1.5MHz Min, 107MHz Max, 50MHz Nom, GREEN, CERAMIC PACKAGE-4

ECAD模型

下載ECAD模型
$4.13 查看
AB-557-03-HCHC-F-L-C-T3 1 Abracon Corporation PLL Based Clock Driver, 557 Series, 2 True Output(s), 2 Inverted Output(s), QFN-14
暫無數(shù)據(jù) 查看

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

針對嵌入式人工智能,物聯(lián)網(wǎng)等專業(yè)技術(shù)分享和交流平臺,內(nèi)容涉及arm,linux,android等各方面。