Linux實時調(diào)度實戰(zhàn):優(yōu)化你的應(yīng)用性能
在如今數(shù)字化浪潮洶涌的時代,無論是工業(yè)自動化生產(chǎn)線的精準(zhǔn)操控,還是多媒體應(yīng)用中流暢的音視頻播放體驗,又或是醫(yī)療設(shè)備對生命體征的實時監(jiān)測,背后都離不開一個關(guān)鍵因素 —— 系統(tǒng)的實時響應(yīng)能力。而 Linux 系統(tǒng),作為開源世界的中流砥柱,在眾多領(lǐng)域廣泛應(yīng)用。但默認(rèn)情況下,Linux 的進(jìn)程調(diào)度主要側(cè)重于公平性,對于那些對時間極為敏感的實時任務(wù)而言,原生的調(diào)度機(jī)制顯得有些力不從心。
那么,如何才能挖掘 Linux 系統(tǒng)的潛力,讓它在實時場景中大放異彩,顯著優(yōu)化應(yīng)用性能呢?今天,就帶大家走進(jìn) Linux 實時調(diào)度的實戰(zhàn)世界,一同探索其中的奧秘,掌握讓應(yīng)用性能飛躍的實用技巧 。
一、Linux調(diào)度概述
在日常生活中,我們常常會遇到排隊的場景。比如在超市結(jié)賬時,顧客們會在收銀臺前依次排隊等待結(jié)賬。在這個過程中,收銀員會按照排隊的順序為顧客服務(wù),這就是一種簡單的調(diào)度方式。而在 Linux 操作系統(tǒng)中,也存在著類似的調(diào)度機(jī)制,它負(fù)責(zé)管理系統(tǒng)中的進(jìn)程,決定哪個進(jìn)程可以獲得 CPU 資源并運行。
進(jìn)程是計算機(jī)中的程序關(guān)于某數(shù)據(jù)集合上的一次運行活動,是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位。在單處理器系統(tǒng)中,同一時刻只有一個進(jìn)程能夠在 CPU 上運行,就像只有一個收銀員為顧客服務(wù)一樣。而在多處理器系統(tǒng)中,雖然可以有多個進(jìn)程同時在不同的 CPU 上運行,但總體上仍然需要對進(jìn)程進(jìn)行合理的調(diào)度,以確保系統(tǒng)的高效運行。
Linux 系統(tǒng)中的進(jìn)程可以分為不同的類型,其中實時進(jìn)程對時間要求較高,它們需要在規(guī)定的時間內(nèi)完成任務(wù)。實時進(jìn)程又可以進(jìn)一步分為硬實時進(jìn)程和軟實時進(jìn)程。硬實時進(jìn)程必須在絕對的時間窗口內(nèi)完成任務(wù),否則可能會導(dǎo)致系統(tǒng)失效或災(zāi)難性后果,比如航空航天控制、醫(yī)療設(shè)備等領(lǐng)域的任務(wù)。軟實時進(jìn)程雖然也追求在規(guī)定時間內(nèi)完成任務(wù),但偶爾的超時通常不會導(dǎo)致系統(tǒng)完全失效,只會影響系統(tǒng)的服務(wù)質(zhì)量或用戶體驗,像多媒體處理、網(wǎng)絡(luò)通信等場景中的任務(wù)。除了實時進(jìn)程,還有普通進(jìn)程,它們對時間的要求相對較低,在系統(tǒng)資源分配中處于相對次要的地位。
為了實現(xiàn)對進(jìn)程的有效調(diào)度,Linux 系統(tǒng)采用了多種調(diào)度算法。其中,時間片輪轉(zhuǎn)調(diào)度算法是一種常見的調(diào)度方式。它將 CPU 的時間劃分為一個個固定長度的時間片,每個進(jìn)程輪流獲得一個時間片來運行。當(dāng)一個進(jìn)程的時間片用完后,即使它還沒有完成任務(wù),也會被暫停,然后被放入就緒隊列的末尾,等待下一輪調(diào)度。這種調(diào)度方式就像是超市里的顧客們輪流在收銀臺結(jié)賬,每個人都有機(jī)會得到服務(wù),從而保證了系統(tǒng)的公平性和響應(yīng)性。
實時調(diào)度器主要為了解決以下四種情況:
- 在喚醒任務(wù)時,待喚醒的任務(wù)放置到哪個運行隊列最合適(這里稱為pre-balance);
- 新喚醒任務(wù)的優(yōu)先級比某個運行隊列的當(dāng)前任務(wù)更低時,怎么處理這個更低優(yōu)先級任務(wù);
- 新喚醒任務(wù)的優(yōu)先級比同一運行隊列的某個任務(wù)更高時,并且搶占了該低優(yōu)先級任務(wù),該低優(yōu)先級任務(wù)怎么處理?
- 當(dāng)某個任務(wù)降低自身優(yōu)先級,導(dǎo)致原來更低優(yōu)先級任務(wù)相比之下具有更高優(yōu)先級,這種情況怎么處理。
對于情況2和情況3,實時調(diào)度器采用push操作。push操作從根域中所有運行隊列中挑選一個運行隊列(一個cpu對應(yīng)一個運行隊列),該運行隊列的優(yōu)先級比待push任務(wù)的優(yōu)先級更低。運行隊列的優(yōu)先級是指該運行隊列上所有任務(wù)的最高優(yōu)先級。
對于情況4,實時調(diào)度器采用pull操作。當(dāng)某個運行隊列上準(zhǔn)備調(diào)度時,候選任務(wù)比當(dāng)前任務(wù)的優(yōu)先級更低時,實時調(diào)度器檢查其他運行隊列,確定是否可以pull更高優(yōu)先級任務(wù)到本運行隊列。還有,當(dāng)某個運行隊列上發(fā)生調(diào)度時,該運行隊列上沒有任務(wù)比當(dāng)前任務(wù)優(yōu)先級高,實時調(diào)度器執(zhí)行pull操作,從其他運行隊列中pull更高優(yōu)先級任務(wù)到本運行隊列。
每CPU變量運行隊列rq,包含一個rt_rq數(shù)據(jù)結(jié)構(gòu)。rt_rq結(jié)構(gòu)體主要內(nèi)容如下:
struct rt_rq {
struct rt_prio_array active;
...
unsigned long rt_nr_running; // 可運行實時任務(wù)個數(shù)
unsigned long rt_nr_migratory; // 該運行隊列上可以遷移到其他運行隊列的實時任務(wù)個數(shù)
unsigned long rt_nr_uninterruptible;
int highest_prio;
int overloaded;
};
實時任務(wù)優(yōu)先級范圍為0到99。這些實時任務(wù)組織成優(yōu)先級索引數(shù)組active,該優(yōu)先級數(shù)組的數(shù)據(jù)結(jié)構(gòu)類型為rt_prio_arry。rt_prio_arry數(shù)據(jù)結(jié)構(gòu)由兩部分組成,一部分是位圖,另一部分是數(shù)組。
struct rt_prio_arry {
unsigned long bitmap[BITS_TO_LONGS(MAX_RT_PRIO+1)];
struct list_head queue[MAX_RT_PRIO];
}
二、實時調(diào)度策略
Linux內(nèi)核中提供了兩種實時調(diào)度策略:SCHED_FIFO和SCHED_RR,其中RR是帶有時間片的FIFO。這兩種調(diào)度算法實現(xiàn)的都是靜態(tài)優(yōu)先級。內(nèi)核不為實時進(jìn)程計算動態(tài)優(yōu)先級。這能保證給定優(yōu)先級別的實時進(jìn)程總能搶占優(yōu)先級比他低得進(jìn)程。linux的實時調(diào)度算法提供了一種軟實時工作方式。
實時優(yōu)先級范圍從0到MAX_RT_PRIO減一。默認(rèn)情況下,MAX_RT_PRIO為100(定義在include/linux/sched.h中),所以默認(rèn)的實時優(yōu)先級范圍是從0到99。SCHED_NORMAL級進(jìn)程的nice值共享了這個取值空間,它的取值范圍是從MAX_RT_PRIO到MAX_RT_PRIO+40。也就是說,在默認(rèn)情況下,nice值從-20到19直接對應(yīng)的是從100到139的優(yōu)先級范圍,這就是普通進(jìn)程的靜態(tài)優(yōu)先級范圍。在實時調(diào)度策略下。schedule()函數(shù)的運行會關(guān)聯(lián)到實時調(diào)度類rt_sched_class。
2.1SCHED_FIFO:獨占 CPU 的 “霸王龍”
在 Linux 的實時調(diào)度策略中,SCHED_FIFO 就像是恐龍時代的霸王龍,霸氣十足。它采用先進(jìn)先出(FIFO)的調(diào)度方式,這種方式非常直接,沒有復(fù)雜的時間片輪轉(zhuǎn)機(jī)制。一旦一個進(jìn)程被調(diào)度,只要沒有更高優(yōu)先級的進(jìn)程出現(xiàn),它就會一直霸占著 CPU 運行下去,運行時長不受任何限制。這就好比在一場比賽中,只要沒有更強(qiáng)的對手出現(xiàn),當(dāng)前的冠軍就可以一直保持領(lǐng)先,不會被替換下場。
以音頻處理場景為例,在實時音頻錄制和播放中,就經(jīng)常會用到 SCHED_FIFO 策略。在錄制音頻時,需要保證音頻數(shù)據(jù)的連續(xù)性和及時性,不能有絲毫的延遲或中斷。如果采用 SCHED_FIFO 策略,音頻錄制進(jìn)程一旦獲得 CPU 資源,就會持續(xù)運行,將麥克風(fēng)采集到的音頻數(shù)據(jù)及時地寫入存儲設(shè)備。在播放音頻時,音頻播放進(jìn)程也會獨占 CPU,按照順序?qū)⒁纛l數(shù)據(jù)從存儲設(shè)備中讀取出來,并發(fā)送到音頻輸出設(shè)備進(jìn)行播放。這樣可以確保音頻的流暢播放,不會出現(xiàn)卡頓或雜音的情況,為用戶帶來高品質(zhì)的音頻體驗。
SCHED_FIFO 策略的優(yōu)點顯而易見,它可以為那些對時間要求極為嚴(yán)格的實時進(jìn)程提供穩(wěn)定且可預(yù)測的執(zhí)行時間,這對于一些需要精確控制時間的系統(tǒng)來說至關(guān)重要,比如工業(yè)控制系統(tǒng)、機(jī)器人控制等領(lǐng)域。在這些系統(tǒng)中,任務(wù)的執(zhí)行時間必須是可預(yù)測的,否則可能會導(dǎo)致嚴(yán)重的后果。
然而,SCHED_FIFO 策略也存在明顯的缺點。由于它沒有時間片的概念,一旦一個低優(yōu)先級的進(jìn)程先獲得了 CPU 資源,并且一直不主動放棄,那么其他優(yōu)先級較低的進(jìn)程就可能會一直處于饑餓狀態(tài),無法獲得 CPU 資源來執(zhí)行。這就好比一群人在排隊等待服務(wù),但是排在前面的人一直占用著服務(wù)資源不離開,后面的人就只能一直等待,這顯然是不公平的。
2.2SCHED_RR:公平輪替的 “時間掌控者”
與 SCHED_FIFO 不同,SCHED_RR 像是一位公平的時間掌控者,采用時間片輪轉(zhuǎn)的調(diào)度機(jī)制。在這種策略下,每個進(jìn)程都會被分配一個固定的時間片。當(dāng)進(jìn)程運行時,時間片會逐漸減少。一旦進(jìn)程用完了自己的時間片,它就會被放入就緒隊列的末尾,同時釋放 CPU 資源,讓其他相同優(yōu)先級的進(jìn)程有機(jī)會執(zhí)行。這就像一場接力比賽,每個選手都有固定的跑步時間,時間一到就把接力棒交給下一位選手,保證了每個選手都有公平的參與機(jī)會。
以動畫渲染場景為例,在制作動畫時,通常會有多個任務(wù)同時進(jìn)行,比如模型渲染、材質(zhì)處理、光影計算等。這些任務(wù)可能具有相同的優(yōu)先級,需要合理地分配 CPU 資源。如果采用 SCHED_RR 策略,每個渲染任務(wù)都會被分配一個時間片。在自己的時間片內(nèi),任務(wù)可以充分利用 CPU 資源進(jìn)行計算和處理。當(dāng)時間片用完后,任務(wù)會暫停,將 CPU 資源讓給其他任務(wù)。這樣可以確保每個渲染任務(wù)都能得到及時的處理,不會因為某個任務(wù)長時間占用 CPU 而導(dǎo)致其他任務(wù)延遲,從而保證了動畫渲染的高效進(jìn)行。
SCHED_RR 策略在保證實時性的同時,還兼顧了公平性。它通過時間片的輪轉(zhuǎn),讓每個進(jìn)程都能在一定的時間內(nèi)獲得 CPU 資源,避免了低優(yōu)先級進(jìn)程長時間得不到執(zhí)行的情況。這使得它在一些對響應(yīng)時間要求較高,同時又需要保證公平性的實時進(jìn)程中得到了廣泛應(yīng)用,比如交互式應(yīng)用程序、游戲等。在這些應(yīng)用中,用戶希望能夠得到及時的響應(yīng),同時也不希望某個任務(wù)獨占 CPU 資源,導(dǎo)致其他操作變得遲緩。
三、實時調(diào)度類的數(shù)據(jù)結(jié)構(gòu)詳解
3.1優(yōu)先級隊列rt_prio_array
在kernel/sched.c中,是一組鏈表,每個優(yōu)先級對應(yīng)一個鏈表。還維護(hù)一個由101 bit組成的bitmap,其中實時進(jìn)程優(yōu)先級為0-99,占100 bit,再加1 bit的定界符。當(dāng)某個優(yōu)先級別上有進(jìn)程被插入列表時,相應(yīng)的比特位就被置位。 通常用sched_find_first_bit()函數(shù)查詢該bitmap,它返回當(dāng)前被置位的最高優(yōu)先級的數(shù)組下標(biāo)。由于使用位圖,查找一個任務(wù)來執(zhí)行所需要的時間并不依賴于活動任務(wù)的個數(shù),而是依賴于優(yōu)先級的數(shù)量??梢妼崟r調(diào)度是一個O(1)調(diào)度策略。
struct rt_prio_array {
DECLARE_BITMAP(bitmap, MAX_RT_PRIO+1); /* 包含1 bit的定界符 */
struct list_head queue[MAX_RT_PRIO];
};
這里用include/linux/types.h中的DECLARE_BITMAP宏來定義指定長度的位圖,用include/linux/list.h中的struct list_head來為100個優(yōu)先級定義各自的雙鏈表。在實時調(diào)度中,運行進(jìn)程根據(jù)優(yōu)先級放到對應(yīng)的隊列里面,對于相同的優(yōu)先級的進(jìn)程后面來的進(jìn)程放到同一優(yōu)先級隊列的隊尾。對于FIFO/RR調(diào)度,各自的進(jìn)程需要設(shè)置相關(guān)的屬性。進(jìn)程運行時,要根據(jù)task中的這些屬性判斷和設(shè)置,放棄cpu的時機(jī)(運行完或是時間片用完)。
3.2實時運行隊列rt_rq
在kernel/sched.c中,用于組織實時調(diào)度的相關(guān)信息。
struct rt_rq {
struct rt_prio_array active;
unsigned long rt_nr_running;
#if defined CONFIG_SMP || defined CONFIG_RT_GROUP_SCHED
struct {
int curr; /* 最高優(yōu)先級的實時任務(wù) */
#ifdef CONFIG_SMP
int next; /* 下一個最高優(yōu)先級的任務(wù) */
#endif
} highest_prio;
#endif
#ifdef CONFIG_SMP
unsigned long rt_nr_migratory;
unsigned long rt_nr_total;
int overloaded;
struct plist_head pushable_tasks;
#endif
int rt_throttled;
u64 rt_time;
u64 rt_runtime;
/* Nests inside the rq lock: */
spinlock_t rt_runtime_lock;
#ifdef CONFIG_RT_GROUP_SCHED
unsigned long rt_nr_boosted;
struct rq *rq;
struct list_head leaf_rt_rq_list;
struct task_group *tg;
struct sched_rt_entity *rt_se;
#endif
};
3.3實時調(diào)度實體 sched_rt_entity
在 Linux 內(nèi)核的實時調(diào)度機(jī)制中,sched_rt_entity結(jié)構(gòu)體扮演著至關(guān)重要的角色,它就像是一個精心打造的 “任務(wù)名片”,記錄了實時進(jìn)程參與調(diào)度所需的關(guān)鍵信息。該結(jié)構(gòu)體定義于include/linux/sched.h頭文件中,其源碼如下:
struct sched_rt_entity {
struct list_head run_list; // 用于將“實時調(diào)度實體”加入到優(yōu)先級隊列中的
unsigned long timeout; // 用于設(shè)置調(diào)度超時時間
unsigned long watchdog_stamp; // 用于記錄jiffies的值
unsigned int time_slice; // 時間片
unsigned short on_rq;
unsigned short on_list;
struct sched_rt_entity *back; // 用于由上到下連接“實時調(diào)度實體”
#ifdef CONFIG_RT_GROUP_SCHED
struct sched_rt_entity *parent; // 指向父類“實時調(diào)度實體”
/* rq on which this entity is (to be) queued: */
struct rt_rq *rt_rq; // 表示“實時調(diào)度實體”所屬的“實時運行隊列”
/* rq "owned" by this entity/group: */
struct rt_rq *my_q; // 表示“實時調(diào)度實體”所擁有的“實時運行隊列”,用于管理“子任務(wù)”
#endif
} __randomize_layout;
run_list字段是一個雙向鏈表節(jié)點,它就像一根無形的線,將各個實時調(diào)度實體按照優(yōu)先級串聯(lián)起來,加入到優(yōu)先級隊列中,方便調(diào)度器快速定位和處理。當(dāng)一個實時進(jìn)程被創(chuàng)建或者狀態(tài)發(fā)生變化時,它的run_list就會被插入到相應(yīng)的優(yōu)先級隊列中,等待調(diào)度器的調(diào)度。
timeout字段用于設(shè)置調(diào)度超時時間,這就像是給任務(wù)設(shè)定了一個 “鬧鐘”。當(dāng)任務(wù)運行時間超過這個設(shè)定的超時時間時,調(diào)度器可能會對其進(jìn)行特殊處理,比如將其從 CPU 上移除,重新調(diào)度其他任務(wù),以確保系統(tǒng)的實時性和穩(wěn)定性。在一些對時間要求極高的實時系統(tǒng)中,如自動駕駛汽車的控制系統(tǒng),每個任務(wù)都必須在規(guī)定的時間內(nèi)完成,否則可能會導(dǎo)致嚴(yán)重的后果。timeout字段就可以保證這些任務(wù)不會因為長時間占用 CPU 而影響其他關(guān)鍵任務(wù)的執(zhí)行。
watchdog_stamp字段用于記錄jiffies的值,jiffies是 Linux 內(nèi)核中的一個全局變量,表示系統(tǒng)啟動以來的時鐘滴答數(shù)。通過記錄jiffies的值,watchdog_stamp可以為調(diào)度器提供時間參考,用于判斷任務(wù)的運行狀態(tài)和調(diào)度時機(jī)。比如,調(diào)度器可以根據(jù)watchdog_stamp和當(dāng)前的jiffies值來計算任務(wù)的運行時間,從而決定是否需要對任務(wù)進(jìn)行調(diào)度。
time_slice字段表示時間片,對于采用時間片輪轉(zhuǎn)調(diào)度策略(如SCHED_RR)的實時進(jìn)程來說,這個字段尤為重要。它規(guī)定了每個進(jìn)程在被調(diào)度后可以連續(xù)運行的時間長度。當(dāng)進(jìn)程的時間片用完后,調(diào)度器會將其從 CPU 上移除,并將其放入就緒隊列的末尾,等待下一輪調(diào)度。這就像一場接力比賽,每個選手都有固定的跑步時間,時間一到就把接力棒交給下一位選手,保證了每個選手都有公平的參與機(jī)會。在多媒體播放系統(tǒng)中,音頻和視頻的解碼任務(wù)通常采用SCHED_RR策略,通過合理設(shè)置time_slice,可以確保音頻和視頻的流暢播放,不會出現(xiàn)卡頓或延遲的情況。
在支持實時組調(diào)度(CONFIG_RT_GROUP_SCHED)的情況下,parent字段指向父類 “實時調(diào)度實體”,這就像是一個家族樹中的父子關(guān)系,通過這種關(guān)系,調(diào)度器可以更好地管理和調(diào)度整個任務(wù)組。rt_rq字段表示 “實時調(diào)度實體” 所屬的 “實時運行隊列”,而my_q字段則表示 “實時調(diào)度實體” 所擁有的 “實時運行隊列”,用于管理 “子任務(wù)”。這種層次化的結(jié)構(gòu)設(shè)計,使得調(diào)度器能夠更加靈活地處理復(fù)雜的實時任務(wù)場景。
3.4實時就緒隊列 struct rt_rq
struct rt_rq結(jié)構(gòu)體是 Linux 內(nèi)核實時調(diào)度的核心數(shù)據(jù)結(jié)構(gòu)之一,它就像是一個高效的 “任務(wù)指揮官”,負(fù)責(zé)管理實時進(jìn)程的運行隊列,在核心調(diào)度器管理活動進(jìn)程中發(fā)揮著舉足輕重的作用。該結(jié)構(gòu)體定義于kernel/sched/sched.h頭文件中,其源碼如下:
struct rt_rq {
struct rt_prio_array active; // 優(yōu)先級隊列
unsigned int rt_nr_running; // 在RT運行隊列中所有活動的任務(wù)數(shù)
unsigned int rr_nr_running;
#if defined CONFIG_SMP || defined CONFIG_RT_GROUP_SCHED
struct {
int curr; // 當(dāng)前RT任務(wù)的最高優(yōu)先級
#ifdef CONFIG_SMP
int next; // 下一個要運行的RT任務(wù)的優(yōu)先級,如果兩個任務(wù)都有最高優(yōu)先級,則curr == next
#endif
} highest_prio;
#endif
#ifdef CONFIG_SMP
unsigned long rt_nr_migratory; // 任務(wù)沒有綁定在某個CPU上時,這個值會增減,用于任務(wù)遷移
unsigned long rt_nr_total; // 用于overload檢查
int overloaded; // RT運行隊列過載,則將任務(wù)推送到其他CPU
struct plist_head pushable_tasks; // 優(yōu)先級列表,用于推送過載任務(wù)
#endif /* CONFIG_SMP */
int rt_queued; // 表示RT運行隊列已經(jīng)加入rq隊列
int rt_throttled; // 用于限流操作
u64 rt_time; // 累加的運行時,超出了本地rt_runtime時,則進(jìn)行限制
u64 rt_runtime; // 分配給本地池的運行時
/* Nests inside the rq lock: */
raw_spinlock_t rt_runtime_lock;
#ifdef CONFIG_RT_GROUP_SCHED
unsigned long rt_nr_boosted; // 用于優(yōu)先級翻轉(zhuǎn)問題解決
struct rq *rq; // 指向運行隊列
struct task_group *tg; // 指向任務(wù)組
#endif
};
active字段是一個rt_prio_array類型的優(yōu)先級隊列,它維護(hù)了 100 個優(yōu)先級的隊列(鏈表),優(yōu)先級范圍從 0 到 99,從高到低排列。同時,它還定義了位圖,用于快速查詢。這就像是一個多層的貨架,每個貨架層對應(yīng)一個優(yōu)先級,實時進(jìn)程根據(jù)其優(yōu)先級被放置在相應(yīng)的貨架層上。調(diào)度器可以通過位圖快速找到最高優(yōu)先級的隊列,從而選擇優(yōu)先級最高的進(jìn)程進(jìn)行調(diào)度,大大提高了調(diào)度效率。在航空航天控制系統(tǒng)中,各種實時任務(wù)的優(yōu)先級劃分非常嚴(yán)格,通過active優(yōu)先級隊列,調(diào)度器能夠快速響應(yīng)高優(yōu)先級任務(wù),確保系統(tǒng)的安全和穩(wěn)定運行。
rt_nr_running字段表示在 “實時運行隊列” 中所有活動的任務(wù)數(shù),這個數(shù)字就像是一個實時監(jiān)控的計數(shù)器,調(diào)度器可以根據(jù)它來了解當(dāng)前實時運行隊列中的任務(wù)負(fù)載情況。如果任務(wù)數(shù)過多,調(diào)度器可能會采取一些措施,如任務(wù)遷移、限流等,以保證系統(tǒng)的正常運行。
在支持對稱多處理(CONFIG_SMP)或?qū)崟r組調(diào)度(CONFIG_RT_GROUP_SCHED)的情況下,highest_prio結(jié)構(gòu)體中的curr字段表示當(dāng)前 RT 任務(wù)的最高優(yōu)先級,next字段表示下一個要運行的 RT 任務(wù)的優(yōu)先級。如果兩個任務(wù)都有最高優(yōu)先級,則curr和next字段值相等。這些字段就像是調(diào)度器的 “指南針”,幫助調(diào)度器在眾多任務(wù)中準(zhǔn)確地選擇下一個要運行的任務(wù)。
rt_nr_migratory字段用于記錄任務(wù)沒有綁定在某個 CPU 上時,這個值會增減,用于任務(wù)遷移。在多處理器系統(tǒng)中,當(dāng)某個 CPU 的負(fù)載過高時,調(diào)度器可以根據(jù)這個字段的值,將一些可遷移的任務(wù)遷移到其他 CPU 上,以實現(xiàn)負(fù)載均衡。rt_nr_total字段用于overload檢查,當(dāng)rt_nr_total超過一定閾值時,說明系統(tǒng)可能處于過載狀態(tài),調(diào)度器會采取相應(yīng)的措施,如將任務(wù)推送到其他 CPU,以緩解系統(tǒng)壓力。overloaded字段表示 RT 運行隊列過載,當(dāng)該字段為真時,調(diào)度器會將任務(wù)推送到其他 CPU,以保證系統(tǒng)的正常運行。pushable_tasks字段是一個優(yōu)先級列表,用于推送過載任務(wù),它就像是一個 “任務(wù)搬運工”,將過載的任務(wù)從一個 CPU 推送到其他 CPU 上。
rt_queued字段表示 RT 運行隊列已經(jīng)加入rq隊列,rq隊列是系統(tǒng)中所有進(jìn)程的運行隊列,RT 運行隊列是其中的一部分。rt_throttled字段用于限流操作,當(dāng)實時進(jìn)程的運行時間超過一定限制時,調(diào)度器會對其進(jìn)行限流,以保證系統(tǒng)的公平性和穩(wěn)定性。rt_time字段表示累加的運行時,當(dāng)超出本地rt_runtime時,則進(jìn)行限制。rt_runtime字段表示分配給本地池的運行時,它就像是一個 “資源配額”,限制了實時進(jìn)程在本地的運行時間。
在支持實時組調(diào)度(CONFIG_RT_GROUP_SCHED)的情況下,rt_nr_boosted字段用于優(yōu)先級翻轉(zhuǎn)問題解決。在實時系統(tǒng)中,可能會出現(xiàn)優(yōu)先級翻轉(zhuǎn)的情況,即低優(yōu)先級任務(wù)持有高優(yōu)先級任務(wù)所需的資源,導(dǎo)致高優(yōu)先級任務(wù)無法執(zhí)行。通過rt_nr_boosted字段,調(diào)度器可以對任務(wù)的優(yōu)先級進(jìn)行調(diào)整,解決優(yōu)先級翻轉(zhuǎn)問題。rq字段指向運行隊列,tg字段指向任務(wù)組,通過這些指針,調(diào)度器可以更好地管理和調(diào)度整個任務(wù)組。
實時調(diào)度的主要操作:實時調(diào)度的操作在kernel/sched_rt.c中實現(xiàn)。
(1)進(jìn)程插入enqueue_task_rt:更新調(diào)度信息,調(diào)用enqueue_rt_entity()-->__enqueue_rt_entity(),將調(diào)度實體插入到相應(yīng)優(yōu)先級隊列的末尾。如下:
static void
enqueue_task_rt(struct rq *rq, struct task_struct *p, int wakeup, bool head)
{
struct sched_rt_entity *rt_se = &p->rt;
if (wakeup)
rt_se->timeout = 0;
enqueue_rt_entity(rt_se, head); /* 實際工作 */
if (!task_current(rq, p) && p->rt.nr_cpus_allowed > 1)
enqueue_pushable_task(rq, p); /* 添加到對應(yīng)的hash表中 */
}
static void enqueue_rt_entity(struct sched_rt_entity *rt_se, bool head)
{
dequeue_rt_stack(rt_se); /* 先從運行隊列中刪除 */
for_each_sched_rt_entity(rt_se)
__enqueue_rt_entity(rt_se, head); /* 然后添加到運行隊列尾部 */
}
static void __enqueue_rt_entity(struct sched_rt_entity *rt_se, bool head)
{
struct rt_rq *rt_rq = rt_rq_of_se(rt_se);
struct rt_prio_array *array = &rt_rq->active;
struct rt_rq *group_rq = group_rt_rq(rt_se);
struct list_head *queue = array->queue + rt_se_prio(rt_se);
/*
* Don't enqueue the group if its throttled, or when empty.
* The latter is a consequence of the former when a child group
* get throttled and the current group doesn't have any other
* active members.
*/
if (group_rq && (rt_rq_throttled(group_rq) || !group_rq->rt_nr_running))
return;
if (head)
list_add(&rt_se->run_list, queue);
else
list_add_tail(&rt_se->run_list, queue);
__set_bit(rt_se_prio(rt_se), array->bitmap);
inc_rt_tasks(rt_se, rt_rq); /* 運行進(jìn)程數(shù)增一 */
}
該函數(shù)先獲取運行隊列中的優(yōu)先級隊列,然后調(diào)用include/linux/list.h:list_add_tail()--->__list_add(),將進(jìn)程插入到鏈表的末尾。如下:
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
(2)進(jìn)程選擇pick_next_task_rt:實時調(diào)度會選擇最高優(yōu)先級的實時進(jìn)程來運行。調(diào)用_pick_next_task_rt()--->pick_next_rt_entity()來完成獲取下一個進(jìn)程的工作。如下:
static struct task_struct *pick_next_task_rt(struct rq *rq)
{
struct task_struct *p = _pick_next_task_rt(rq); /* 實際工作 */
/* The running task is never eligible for pushing */
if (p)
dequeue_pushable_task(rq, p);
#ifdef CONFIG_SMP
/*
* We detect this state here so that we can avoid taking the RQ
* lock again later if there is no need to push
*/
rq->post_schedule = has_pushable_tasks(rq);
#endif
return p;
}
static struct task_struct *_pick_next_task_rt(struct rq *rq)
{
struct sched_rt_entity *rt_se;
struct task_struct *p;
struct rt_rq *rt_rq;
rt_rq = &rq->rt;
if (unlikely(!rt_rq->rt_nr_running))
return NULL;
if (rt_rq_throttled(rt_rq))
return NULL;
do { /* 遍歷組調(diào)度中的每個進(jìn)程 */
rt_se = pick_next_rt_entity(rq, rt_rq);
BUG_ON(!rt_se);
rt_rq = group_rt_rq(rt_se);
} while (rt_rq);
p = rt_task_of(rt_se);
/* 更新執(zhí)行域 */
p->se.exec_start = rq->clock_task;
return p;
}
static struct sched_rt_entity *pick_next_rt_entity(struct rq *rq,
struct rt_rq *rt_rq)
{
struct rt_prio_array *array = &rt_rq->active;
struct sched_rt_entity *next = NULL;
struct list_head *queue;
int idx;
/* 找到第一個可用的 */
idx = sched_find_first_bit(array->bitmap);
BUG_ON(idx >= MAX_RT_PRIO);
/* 從鏈表組中找到對應(yīng)的鏈表 */
queue = array->queue + idx;
next = list_entry(queue->next, struct sched_rt_entity, run_list);
/* 返回找到的運行實體 */
return next;
}
該函數(shù)調(diào)用include/asm-generic/bitops/sched.h:sched_find_first_bit()返回位圖中當(dāng)前被置位的最高優(yōu)先級,以作為這組鏈表的數(shù)組下標(biāo)找到其優(yōu)先級隊列。然后調(diào)用include/linux/list.h:list_entry()--->include/linux/kernel.h:container_of(),返回該優(yōu)先級隊列中的第一個進(jìn)程,以作為下一個要運行的實時進(jìn)程。例如當(dāng)前所有實時進(jìn)程中最高優(yōu)先級為45(換句話說,系統(tǒng)中沒有任何實時進(jìn)程的優(yōu)先級小于45),則直接讀取rt_prio_array中的queue[45],得到優(yōu)先級為45的進(jìn)程隊列指針。該隊列頭上的第一個進(jìn)程就是被選中的進(jìn)程。這種算法的復(fù)雜度為O(1)。
sched_find_first_bit的實現(xiàn)如下。它與CPU體系結(jié)構(gòu)相關(guān),其他體系結(jié)構(gòu)會實現(xiàn)自己的sched_find_fist_bit函數(shù)。下面的實現(xiàn)以最快的方式搜索100 bit的位圖,它能保證100 bit中至少有一位被清除。
static inline int sched_find_first_bit(const unsigned long *b)
{
#if BITS_PER_LONG == 64
if (b[0])
return __ffs(b[0]);
return __ffs(b[1]) + 64;
#elif BITS_PER_LONG == 32
if (b[0])
return __ffs(b[0]);
if (b[1])
return __ffs(b[1]) + 32;
if (b[2])
return __ffs(b[2]) + 64;
return __ffs(b[3]) + 96;
#else
#error BITS_PER_LONG not defined
#endif
}
(3)進(jìn)程刪除dequeue_task_rt:從優(yōu)先級隊列中刪除實時進(jìn)程,并更新調(diào)度信息,然后把這個進(jìn)程添加到隊尾。調(diào)用鏈為dequeue_rt_entity()--->dequeue_rt_stack()--->__dequeue_rt_entity(),如下:
static void dequeue_task_rt(struct rq *rq, struct task_struct *p, int sleep)
{
struct sched_rt_entity *rt_se = &p->rt;
/* 更新調(diào)度信息 */
update_curr_rt(rq);
/* 實際工作,將rt_se從運行隊列中刪除然后
添加到隊列尾部 */
dequeue_rt_entity(rt_se);
/* 從hash表中刪除 */
dequeue_pushable_task(rq, p);
}
static void update_curr_rt(struct rq *rq)
{
struct task_struct *curr = rq->curr;
struct sched_rt_entity *rt_se = &curr->rt;
struct rt_rq *rt_rq = rt_rq_of_se(rt_se);
u64 delta_exec;
if (!task_has_rt_policy(curr)) /* 判斷是否問實時調(diào)度進(jìn)程 */
return;
/* 執(zhí)行時間 */
delta_exec = rq->clock_task - curr->se.exec_start;
if (unlikely((s64)delta_exec < 0))
delta_exec = 0;
schedstat_set(curr->se.exec_max, max(curr->se.exec_max, delta_exec));
/* 更新當(dāng)前進(jìn)程的總的執(zhí)行時間 */
curr->se.sum_exec_runtime += delta_exec;
account_group_exec_runtime(curr, delta_exec);
/* 更新執(zhí)行的開始時間 */
curr->se.exec_start = rq->clock_task;
cpuacct_charge(curr, delta_exec); /* 組調(diào)度相關(guān) */
sched_rt_avg_update(rq, delta_exec);
if (!rt_bandwidth_enabled())
return;
for_each_sched_rt_entity(rt_se) {
rt_rq = rt_rq_of_se(rt_se);
if (sched_rt_runtime(rt_rq) != RUNTIME_INF) {
spin_lock(&rt_rq->rt_runtime_lock);
rt_rq->rt_time += delta_exec;
if (sched_rt_runtime_exceeded(rt_rq))
resched_task(curr);
spin_unlock(&rt_rq->rt_runtime_lock);
}
}
}
static void dequeue_rt_entity(struct sched_rt_entity *rt_se)
{
dequeue_rt_stack(rt_se); /* 從運行隊列中刪除 */
for_each_sched_rt_entity(rt_se) {
struct rt_rq *rt_rq = group_rt_rq(rt_se);
if (rt_rq && rt_rq->rt_nr_running)
__enqueue_rt_entity(rt_se, false); /* 添加到隊尾 */
}
}
static void dequeue_rt_stack(struct sched_rt_entity *rt_se)
{
struct sched_rt_entity *back = NULL;
for_each_sched_rt_entity(rt_se) { /* 遍歷整個組調(diào)度實體 */
rt_se->back = back; /* 可見rt_se的back實體為組調(diào)度中前一個調(diào)度實體 */
back = rt_se;
}
/* 將組中的所有進(jìn)程從運行隊列中移除 */
for (rt_se = back; rt_se; rt_se = rt_se->back) {
if (on_rt_rq(rt_se))
__dequeue_rt_entity(rt_se);
}
}
static void __dequeue_rt_entity(struct sched_rt_entity *rt_se)
{
struct rt_rq *rt_rq = rt_rq_of_se(rt_se);
struct rt_prio_array *array = &rt_rq->active;
/* 移除進(jìn)程 */
list_del_init(&rt_se->run_list);
/* 如果鏈表變?yōu)榭?,則將位圖中對應(yīng)的bit位清零 */
if (list_empty(array->queue + rt_se_prio(rt_se)))
__clear_bit(rt_se_prio(rt_se), array->bitmap);
dec_rt_tasks(rt_se, rt_rq); /* 運行進(jìn)程計數(shù)減一 */
}
可見更新調(diào)度信息的函數(shù)為update_curr_rt(),在dequeue_rt_entity()中將當(dāng)前實時進(jìn)程從運行隊列中移除,并添加到隊尾。完成工作函數(shù)為dequeue_rt_stack()--->__dequeue_rt_entity(),它調(diào)用include/linux/list.h:list_del_init()--->__list_del()刪除進(jìn)程。然后如果鏈表變?yōu)榭?,則將位圖中對應(yīng)優(yōu)先級的bit位清零。如下:
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
從上面的介紹可以看出,對于實時調(diào)度,Linux的實現(xiàn)比較簡單,仍然采用之前的O(1)調(diào)度策略,把所有的運行進(jìn)程根據(jù)優(yōu)先級放到不用的隊列里面,采用位圖方式進(jìn)行使用記錄。進(jìn)隊列僅僅是刪除原來隊列里面的本進(jìn)程,然后將他掛到隊列尾部;而對于“移除”操作,也僅僅是從隊列里面移除后添加到運行隊列尾部。
四、實時調(diào)度類在實際中的應(yīng)用
4.1工業(yè)控制系統(tǒng):精準(zhǔn)控制的幕后英雄
在工業(yè) 4.0 的浪潮下,工業(yè)控制系統(tǒng)正朝著智能化、自動化的方向飛速發(fā)展。從汽車制造到電子設(shè)備生產(chǎn),自動化生產(chǎn)線已經(jīng)成為現(xiàn)代工業(yè)的核心。在這些復(fù)雜的生產(chǎn)線上,各種設(shè)備協(xié)同工作,每一個動作、每一次數(shù)據(jù)傳輸都需要精確的時間控制。Linux 實時調(diào)度類在其中扮演著至關(guān)重要的角色,它就像是一位精準(zhǔn)的指揮官,確保每一個任務(wù)都能按時執(zhí)行,從而實現(xiàn)整個生產(chǎn)線的高效、穩(wěn)定運行。
以汽車制造為例,自動化生產(chǎn)線涉及眾多復(fù)雜的工序,如車身焊接、零部件裝配、噴漆等。在車身焊接環(huán)節(jié),機(jī)械臂需要按照精確的時間順序和位置坐標(biāo)進(jìn)行焊接操作。如果焊接任務(wù)不能按時完成,可能會導(dǎo)致車身結(jié)構(gòu)不穩(wěn)定,影響汽車的質(zhì)量和安全性。
Linux 實時調(diào)度類通過采用 SCHED_FIFO 或 SCHED_RR 策略,為焊接任務(wù)分配高優(yōu)先級,確保機(jī)械臂能夠及時響應(yīng)控制指令,準(zhǔn)確地完成焊接操作。同時,在生產(chǎn)線的物料運輸環(huán)節(jié),AGV(自動導(dǎo)引車)需要根據(jù)生產(chǎn)進(jìn)度及時將零部件運輸?shù)街付ㄎ恢?。Linux 實時調(diào)度類可以根據(jù) AGV 的任務(wù)優(yōu)先級和實時路況,合理地調(diào)度 AGV 的運行,避免出現(xiàn)交通擁堵和任務(wù)延遲的情況,保證生產(chǎn)線的物料供應(yīng)順暢。
據(jù)相關(guān)數(shù)據(jù)顯示,采用 Linux 實時調(diào)度類的工業(yè)控制系統(tǒng),生產(chǎn)效率能夠提升 20% 以上,產(chǎn)品次品率降低 15% 左右。這充分證明了 Linux 實時調(diào)度類在工業(yè)控制系統(tǒng)中的重要性和價值,它為工業(yè)生產(chǎn)的精準(zhǔn)控制提供了堅實的技術(shù)保障,是工業(yè)自動化不可或缺的關(guān)鍵技術(shù)之一。
4.2多媒體處理:流暢體驗的保障
在當(dāng)今的數(shù)字時代,多媒體應(yīng)用已經(jīng)深入到我們生活的方方面面,從高清視頻播放到音頻實時處理,從視頻會議到游戲娛樂,我們對多媒體體驗的要求越來越高。而 Linux 實時調(diào)度類正是實現(xiàn)流暢多媒體體驗的幕后功臣,它對時間敏感性任務(wù)的精確調(diào)度,確保了多媒體處理的流暢性、穩(wěn)定性和實時性。
以高清視頻播放為例,在播放高清視頻時,視頻解碼和音頻解碼任務(wù)需要在極短的時間內(nèi)完成,以保證視頻和音頻的同步播放,避免出現(xiàn)卡頓和延遲的情況。Linux 實時調(diào)度類可以為視頻解碼和音頻解碼任務(wù)分配較高的優(yōu)先級,并采用合適的調(diào)度策略,如 SCHED_RR 策略,為每個任務(wù)分配固定的時間片,確保它們能夠及時獲取 CPU 資源,快速地完成解碼工作。同時,在視頻渲染和音頻輸出環(huán)節(jié),實時調(diào)度類也能保證任務(wù)的及時執(zhí)行,將解碼后的視頻幀和音頻數(shù)據(jù)快速地輸出到顯示設(shè)備和音頻設(shè)備上,為用戶帶來流暢、清晰的視聽體驗。
在音頻實時處理領(lǐng)域,如語音識別、音頻編輯等應(yīng)用中,Linux 實時調(diào)度類同樣發(fā)揮著重要作用。在語音識別過程中,麥克風(fēng)采集到的語音信號需要及時進(jìn)行處理和分析,以準(zhǔn)確識別用戶的語音指令。Linux 實時調(diào)度類通過對語音處理任務(wù)的優(yōu)先調(diào)度,能夠快速地對語音信號進(jìn)行采樣、濾波、特征提取等操作,提高語音識別的準(zhǔn)確率和實時性。在音頻編輯軟件中,當(dāng)用戶對音頻進(jìn)行剪輯、混音等操作時,實時調(diào)度類可以確保音頻處理任務(wù)的高效執(zhí)行,讓用戶能夠?qū)崟r聽到處理后的音頻效果,提升音頻編輯的效率和體驗。
五、配置與優(yōu)化指南
5.1系統(tǒng)調(diào)用設(shè)置:掌控調(diào)度的 “魔法棒”
在 Linux 系統(tǒng)中,要實現(xiàn)對線程或進(jìn)程調(diào)度策略和優(yōu)先級的精確控制,pthread_setschedparam和sched_setscheduler這兩個系統(tǒng)調(diào)用就像是掌控調(diào)度的 “魔法棒”,發(fā)揮著關(guān)鍵作用。
pthread_setschedparam主要用于設(shè)置線程的調(diào)度參數(shù),其函數(shù)原型如下:
#include <pthread.h>
int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param);
thread參數(shù)表示目標(biāo)線程的標(biāo)識符,它就像是線程的 “身份證”,通過這個標(biāo)識符,系統(tǒng)能夠準(zhǔn)確地定位到需要設(shè)置調(diào)度參數(shù)的線程。policy參數(shù)用于指定調(diào)度策略,可取值包括SCHED_OTHER(普通分時調(diào)度策略)、SCHED_FIFO(先進(jìn)先出實時調(diào)度策略)和SCHED_RR(時間片輪轉(zhuǎn)實時調(diào)度策略)等,不同的取值決定了線程在系統(tǒng)中的調(diào)度方式。param是一個指向struct sched_param結(jié)構(gòu)體的指針,該結(jié)構(gòu)體中包含了線程的優(yōu)先級信息,通過設(shè)置param->sched_priority的值,可以調(diào)整線程的優(yōu)先級。例如:
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
void* thread_function(void* arg) {
// 獲取線程的tid
pid_t tid = gettid();
// 定義調(diào)度策略和優(yōu)先級變量
int policy;
struct sched_param sched_param;
// 獲取當(dāng)前線程的調(diào)度參數(shù)
pthread_getschedparam(pthread_self(), &policy, &sched_param);
// 打印當(dāng)前的調(diào)度策略和優(yōu)先級
printf("Current thread (tid: %d) policy: %d priority: %d\n", tid, policy, sched_param.sched_priority);
// 設(shè)置線程的調(diào)度策略為FIFO
policy = SCHED_FIFO;
// 設(shè)置線程的優(yōu)先級為最大值
sched_param.sched_priority = sched_get_priority_max(policy);
// 設(shè)置線程的調(diào)度策略和優(yōu)先級
pthread_setschedparam(pthread_self(), policy, &sched_param);
// 再次獲取并打印調(diào)度參數(shù)
pthread_getschedparam(pthread_self(), &policy, &sched_param);
printf("New thread (tid: %d) policy: %d priority: %d\n", tid, policy, sched_param.sched_priority);
// 線程工作代碼...
return NULL;
}
int main() {
pthread_t thread;
// 創(chuàng)建線程
pthread_create(&thread, NULL, &thread_function, NULL);
// 等待線程結(jié)束
pthread_join(thread, NULL);
return 0;
}
在上述示例中,首先獲取當(dāng)前線程的調(diào)度參數(shù)并打印,然后將線程的調(diào)度策略設(shè)置為SCHED_FIFO,優(yōu)先級設(shè)置為該策略允許的最大值,最后再次獲取并打印調(diào)度參數(shù),以驗證設(shè)置是否生效。
sched_setscheduler函數(shù)則用于設(shè)置進(jìn)程的調(diào)度策略和優(yōu)先級,其函數(shù)原型為:
#include <sched.h>
int sched_setscheduler(pid_t pid, int policy, const struct sched_param *param);
pid參數(shù)表示要設(shè)置調(diào)度策略的進(jìn)程的進(jìn)程 ID,如果pid為 0,則表示當(dāng)前進(jìn)程。policy和param的含義與pthread_setschedparam中的類似。例如,將當(dāng)前進(jìn)程的調(diào)度策略設(shè)置為SCHED_RR,并設(shè)置優(yōu)先級為 50,可以這樣實現(xiàn):
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
struct sched_param param;
param.sched_priority = 50;
int ret = sched_setscheduler(0, SCHED_RR, ?m);
if (ret == -1) {
perror("Failed to set scheduler");
exit(EXIT_FAILURE);
}
printf("Successfully set scheduler to SCHED_RR with priority 50\n");
return 0;
}
通過這兩個系統(tǒng)調(diào)用,開發(fā)者可以根據(jù)實際需求靈活地設(shè)置線程或進(jìn)程的調(diào)度策略和優(yōu)先級,從而優(yōu)化系統(tǒng)的性能和實時性。在實際應(yīng)用中,需要根據(jù)任務(wù)的特點和系統(tǒng)的要求,謹(jǐn)慎選擇調(diào)度策略和優(yōu)先級,以確保系統(tǒng)的穩(wěn)定運行和高效執(zhí)行。
5.2性能優(yōu)化建議:提升效率的 “秘籍”
在 Linux 實時調(diào)度中,為了充分發(fā)揮系統(tǒng)的性能,提升實時任務(wù)的執(zhí)行效率,我們可以從以下幾個方面入手進(jìn)行優(yōu)化。
首先,根據(jù)任務(wù)特點選擇調(diào)度策略是關(guān)鍵的一步。對于那些對時間要求極為嚴(yán)格,需要在最短時間內(nèi)完成的任務(wù),如工業(yè)控制系統(tǒng)中的緊急控制任務(wù)、航空航天中的關(guān)鍵飛行控制指令處理等,應(yīng)優(yōu)先考慮使用SCHED_FIFO策略。因為該策略能夠確保任務(wù)一旦獲得 CPU 資源,就可以一直運行,直到完成或者被更高優(yōu)先級的任務(wù)搶占,從而保證了任務(wù)執(zhí)行的及時性和連續(xù)性。
而對于那些需要公平分配 CPU 時間,且對響應(yīng)時間有一定要求的任務(wù),如多媒體播放中的音頻和視頻同步處理、交互式應(yīng)用程序中的用戶輸入響應(yīng)等,SCHED_RR策略則更為合適。它通過時間片輪轉(zhuǎn)的方式,讓每個任務(wù)都能在一定的時間內(nèi)獲得 CPU 資源,避免了某個任務(wù)長時間獨占 CPU,導(dǎo)致其他任務(wù)無法及時執(zhí)行的情況,兼顧了公平性和實時性。
合理分配優(yōu)先級也是優(yōu)化實時調(diào)度性能的重要手段。在一個復(fù)雜的實時系統(tǒng)中,通常會有多個不同類型的任務(wù)同時運行,這些任務(wù)對時間的敏感度和重要性各不相同。因此,我們需要根據(jù)任務(wù)的實際需求,為它們分配合理的優(yōu)先級。高優(yōu)先級應(yīng)分配給那些對系統(tǒng)正常運行至關(guān)重要,且時間要求緊迫的任務(wù),例如在醫(yī)療設(shè)備控制系統(tǒng)中,生命體征監(jiān)測和緊急治療控制任務(wù)就需要設(shè)置較高的優(yōu)先級,以確保能夠及時響應(yīng)患者的生命體征變化,保障患者的生命安全。
而對于一些相對次要的任務(wù),如系統(tǒng)日志記錄、數(shù)據(jù)備份等,可以分配較低的優(yōu)先級,讓它們在系統(tǒng)資源空閑時執(zhí)行,避免影響關(guān)鍵任務(wù)的執(zhí)行。同時,要注意避免出現(xiàn)優(yōu)先級反轉(zhuǎn)的情況,即低優(yōu)先級任務(wù)持有高優(yōu)先級任務(wù)所需的資源,導(dǎo)致高優(yōu)先級任務(wù)無法執(zhí)行??梢酝ㄟ^采用優(yōu)先級繼承、優(yōu)先級天花板等算法來解決這個問題。
優(yōu)化系統(tǒng)資源配置也不容忽視。一方面,要合理分配 CPU 資源。在多處理器系統(tǒng)中,可以根據(jù)任務(wù)的特點和 CPU 的負(fù)載情況,將任務(wù)綁定到特定的 CPU 核心上執(zhí)行,以提高 CPU 緩存的命中率,減少任務(wù)在不同 CPU 核心之間切換帶來的開銷。例如,對于一些計算密集型的實時任務(wù),可以將它們固定分配到性能較強(qiáng)的 CPU 核心上,以充分發(fā)揮 CPU 的計算能力。
另一方面,要關(guān)注內(nèi)存資源的管理。實時任務(wù)通常對內(nèi)存的訪問速度和穩(wěn)定性有較高的要求,因此可以通過優(yōu)化內(nèi)存分配算法、減少內(nèi)存碎片等方式,提高內(nèi)存的使用效率和性能。此外,還可以采用內(nèi)存鎖定技術(shù),將關(guān)鍵的實時任務(wù)所需的內(nèi)存頁面鎖定在物理內(nèi)存中,避免它們被交換到磁盤上,從而提高任務(wù)的執(zhí)行速度和實時性。在一些對響應(yīng)時間要求極高的金融交易系統(tǒng)中,就可以采用內(nèi)存鎖定技術(shù),確保交易處理任務(wù)能夠快速、穩(wěn)定地執(zhí)行。