大家好,我是良許。
為什么Linux不是實(shí)時(shí)操作系統(tǒng)?

這個(gè)問(wèn)題,我在做嵌入式的時(shí)候被問(wèn)過(guò)很多次。
有人說(shuō):"Linux不是有實(shí)時(shí)調(diào)度策略嗎?SCHED_FIFO、SCHED_RR這些,聽起來(lái)很實(shí)時(shí)啊。"
還有人問(wèn):"如果把所有進(jìn)程都設(shè)成實(shí)時(shí)調(diào)度,Linux能不能當(dāng)實(shí)時(shí)系統(tǒng)用?"
答案是:不能。
或者說(shuō),不完全能。
咱先來(lái)搞清楚什么叫實(shí)時(shí)操作系統(tǒng)。
很多人以為實(shí)時(shí)就是"快",其實(shí)不是。實(shí)時(shí)的核心是"可預(yù)測(cè)性",也就是說(shuō),系統(tǒng)對(duì)事件的響應(yīng)時(shí)間必須是確定的、有上限的。
舉個(gè)例子。一個(gè)普通系統(tǒng),處理一個(gè)中斷,可能需要10微秒,也可能需要1毫秒,甚至更長(zhǎng)。這個(gè)時(shí)間是不確定的。
但實(shí)時(shí)系統(tǒng)不行。實(shí)時(shí)系統(tǒng)必須保證,處理這個(gè)中斷,最壞情況下也不會(huì)超過(guò)100微秒。這個(gè)"最壞情況"的上限,就是實(shí)時(shí)性的關(guān)鍵。
Linux為什么做不到?
因?yàn)長(zhǎng)inux的設(shè)計(jì)目標(biāo),從一開始就不是實(shí)時(shí)系統(tǒng)。
Linux是通用操作系統(tǒng),設(shè)計(jì)目標(biāo)是高吞吐量、高并發(fā)、好的平均性能。為了達(dá)到這個(gè)目標(biāo),Linux做了很多優(yōu)化:復(fù)雜的調(diào)度算法、動(dòng)態(tài)內(nèi)存管理、緩存機(jī)制、中斷合并...
這些優(yōu)化能提升平均性能,但會(huì)犧牲實(shí)時(shí)性。
比如說(shuō),Linux的調(diào)度器。Linux用的是CFS(完全公平調(diào)度器),它的目標(biāo)是讓所有進(jìn)程公平地分享CPU時(shí)間。但"公平"和"實(shí)時(shí)"是矛盾的。
實(shí)時(shí)系統(tǒng)需要的是:高優(yōu)先級(jí)的任務(wù)必須立即執(zhí)行,不管其他任務(wù)等了多久。但CFS會(huì)考慮所有任務(wù)的等待時(shí)間,避免某個(gè)任務(wù)餓死。
這就導(dǎo)致,即使你設(shè)置了SCHED_FIFO,Linux也不能保證你的任務(wù)一定能在規(guī)定時(shí)間內(nèi)執(zhí)行。
為什么?因?yàn)長(zhǎng)inux內(nèi)核本身不是完全可搶占的。
什么意思?就是說(shuō),當(dāng)內(nèi)核在執(zhí)行某些關(guān)鍵代碼的時(shí)候,是不能被打斷的。比如說(shuō),內(nèi)核在操作某個(gè)數(shù)據(jù)結(jié)構(gòu),為了保證一致性,會(huì)加鎖。這個(gè)鎖持有期間,即使來(lái)了一個(gè)高優(yōu)先級(jí)的實(shí)時(shí)任務(wù),也得等著。
這個(gè)等待時(shí)間,就是不可預(yù)測(cè)的。
我當(dāng)年做汽車電子的時(shí)候,就遇到過(guò)這個(gè)問(wèn)題。我們有個(gè)控制模塊,需要每10毫秒采集一次傳感器數(shù)據(jù)。我用SCHED_FIFO把這個(gè)任務(wù)設(shè)成最高優(yōu)先級(jí),理論上應(yīng)該沒問(wèn)題。
但實(shí)際測(cè)試的時(shí)候,發(fā)現(xiàn)偶爾會(huì)有延遲。大部分時(shí)候是10毫秒,但有時(shí)候會(huì)到15毫秒,甚至20毫秒。
為什么?后來(lái)分析發(fā)現(xiàn),是因?yàn)閮?nèi)核在處理網(wǎng)絡(luò)中斷的時(shí)候,禁用了搶占。我的實(shí)時(shí)任務(wù)雖然優(yōu)先級(jí)高,但也得等內(nèi)核處理完網(wǎng)絡(luò)中斷才能執(zhí)行。
這就是Linux的問(wèn)題:它不能保證最壞情況下的響應(yīng)時(shí)間。
那有沒有辦法改進(jìn)呢?有,就是PREEMPT_RT補(bǔ)丁。
PREEMPT_RT是一個(gè)內(nèi)核補(bǔ)丁,它的目標(biāo)就是讓Linux變成實(shí)時(shí)系統(tǒng)。它做了什么呢?
第一,讓內(nèi)核完全可搶占。原來(lái)那些不能被打斷的代碼段,現(xiàn)在都可以被打斷了。
第二,把中斷處理線程化。原來(lái)中斷處理是在中斷上下文執(zhí)行的,優(yōu)先級(jí)最高,不能被搶占?,F(xiàn)在把中斷處理變成內(nèi)核線程,可以被更高優(yōu)先級(jí)的任務(wù)搶占。
第三,把自旋鎖改成可睡眠的互斥鎖。原來(lái)持有自旋鎖的時(shí)候,是不能睡眠的,也不能被搶占。現(xiàn)在改成互斥鎖,持有鎖的線程可以被搶占,等待鎖的線程可以睡眠。
這些改動(dòng),大大降低了Linux的最大延遲。
我見過(guò)一個(gè)測(cè)試數(shù)據(jù):標(biāo)準(zhǔn)Linux內(nèi)核,最大延遲可能有幾毫秒;打了PREEMPT_RT補(bǔ)丁之后,最大延遲可以降到100微秒以內(nèi)。
這個(gè)數(shù)據(jù),對(duì)很多應(yīng)用來(lái)說(shuō),已經(jīng)夠用了。
但即使打了PREEMPT_RT補(bǔ)丁,Linux還是不能算嚴(yán)格意義上的硬實(shí)時(shí)系統(tǒng)。
為什么?因?yàn)檫€有一些因素是無(wú)法控制的。
比如說(shuō),內(nèi)存管理。Linux用的是虛擬內(nèi)存,有缺頁(yè)中斷。如果你的實(shí)時(shí)任務(wù)訪問(wèn)了一個(gè)不在內(nèi)存中的頁(yè)面,就會(huì)觸發(fā)缺頁(yè)中斷,需要從磁盤加載數(shù)據(jù)。這個(gè)時(shí)間,可能是幾毫秒,甚至更長(zhǎng)。
怎么辦?你得用mlock把實(shí)時(shí)任務(wù)的內(nèi)存鎖定在物理內(nèi)存中,避免缺頁(yè)。
再比如,動(dòng)態(tài)內(nèi)存分配。malloc/free的時(shí)間是不確定的,可能很快,也可能很慢。如果你在實(shí)時(shí)任務(wù)中調(diào)用malloc,就可能導(dǎo)致延遲。
怎么辦?你得提前分配好內(nèi)存,或者用內(nèi)存池,避免在實(shí)時(shí)路徑上調(diào)用malloc。
還有,中斷。即使你的任務(wù)優(yōu)先級(jí)再高,也得等中斷處理完。如果系統(tǒng)中有很多中斷,而且中斷處理時(shí)間很長(zhǎng),你的實(shí)時(shí)任務(wù)就會(huì)被延遲。
怎么辦?你得優(yōu)化中斷處理,把耗時(shí)的操作放到線程中;或者用CPU隔離,把實(shí)時(shí)任務(wù)綁定到一個(gè)專門的核上,這個(gè)核不處理中斷。
所以你看,即使用了PREEMPT_RT,要讓Linux達(dá)到實(shí)時(shí)性能,還需要做很多額外的工作。
這就是為什么,真正對(duì)實(shí)時(shí)性要求極高的場(chǎng)景,還是會(huì)用專門的實(shí)時(shí)操作系統(tǒng),比如VxWorks、QNX、FreeRTOS...
這些系統(tǒng),從設(shè)計(jì)上就是為實(shí)時(shí)而生的。內(nèi)核小、可預(yù)測(cè)性強(qiáng)、最大延遲有保證。
但Linux也有它的優(yōu)勢(shì):生態(tài)好、功能全、社區(qū)活躍。很多應(yīng)用,不需要微秒級(jí)的實(shí)時(shí)性,毫秒級(jí)就夠了。這種情況下,用Linux + PREEMPT_RT,是個(gè)很好的選擇。
我現(xiàn)在做的很多項(xiàng)目,就是這個(gè)方案。汽車電子、工業(yè)控制,這些領(lǐng)域?qū)?shí)時(shí)性有要求,但不是極端的硬實(shí)時(shí)。用Linux + PREEMPT_RT,既能滿足實(shí)時(shí)性要求,又能享受Linux的生態(tài)。
所以,回到最初的問(wèn)題:為什么Linux不是實(shí)時(shí)操作系統(tǒng)?
因?yàn)樗脑O(shè)計(jì)目標(biāo)不是實(shí)時(shí)。
但通過(guò)PREEMPT_RT補(bǔ)丁和一些優(yōu)化手段,Linux可以達(dá)到軟實(shí)時(shí)的性能,滿足很多應(yīng)用的需求。
至于"把所有進(jìn)程都設(shè)成實(shí)時(shí)調(diào)度,能不能當(dāng)實(shí)時(shí)系統(tǒng)用"?
答案是:不能。
因?yàn)閷?shí)時(shí)調(diào)度只是解決了任務(wù)優(yōu)先級(jí)的問(wèn)題,但沒有解決內(nèi)核延遲、中斷延遲、內(nèi)存管理這些問(wèn)題。
真正的實(shí)時(shí)性,需要從內(nèi)核層面做優(yōu)化,而不只是改調(diào)度策略。
這就是答案。

END
來(lái)源:良許Linux