作為一個硬實時操作系統(tǒng),QNX是一個基于優(yōu)先級搶占的系統(tǒng)。這也導(dǎo)致其基本調(diào)度算法相對比較簡單。因為不需要像別的通用操作系統(tǒng)考慮一些復(fù)雜的“公平性”,只需要保證“優(yōu)先級最高的線程最優(yōu)先得到 CPU”就可以了。
基本調(diào)度算法
調(diào)度算法,是基于優(yōu)先級的。QNX的線程優(yōu)先級,是一個0-255的數(shù)字,數(shù)字越大優(yōu)先級越高。所以,優(yōu)先級0是內(nèi)核中的idle線程。同時,優(yōu)先級64是一個分界嶺。就是說,優(yōu)先級1 – 63 是非特權(quán)優(yōu)先級,一般用戶都可以用,而64 – 255必須是有root權(quán)限的線程才以設(shè)。這個“優(yōu)先級64”分界線,如果有必要,還可以通過啟動Procnto時傳 –P
圖 1 有兩個CPU的系統(tǒng)里的線程 這是一個有兩個CPU的系統(tǒng),所以可以看到有兩個RUNNING線程;對于 BLOCK THREAD,它們不參于調(diào)度,所以不需要考慮它們的優(yōu)先級。
調(diào)度策略
在QNX上實質(zhì)上只有三種基本調(diào)度策略,“輪詢”(Round Robin),“先進先出”(First in first out)和"零星調(diào)度”(Sporadic) 算法。雖然形式上還有一個“其他”,但“其他”跟“輪詢”是一樣的。這些調(diào)度策略,在 /usr/include/sched.h 里有定義。(SCHED_FIFO, SCHED_RR, SCHED_SPORADIC, SCHED_OTHER) 強調(diào)一下,調(diào)度策略只限于在READY隊列里的線程,優(yōu)線級最高的線程有不止一個時,才會用到。如果線程不再 READY,或是有別的更高優(yōu)先級的線程 READY了,那就高優(yōu)先級線程獲取CPU,沒有什么策略可言。 “輪詢調(diào)度”(Round Robin)跟平時生活里排隊的情形差不多,晚到的人排在隊尾,早到的人排在隊首,等到叫號(調(diào)度)的時候,隊首的人會被先叫到 。如下圖所示:
圖 2 論詢調(diào)度示意
首先在CPU 1上運行的線程4,被挪入優(yōu)先級15的隊列末尾
然后重新搜索可執(zhí)行的最高優(yōu)先級線程,這里有優(yōu)先級15隊列上的線程3和4
線程3因為在隊列最前端,它被選擇得到CPU,線程3的狀態(tài)變?yōu)镽UNNING,在CPU1上執(zhí)行
可以預(yù)期,當(dāng)下一次調(diào)度發(fā)生時,線程3會被挪入優(yōu)先級15隊列末尾,而線程4會被調(diào)度執(zhí)行,這樣線程3和4會分別得到CPU1. “先進先出”(First in first out)調(diào)度則剛好相反,后來的人插在隊首,然后在叫號的時候被先叫到??聪聢D: ?
圖 3 先進先出調(diào)度示意
首先在CPU 1上運行的線程4,被挪入優(yōu)先級15的隊列隊首
然后重新搜索可執(zhí)行的最高優(yōu)先級線程,這里有優(yōu)先級15隊列上的線程4和3
線程4因為在隊列最前端,它被選擇得到CPU,線程4的狀態(tài)變?yōu)镽UNNING,在CPU1上執(zhí)行
可以看到,在這個調(diào)度算法下,如果沒有別的狀態(tài)發(fā)生,事實上線程4就會一直占據(jù)CPU1。 如果在優(yōu)先級15上的線程3和線程4都是FIFO會怎樣?按上面的描述,線程3還是始終無法獲得CPU1,因為線程4每次都會插在3的前面,再調(diào)度就又是4獲得CPU1。除非線層4進入了阻塞狀態(tài)(從而不在READY隊列里了),那么線程3才能獲得CPU。 “零星調(diào)度”(Sporadic)算法比較特殊,它比較適合長時間占用CPU的線程。它的基本設(shè)計思想是給一個線程準(zhǔn)備兩個優(yōu)先級,“前臺”優(yōu)先級比較高,“后臺”優(yōu)先級稍微底一點。如果線程在高優(yōu)先級連續(xù)占用CPU超過一定時間后,線程會被強行降到“后臺”低優(yōu)先級上(這時線程能不能占用CPU取決于系統(tǒng)中有沒有比“后臺”優(yōu)先級高的別的線程了);然后線程在低優(yōu)先級上經(jīng)過了一段時間后,會重新被調(diào)回高優(yōu)先級。
圖 4 零星調(diào)度示意 上圖是一個零星調(diào)度線程的示意。
開始的時候,線程在比較高的(正常)優(yōu)先級 H 上運行,一直到把預(yù)先分配給零星調(diào)度的時間用完(sched_ss_init_budget)
這時,線程會被自動調(diào)整為低優(yōu)先級L(sched_ss_low_priority);一旦被調(diào)低,線程也可能運行(如果優(yōu)先級L依然是系統(tǒng)里最高優(yōu)先級的線程),也可能無法運行呆在READY隊列里(系統(tǒng)里有比L更高的優(yōu)先級)
不管線程有沒有執(zhí)行,從最開始運行時間點算起,當(dāng)線程“執(zhí)行補充時間"(sched_ss_repl_period)過了以后,線程的優(yōu)先級被重新提到優(yōu)先級H,并試圖取得CPU來。
“零星調(diào)度”看上去比較“公平”,但是實際在用QNX的項目中,這個調(diào)度算法很少被用戶用到。主要是因為一般來說在QNX上很少有線程能夠“連續(xù)占用CPU”的。而且當(dāng)系統(tǒng)變得復(fù)雜,線程數(shù)成百上千后,這種上下調(diào)優(yōu)先級的做法,很容易出現(xiàn)別的后遺癥。
什么時候會發(fā)生調(diào)度?
上面介紹了QNX支持的幾個調(diào)度算法。那么,什么時候才會發(fā)生調(diào)度呢? QNX的設(shè)計目標(biāo)是一個硬實時操作系統(tǒng),所以,保證最高優(yōu)先級的線程在第一時間占據(jù)CPU是很重要的??紤]到線程的狀態(tài)都是在內(nèi)核中進行變化的(都是因為線程進行了某個內(nèi)核調(diào)用后變化的),所以QNX在每次從內(nèi)核調(diào)用退出時,都會進行一次線程調(diào)度,以保證最高優(yōu)先級的線程可以占據(jù)CPU。 得益于微內(nèi)核結(jié)構(gòu),QNX的內(nèi)核調(diào)用通常都非常短,或者說,每一個內(nèi)核調(diào)用,都能夠比較確定地知道要花多少時間。而且,因為微內(nèi)核系統(tǒng)的基本就是進程間通信,所以在QNX上,一段程序非常容易進入內(nèi)核并進行線程狀態(tài)切換,很少能有長時間占滿CPU的,在實際系統(tǒng)上測,現(xiàn)實上很少能有線程執(zhí)行完整個時間片的。 舉個例子,哪怕程序里只寫一個 printf("Hello World! "); 可是在libc庫里,最后這個會變成一個IO_WRITE消息,MsgSend() 給控制臺驅(qū)動;這時,在MsgSend()這個內(nèi)核調(diào)用里,會把printf() 的線程置為阻塞狀態(tài)(REPLY BLOCK),同時會把控制臺驅(qū)動的信息接收線程(從RECEIVE BLOCK)改到 READY狀態(tài),并放入 READY 隊列。當(dāng)退出MsgSend() 內(nèi)核調(diào)用時,線程調(diào)度發(fā)生,通常情況下(如果沒有別的線程READY 的話)控制臺驅(qū)動的信息接收線程被激活,并占據(jù)CPU. 如果用戶寫了一個既不內(nèi)核調(diào)用,也不放棄CPU的線程會怎么樣?那時候,時鐘中斷會發(fā)生,當(dāng)內(nèi)核記時到線程占據(jù)了一整個時間片(QNX上是4ms)后,內(nèi)核會強制當(dāng)前線程進入 READY,并重新調(diào)度。如果同一優(yōu)先級只有這一個線程(這是優(yōu)先級最高線程),那么調(diào)度后,還是這個線程獲取CPU。如果同一優(yōu)先級有別的線程存在,那么根據(jù)調(diào)度算法來決定哪個線程獲得CPU。 另一種常見情況是,由于某些別的原因?qū)е赂邇?yōu)先級線程被激活,比如網(wǎng)卡驅(qū)動中斷導(dǎo)致高優(yōu)先級驅(qū)動線程READY,所設(shè)時鐘到達導(dǎo)致高優(yōu)先級線程從阻塞狀態(tài)返回READY狀態(tài)了,當(dāng)前線程開放互斥鎖之類的線程同步對象,導(dǎo)致別的線程返回READY狀態(tài)了。這些,都會在從內(nèi)核調(diào)用退出時,進行調(diào)度。
中斷與優(yōu)先級
上面提到如果用戶線程長期占有CPU,時鐘中斷會打斷用戶線程。細心的讀者或許會有疑問,那中斷的優(yōu)先級是多少呢? 答案是在QNX這樣的實時操作系統(tǒng)里,“硬件中斷”永遠高于任何線程優(yōu)先級,哪怕你的線程優(yōu)先級到了255,只要有中斷發(fā)生,都要讓路,CPU會跳轉(zhuǎn)去執(zhí)行中斷處理程序,執(zhí)行完了再回歸用戶線程。事實上,能夠快速穩(wěn)定地響應(yīng)中斷處理,是一個實時操作系統(tǒng)的硬指標(biāo)。 我們這里說的是“硬件中斷”,就是說,當(dāng)外部設(shè)備,通過中斷控制器,向CPU發(fā)出中斷請求時,無論當(dāng)時CPU上執(zhí)行的線程優(yōu)先級是什么,都會先跳轉(zhuǎn)到內(nèi)核的中斷處理程序;中斷處理程序會去中斷控制器找到具體是哪一個源發(fā)生了中斷(中斷號),并據(jù)此,跳轉(zhuǎn)到該中斷號的中斷處理程序(通常是硬件驅(qū)動程序 通過 InterruptAttach() 掛接的函數(shù))。在這個過程中,如果當(dāng)前CPU正在處理另一個中斷,那么這時,會根據(jù)中斷的優(yōu)先級來決定是讓CPU繼續(xù)處理下去(當(dāng)前中斷進入等待);或者發(fā)生中斷搶占,新中斷的優(yōu)先級比舊中斷高,所以跳轉(zhuǎn)新中斷處理。 當(dāng)然,實際應(yīng)用中,特別是微內(nèi)核環(huán)境下,考慮中斷其實只是中斷設(shè)備給出的一個通知,對這中斷的響應(yīng)并不需要真的在中斷處理中進行,驅(qū)動程序可以選擇在普通線程中處理,QNX上有InterruptAttachEvent() 就是為了這個設(shè)計的。通常這里的“事件”會是一個“脈沖”,也就是說,當(dāng)硬件中斷發(fā)生,內(nèi)核檢查到相應(yīng)中斷綁定了事件。這時,不會跳轉(zhuǎn)到用戶中斷處理程序,而是直接發(fā)出那個脈沖,以激活一個外部(驅(qū)動器中)線程,在這線程中,做設(shè)備中斷所需要的處理。這樣做,雖然稍微增加了一些中斷延遲,但也帶來了不少好處。首先,這個外部線程同普通的用戶線程一樣,所以可以調(diào)用任何庫函數(shù),而中斷服務(wù)程序因為執(zhí)行環(huán)境的不同,有好多限制。其次,因為是普通用戶線程,就可以用線程調(diào)度的方法規(guī)定其優(yōu)先級(脈沖事件是帶優(yōu)先級的),使不同的設(shè)備中斷處理,跟正常業(yè)務(wù)邏輯更好地一起使用。
多CPU上的線程調(diào)度
現(xiàn)在同步多處理器(SMP)已經(jīng)相當(dāng)普及了。在SMP上,也就是說當(dāng)有多個CPU時,我們的調(diào)度算法有什么變化呢?比如一個有2個CPU的系統(tǒng),首先肯定,系統(tǒng)上可執(zhí)行線程中的最高優(yōu)先級線程,一定在2個CPU上的某一個上執(zhí)行;那,是不是第二高優(yōu)先級的線程就在另一個CPU上執(zhí)行呢? 雖然直覺上我們覺得應(yīng)該是這樣的(系統(tǒng)里的第一,第二高優(yōu)先級的線程占據(jù)CPU1和CPU2),但事實上,第二高優(yōu)先級的線程占據(jù)CPU2這件事,并不是必要的。實時搶占系統(tǒng)的要求是最高優(yōu)先級”必須“能夠搶占CPU,但對第二高優(yōu)先級并沒有規(guī)定。拿我們最開始的雙CPU圖再看一眼。
圖 5 有兩個CPU的系統(tǒng)里的線程 線程4以優(yōu)先級15占據(jù)CPU1這是毫無疑問的,但線程5只有優(yōu)先級12,為什么它可以占據(jù)CPU2,而線程3明明也有優(yōu)先級15,但只能排隊等候,這是不是優(yōu)先級倒置了?其實并沒有,如上所述,系統(tǒng)確實保證了“最高優(yōu)先級占據(jù)CPU”的要求,但在CPU2上執(zhí)行什么線程,除了線程本身的優(yōu)先級以外,還有一些別的因素可以權(quán)衡,其中一個在SMP上比較重要的,就是“線程躍遷”。 “線程躍遷”指的是一個線程,一會兒在CPU1上執(zhí)行,一會兒在CPU2上執(zhí)行。在SMP系統(tǒng)上,線程躍遷而導(dǎo)致的緩存清除與重置,會給系統(tǒng)性能帶來很大的影響。所以在線程調(diào)度時,盡量把線程調(diào)度到上次執(zhí)行時用的CPU,是SMP調(diào)度算法里比較重要的一環(huán)。上述例子中,很有可能就是線程3上一次是在CPU1上執(zhí)行的,而線程5雖然優(yōu)先級比較低,很有可能上一次就是在CPU2上執(zhí)行的。 實際應(yīng)用中,因為QNX的易于阻塞的特性,其實大多數(shù)情況下,還是符合“第一,第二高優(yōu)先級線程在CPU上執(zhí)行”的。只是,如果你觀察到了上述情形,也不需要擔(dān)心,設(shè)計上確實有可能不是第二高優(yōu)先級的線程在運行。 另一個多處理器上常見的應(yīng)用,是線程綁定。在正常情況下,把可執(zhí)行線程調(diào)度到哪一個CPU上,是由操作系統(tǒng)完成的。當(dāng)然操作系統(tǒng)會考慮“線程躍遷”等情形來做決定。但是,QNX的用戶也可以把線程綁定到某一個(或者某幾個)CPU上,這樣操作系統(tǒng)在調(diào)度時,會考慮用戶的要求來進行。綁定是通過ThreadCtl() 修改線程的 “RUNMASK” 來進行的,如果你有0,1,2,3 總共4個CPU,那么 0x00000003意味著線程可以在CPU0和CPU1上執(zhí)行,具體例子可以參考ThreadCtl()函數(shù)說明。更簡單的辦法,是通過QNX特有的 on 命令的 –C 參數(shù)來指定,這個指定的 runmask,還會自動繼承。所以你可以簡單的如下執(zhí)行: # on –C 0x00000003 Navigation &
# on –C 0x00000004 Media &
# on –C 0x00000008 System & 這樣來把不同的系統(tǒng)部署到不同的CPU上。 這樣做的好處當(dāng)然是可以減少比如因為系統(tǒng)繁忙而對導(dǎo)航帶來的影響,但不要忘了,另一面,如果所有 Media 線程都處于阻塞狀態(tài),上述綁定也限制了導(dǎo)航線程使用CPU2的可能,CPU2這時候就會空轉(zhuǎn)(執(zhí)行內(nèi)核 idle 線程)。
自適應(yīng)分區(qū)調(diào)度算法
前面我們提到過,在討論優(yōu)先級調(diào)度時,只是討論當(dāng)有多個優(yōu)先級相同的線程時,系統(tǒng)怎樣取舍。優(yōu)先級不一樣時,肯定是優(yōu)先級高的贏。但是“高出多少”并不是一個考量因素。兩個線程,一個優(yōu)先級10,另一個優(yōu)先級11的情況,和一個10,另一個40的情況是一樣的。并不會因為10和40差距比較大而有什么不同。 假如我們有紅藍兩個線程,它們的優(yōu)先級一樣,調(diào)度策略是RR,兩個線程都不阻塞,那么在10時間片的區(qū)間里,我們看到的就是這樣一個執(zhí)行結(jié)果: 也就是說,各占了50%的CPU。但只要把藍色線程提高哪怕1,執(zhí)行結(jié)果就成了下面這樣。 這種“非黑即白”的情形,是實時系統(tǒng)的基本要求(高優(yōu)先級搶占CPU)。但是當(dāng)然,現(xiàn)實情況有時候比較復(fù)雜。比如 “HMI渲染” 是需要經(jīng)常占據(jù)CPU的一個任務(wù)(這樣畫面才會順暢),但“用戶輸入”也是需要響應(yīng)比較快的(不然用戶的點擊就會沒有反應(yīng))。如果“用戶輸入”的優(yōu)先級太高的話,那用戶拖拽時,畫面就會卡頓甚至沒有反應(yīng)?反之,如果”HMI 渲染“的優(yōu)先級太高,那么有用戶輸入時,因為處理程序優(yōu)先級低而造成用戶輸入反應(yīng)慢。通常情況下,需要有經(jīng)驗的系統(tǒng)工程師不斷調(diào)整這兩個任務(wù)的優(yōu)先級(因為優(yōu)先級繼承與傳統(tǒng),一個任務(wù)可能涉及到多個線程),來達到系統(tǒng)的最優(yōu)。那么,有沒有別的辦法呢?
分區(qū)調(diào)度
傳統(tǒng)上,有一種“分區(qū)調(diào)度”的方法,今天還有一些Hypervisor采取這個辦法。這個想法很簡單,就是把CPU算力隔成幾個分區(qū),比如70%,30%這樣,然后把不同線程分到這些分區(qū)里,當(dāng)分區(qū)里的CPU預(yù)算被用完以后,那個分區(qū)里所有可執(zhí)行線程都會被”停住“,直到預(yù)算恢復(fù)。 假設(shè)我們把紅線程放入70%紅色分區(qū),藍線程放入30%藍色分區(qū),然后以10個時間片為預(yù)算滑動窗口大小,各線程具體就會如下圖占據(jù)CPU:
圖 6 分區(qū)調(diào)度算力全滿示意 在前6個時間片中,藍紅分區(qū)分別占據(jù)CPU,注意在第7個時間片時,雖然藍分區(qū)中線程跟紅分區(qū)中線程有相同的優(yōu)先級,雖然調(diào)度策略是輪回,應(yīng)該輪到藍線程上了,但是因為藍線程已經(jīng)用完了10個時間片里的3個,所以系統(tǒng)沒有執(zhí)行藍線程,而是繼續(xù)讓紅線程占據(jù)CPU,一直到第8第9和第10個時間片結(jié)束。 10個時間片結(jié)束后,窗口向右滑動,這時我們等于又多了一個時間片的預(yù)算,在新的10個時間片中,藍線程只占了兩個(20%),這樣,新的第11個時間片,就分給了藍分區(qū)。 同理再滑動后,第12個時間片,分給紅線程;一直到17個時間片時,同樣的事情再度發(fā)生,藍分區(qū)線程又用完了10個時間片里的3個,而被迫等待它的預(yù)算重新補充進來。 綜上,在任意一個滑動窗口中,藍色分區(qū)總是只占30%,而紅色分區(qū)卻占了70%。QNX的自適應(yīng)分區(qū)調(diào)度,跟上面這個是類似的。只是傳統(tǒng)的分區(qū)調(diào)度,有一個明顯的弱點。 想一下這個情況,如果紅線程因為某些情況被阻塞了,會發(fā)生什么呢?
圖 7 分區(qū)調(diào)度算力有富余示意 對,藍線程是唯一可執(zhí)行線程,所以它一直占據(jù)CPU。但是,當(dāng)3個時間片輪轉(zhuǎn)之后,因為藍分區(qū)只有30%的時間預(yù)算,它將不再占據(jù)CPU,而因為紅線程無法執(zhí)行,接下來的7個時間片CPU處于空轉(zhuǎn)狀態(tài)(執(zhí)行Idle線程)。 一直到時間窗口移動,那時,因為藍分區(qū)只占用了20%的算力,所以它再次占據(jù)CPU…… 所以你也看到了,在傳統(tǒng)的分區(qū)調(diào)度里,當(dāng)一個分區(qū)的算力有富裕的時候,CPU就被浪費了。
自適應(yīng)分區(qū)調(diào)度
QNX在傳統(tǒng)的分區(qū)調(diào)度上,增加了“自適應(yīng)”的部份。其基本思想是一樣的,給算力加分區(qū),然后把不同的線程分到分區(qū)里。這樣,當(dāng)所有的線程都忙起來時,你會發(fā)現(xiàn)情況跟圖7是一樣的。但是當(dāng)分區(qū)算力有富裕時,“自適應(yīng)“允許把多出來的算力”借“給需要更多算力的分區(qū)。
圖 8 自適就分區(qū)算力有富裕示意 如上,當(dāng)藍色分區(qū)里的線程消耗完了他自己的分區(qū)預(yù)算后,自適應(yīng)分區(qū)會把有富裕算力的紅色分區(qū)的預(yù)算,借給藍色分區(qū),藍分區(qū)內(nèi)線程得以繼續(xù)在CPU上運行。注意,在第8個時間片時,紅色分區(qū)需要使用CPU,藍色分區(qū)立即讓路,把CPU讓給紅色分區(qū)。而當(dāng)紅色分區(qū)里的線程被阻塞住以后,藍色分區(qū)線程繼續(xù)使用CPU。 自適應(yīng)分區(qū)似乎確實帶來了好處,但是也帶來了一些潛在的問題,需要在系統(tǒng)設(shè)計的時候做好決定。
自適應(yīng)分區(qū)調(diào)度與線程優(yōu)先級
你可能會好奇,在分區(qū)調(diào)度的系統(tǒng)里,線程的優(yōu)先級代表了什么? 答案取決于各個分區(qū)對各自算力的消耗情況。我們假設(shè)藍色分區(qū)里的線程優(yōu)先級比較高,紅色的優(yōu)先級比較低,當(dāng)兩個分區(qū)都有預(yù)算時,內(nèi)核會調(diào)度(所有分區(qū)里的)最高優(yōu)先級線程執(zhí)行。如果系統(tǒng)一直不是很忙,那么不論分區(qū),永遠是有最高優(yōu)先級的線程得到CPU,這個,跟一個標(biāo)準(zhǔn)的實時操作系統(tǒng)是一致的。 當(dāng)兩個分區(qū)中某一個有預(yù)算時(意味著那個分區(qū)中所有的線程都不在執(zhí)行狀態(tài)),那么多出來的CPU算力會被分給另一個分區(qū),另一個分區(qū)中的最高優(yōu)先級線程(雖然用完了自己分區(qū)的預(yù)算,但得到了別的分區(qū)的算力),繼續(xù)占據(jù)CPU。這個,也是跟實時操作系統(tǒng)是一致的。 比較特殊的情況是,當(dāng)兩個分區(qū)都沒有預(yù)算,都需要占據(jù)CPU時,這時,藍色線程雖然有較高的優(yōu)先級,但因為分區(qū)算力(30%)被用完,面且沒有別的算力可以“借”,所以它被留在READY隊列中,而比它優(yōu)先級低的紅色線程得以占據(jù)CPU。
自適應(yīng)分區(qū)調(diào)度富裕算力分配
我們上面的例子只有兩個分區(qū),考慮這樣一個例子。假設(shè)我們現(xiàn)在有A (70%),B (20%),C (10%) 三個分區(qū),A分區(qū)沒有可執(zhí)行線程,B分區(qū)有個優(yōu)先級為10的線程,C分區(qū)有個優(yōu)先級為20的線程。我們知道A分區(qū)的70%會分配給B和C,但具體是怎么分配的呢? 如上所述,當(dāng)預(yù)算有富裕時,系統(tǒng)挑選所有分區(qū)中,優(yōu)先級最高的線程執(zhí)行,也就是說C分區(qū)中的線程得到運行。在一個窗口以后,你會發(fā)現(xiàn)A的CPU使用率是0%,B是20%,C則達到了80%。也就是說A所有的富裕算力,都給了C分區(qū)(因為C中的線程優(yōu)先級高)。 也許,在某些時候,這個不是你所期望的。也許C中有一些第三方程序你無法控制,你也不希望他們偷偷提高優(yōu)先級而占用全部富裕算力。QNX提供了SchedCtl()函數(shù),可以設(shè)SCHED_APS_FREETIME_BY_RATIO 標(biāo)志。設(shè)了這個標(biāo)志后,富裕算力會按照各分區(qū)的預(yù)算比例分配給各分區(qū)。上面的例子下,最后的CPU使用率會變成 A 是0%, B是65%,而C是35%。A分區(qū)富裕的70%算力,按照大約 2: 1的比例,分給了分區(qū)B和C。
“關(guān)鍵線程”與“關(guān)鍵分區(qū)”
在實際使用中,有一些重要任務(wù),可能需要響應(yīng),不論其所在的分區(qū)還有沒有算力。比如一個緊急中斷服務(wù)線程,不管分區(qū)是不是還有預(yù)算,都需要響應(yīng)。為了解決這種情況,在QNX的自適應(yīng)分區(qū)調(diào)度里,除了給分區(qū)分配算力預(yù)算以外,還允許有權(quán)限的用戶為分區(qū)分配“關(guān)鍵響應(yīng)時間”,并把特定線程定義為“關(guān)鍵線程”。 當(dāng)一個“關(guān)鍵線程”需要執(zhí)行時,如果線程所在分區(qū)有預(yù)算,它就直接使用所在分區(qū)預(yù)算就好,如同普通線程;如果所在分區(qū)沒有預(yù)算了,但是別的分區(qū)還有預(yù)算,那么“自適應(yīng)”部份會把別分區(qū)的預(yù)算拿過來,并用于關(guān)鍵線程,這個跟普通的自適應(yīng)分區(qū)調(diào)度一樣。 只有當(dāng)系統(tǒng)里所有分區(qū)都沒有預(yù)算了,而有一個關(guān)鍵線程需要運行,而且線程所在的分區(qū)已經(jīng)預(yù)先分配了關(guān)鍵響應(yīng)預(yù)算,那么線程允許“突破”分區(qū)的預(yù)算,使用“關(guān)鍵響應(yīng)預(yù)算”來執(zhí)行。在QNX里,一個關(guān)鍵線程消耗的時間,從退出RECEIVE_BLOCK開始,到下一次進入RECEIVE_BLOCK。而且,關(guān)鍵線程的屬性是可傳遞的,如果關(guān)鍵線程在執(zhí)行中,給別的線程發(fā)送了消息,那個線程也會變成關(guān)鍵線程。 總的來說,關(guān)鍵線程是用來保證關(guān)鍵任務(wù)不會因為系統(tǒng)太忙而無法取得CPU時間。即使所有的分區(qū)都被占滿了,至少還有“關(guān)鍵響應(yīng)時間”可供關(guān)鍵線程來使用。當(dāng)然,一個系統(tǒng)里不應(yīng)該有太多的關(guān)鍵線程和關(guān)鍵響應(yīng)時間。理論上,假設(shè)所有的線程都是關(guān)鍵線程,那么整個系統(tǒng)其實就變成了一個普通的按優(yōu)先級調(diào)度的實時系統(tǒng),所有的分區(qū)和預(yù)算都不起作用了。 在最緊急的情況下,關(guān)鍵線程可以使用“關(guān)鍵響應(yīng)時間”來完成它的任務(wù)。如果“關(guān)鍵響應(yīng)時間”還是不夠,會怎么樣?這個是系統(tǒng)設(shè)計問題,在設(shè)計系統(tǒng)的時候,你就應(yīng)該為關(guān)鍵線程分配它能夠完成任務(wù)所需要的最大時間。如果依然發(fā)生“關(guān)鍵響應(yīng)時間”不夠的狀況(被稱為“破產(chǎn)”狀態(tài)),這個就是一個設(shè)計錯誤了。
關(guān)鍵線程的破產(chǎn)
如上所述,關(guān)鍵線程的破產(chǎn)是一個設(shè)計問題?;蛘呔€程完成的任務(wù)并不那么“關(guān)鍵”,或者設(shè)計時給出的預(yù)算不夠。這種情況下需要重新審視整個系統(tǒng)設(shè)計(因為系統(tǒng)在某些情況下無法保證關(guān)鍵任務(wù)在預(yù)定時間內(nèi)完成)。QNX在自適應(yīng)分區(qū)里提供了偵測到關(guān)鍵線程破產(chǎn)時的多種響應(yīng)辦法,可以是強行忽視,或者重啟系統(tǒng),或者由自適應(yīng)分區(qū)系統(tǒng)自動調(diào)整分區(qū)的預(yù)算。
自適應(yīng)分區(qū)繼承
想像這個場景,文件系統(tǒng)在System分區(qū)里,但另一個Others分區(qū)里的第三方應(yīng)用拼命調(diào)用文件系統(tǒng),很有可能造成System分區(qū)的預(yù)算耗盡;這樣,首先可能導(dǎo)致別的應(yīng)用無法使用文件系統(tǒng);更嚴重的,可能是System分區(qū)里別的系統(tǒng),比如Audio也無法正常工作。這個,顯然是自適應(yīng)分區(qū)系統(tǒng)帶來的安全隱患。 解決辦法,就是跟優(yōu)先級在消息傳遞上可以繼承一樣,分區(qū)也是可以繼承的。文件系統(tǒng)雖然分配在System系統(tǒng)里,但根據(jù)它響應(yīng)的是誰的請求,時間被記到請求服務(wù)的線程分區(qū)里。這樣,如果一個第三方應(yīng)用拼命調(diào)用文件系統(tǒng),最多能做的,也只是消耗它自己的分區(qū),當(dāng)他自己分區(qū)的預(yù)算被耗盡時,影響它自己的CPU占用率。
自適應(yīng)分區(qū)的小結(jié)
自適應(yīng)分區(qū)有一些有趣的用法,比如我們常常被要求“系統(tǒng)需要保留30%的算力”。有了自適應(yīng)分區(qū),就可以建一個有30%預(yù)算的分區(qū),在里面跑一個 for (;;); 這樣的死循環(huán)。這樣,剩下的系統(tǒng)就只有70%的算力了,可以在這個環(huán)境下檢驗一下系統(tǒng)的性能和穩(wěn)定性。 自適應(yīng)分區(qū)的具體操作方法,可以參考QNX的文檔。不同版本的QNX有稍微不同的命令行,但基本設(shè)計是一樣的。這篇文章只是介紹了自適應(yīng)分區(qū)的基本概念,實際使用上,還是有許多細節(jié)需要考慮的,真的要使用,還是需要詳細參考QNX對應(yīng)文檔。 ?
編輯:黃飛
?
評論
查看更多