從CPU資源大戰(zhàn),看懂內(nèi)核搶占機(jī)制
當(dāng)你同時(shí)打開瀏覽器刷視頻、IDE 編譯代碼、后臺(tái)還在同步云文檔時(shí),有沒有突然遇到過(guò)鼠標(biāo)卡頓半秒的情況?這不是硬件故障,而是一場(chǎng)無(wú)聲的 CPU 資源爭(zhēng)奪戰(zhàn) —— 低優(yōu)先級(jí)任務(wù)正霸占著處理器,高優(yōu)先級(jí)任務(wù)卻在 “排隊(duì)等待”。在 Linux 系統(tǒng)中,每秒要處理成百上千個(gè)進(jìn)程 / 線程,CPU 就像唯一的檢票口,如何避免 “慢任務(wù)堵路”、讓緊急任務(wù)快速通過(guò)?
為了解決 CPU 資源分配這一關(guān)鍵問(wèn)題,操作系統(tǒng)引入了內(nèi)核搶占機(jī)制。內(nèi)核搶占機(jī)制就像是一位高效的交通警察,能夠根據(jù)各個(gè)程序的 “緊急程度” 和 “重要性”,動(dòng)態(tài)地分配 CPU 資源,確保系統(tǒng)能夠高效、穩(wěn)定地運(yùn)行。接下來(lái),就讓我們深入探索內(nèi)核搶占機(jī)制,看看它是如何在這場(chǎng) CPU 資源的爭(zhēng)奪戰(zhàn)中發(fā)揮關(guān)鍵作用的。
一、內(nèi)核搶占機(jī)制是什么?
1.1內(nèi)核搶占概述
內(nèi)核搶占,簡(jiǎn)單來(lái)說(shuō),就是在系統(tǒng)內(nèi)核運(yùn)行的過(guò)程中,允許高優(yōu)先級(jí)的任務(wù)中斷當(dāng)前正在執(zhí)行的低優(yōu)先級(jí)任務(wù),從而獲取 CPU 資源并得以執(zhí)行 。這就好比一場(chǎng)正在進(jìn)行的接力比賽,原本低優(yōu)先級(jí)任務(wù)是正在奔跑的運(yùn)動(dòng)員,但當(dāng)高優(yōu)先級(jí)任務(wù)這個(gè) “更厲害的運(yùn)動(dòng)員” 出現(xiàn)時(shí),就可以強(qiáng)行接過(guò)接力棒,繼續(xù)向前沖刺。
在操作系統(tǒng)中,任務(wù)(也可以稱為進(jìn)程或線程)有著不同的優(yōu)先級(jí)劃分。比如,系統(tǒng)中負(fù)責(zé)處理硬件中斷的任務(wù),像硬盤數(shù)據(jù)讀取完成后的中斷處理任務(wù),往往需要極高的優(yōu)先級(jí),因?yàn)樗皶r(shí)處理硬件傳來(lái)的數(shù)據(jù),確保數(shù)據(jù)的完整性和系統(tǒng)的穩(wěn)定運(yùn)行。而一些后臺(tái)運(yùn)行的任務(wù),比如自動(dòng)備份文件的任務(wù),雖然也重要,但在即時(shí)性上要求就沒有那么高,優(yōu)先級(jí)相對(duì)較低。當(dāng)一個(gè)低優(yōu)先級(jí)的任務(wù)正在內(nèi)核態(tài)運(yùn)行時(shí),如果此時(shí)有一個(gè)高優(yōu)先級(jí)的任務(wù)被觸發(fā),內(nèi)核搶占機(jī)制就會(huì)發(fā)揮作用,暫停低優(yōu)先級(jí)任務(wù),將 CPU 資源分配給高優(yōu)先級(jí)任務(wù)。這樣做的目的是為了確保系統(tǒng)能夠?qū)o急事件做出快速響應(yīng),提高整個(gè)系統(tǒng)的性能和穩(wěn)定性 。
1.2內(nèi)核搶占與用戶搶占的區(qū)別
內(nèi)核搶占和用戶搶占雖然都是為了實(shí)現(xiàn) CPU 資源的合理分配,但它們?cè)谟|發(fā)時(shí)機(jī)、條件和實(shí)現(xiàn)方式上有著明顯的區(qū)別。從觸發(fā)時(shí)機(jī)來(lái)看,用戶搶占通常發(fā)生在從系統(tǒng)調(diào)用返回用戶空間,或者從中斷(異常)處理程序返回用戶空間的時(shí)候。當(dāng)內(nèi)核即將把執(zhí)行權(quán)交回給用戶空間時(shí),會(huì)檢查一個(gè)名為need_resched的標(biāo)志位。如果這個(gè)標(biāo)志位被設(shè)置了,就意味著有其他更需要 CPU 資源的任務(wù)在等待,于是內(nèi)核會(huì)調(diào)用調(diào)度程序schedule(),進(jìn)行任務(wù)的重新調(diào)度,從而發(fā)生用戶搶占 。打個(gè)比方,就好像一個(gè)學(xué)生在課間休息(用戶空間)時(shí),老師(內(nèi)核)檢查到有更緊急的任務(wù)(need_resched標(biāo)志位被設(shè)置)需要安排給某個(gè)同學(xué),于是就會(huì)重新調(diào)整同學(xué)們的任務(wù)安排(用戶搶占)。
而內(nèi)核搶占的觸發(fā)時(shí)機(jī)則更加多樣化。當(dāng)中斷處理程序返回內(nèi)核空間時(shí),如果此時(shí)滿足搶占條件,就可能發(fā)生內(nèi)核搶占;當(dāng)內(nèi)核代碼執(zhí)行完畢,再次變得可搶占時(shí),也可能觸發(fā)內(nèi)核搶占;另外,如果內(nèi)核中的任務(wù)顯式地調(diào)用了schedule()函數(shù),或者任務(wù)因?yàn)槟承┰蜃枞耍瑯訒?huì)引發(fā)內(nèi)核搶占 。這就如同在一場(chǎng)課堂討論(內(nèi)核空間運(yùn)行)中,老師(內(nèi)核)隨時(shí)可能因?yàn)橛懈匾氖虑椋ǜ邇?yōu)先級(jí)任務(wù)出現(xiàn)等情況),而打斷當(dāng)前同學(xué)(低優(yōu)先級(jí)任務(wù))的發(fā)言,讓更合適的同學(xué)(高優(yōu)先級(jí)任務(wù))發(fā)言。
在觸發(fā)條件方面,用戶搶占主要依賴于need_resched標(biāo)志位,只要這個(gè)標(biāo)志位被設(shè)置,并且在合適的返回用戶空間的時(shí)機(jī),就可能發(fā)生用戶搶占。而內(nèi)核搶占則依賴于一個(gè)名為preempt_count的計(jì)數(shù)器 。preempt_count用于表示當(dāng)前內(nèi)核上下文是否允許被搶占。每次調(diào)用preempt_disable()函數(shù)(或者持有鎖等操作)會(huì)使preempt_count增加;調(diào)用preempt_enable()函數(shù)時(shí)則會(huì)使其減少。當(dāng)preempt_count等于 0 時(shí),表示沒有阻止搶占的條件,內(nèi)核允許被搶占;否則,搶占就會(huì)被延遲 。這就好像一個(gè)倉(cāng)庫(kù),preempt_count就像是倉(cāng)庫(kù)的 “占用計(jì)數(shù)器”,當(dāng)計(jì)數(shù)器為 0 時(shí),說(shuō)明倉(cāng)庫(kù)可以被新的物品(高優(yōu)先級(jí)任務(wù))占用,也就是可以發(fā)生內(nèi)核搶占;當(dāng)計(jì)數(shù)器不為 0 時(shí),說(shuō)明倉(cāng)庫(kù)被占用(有阻止搶占的條件),就不能發(fā)生內(nèi)核搶占。
從實(shí)現(xiàn)方式上看,用戶搶占相對(duì)簡(jiǎn)單,主要通過(guò)檢查need_resched標(biāo)志位并在合適時(shí)機(jī)調(diào)用schedule()函數(shù)來(lái)實(shí)現(xiàn)。而內(nèi)核搶占的實(shí)現(xiàn)則較為復(fù)雜,它需要在內(nèi)核的關(guān)鍵位置,如中斷返回處、preempt_enable()函數(shù)調(diào)用處等,檢查preempt_count和need_resched等條件,以決定是否進(jìn)行搶占 。這就好比管理一個(gè)圖書館,用戶搶占就像是在特定的時(shí)間點(diǎn)(如閉館前)檢查是否有讀者需要特殊安排(need_resched標(biāo)志位),然后進(jìn)行相應(yīng)調(diào)整;而內(nèi)核搶占則像是在圖書館的各個(gè)關(guān)鍵區(qū)域(如入口、借閱處等)都設(shè)置了檢查點(diǎn),隨時(shí)檢查是否可以讓更重要的讀者(高優(yōu)先級(jí)任務(wù))優(yōu)先使用資源(發(fā)生內(nèi)核搶占)。
內(nèi)核搶占在系統(tǒng)資源分配中有著獨(dú)特的作用。它能夠更及時(shí)地響應(yīng)高優(yōu)先級(jí)任務(wù),尤其是在處理一些對(duì)時(shí)間要求苛刻的任務(wù)時(shí),如實(shí)時(shí)數(shù)據(jù)處理、緊急的硬件中斷處理等,內(nèi)核搶占可以確保這些任務(wù)能夠迅速得到 CPU 資源,從而保證系統(tǒng)的實(shí)時(shí)性和穩(wěn)定性 。而用戶搶占更多地是從用戶空間任務(wù)的整體調(diào)度角度出發(fā),優(yōu)化用戶空間任務(wù)的執(zhí)行效率。
1.3為什么需要內(nèi)核搶占
當(dāng)一個(gè)以優(yōu)先級(jí)為主的調(diào)度器中,當(dāng)一個(gè)新的進(jìn)程(下圖中的task2)進(jìn)入到可執(zhí)行(running)的狀態(tài),核心的調(diào)度器會(huì)檢查它的優(yōu)先級(jí),若該進(jìn)程的優(yōu)先權(quán)比目前正在執(zhí)行的進(jìn)程(下圖中的task1)還高,核心調(diào)度器便會(huì)觸發(fā)搶占(preempt),使得正在執(zhí)行的進(jìn)程被打斷,而擁有更高優(yōu)先級(jí)的進(jìn)程會(huì)開始執(zhí)行。
圖片
在不支持內(nèi)核搶占模型中,搶占點(diǎn)比較少,對(duì)于內(nèi)核搶占,如右圖會(huì)在系統(tǒng)中添加很多搶占點(diǎn),同時(shí)會(huì)導(dǎo)致執(zhí)行時(shí)間會(huì)比左圖多一點(diǎn),可搶占會(huì)導(dǎo)致每隔一定時(shí)間去檢查是否需要搶占,這樣也會(huì)影響cache,pipeline,這樣就會(huì)犧牲吞吐量。從上面圖可以看出,操作系統(tǒng)演進(jìn)過(guò)程中,不是新的就一定比舊的好,需要考量場(chǎng)景選擇合適的方案。從這張圖我們可以看出,內(nèi)核搶占主要解決以下問(wèn)題:
- 提高系統(tǒng)響應(yīng)實(shí)時(shí)性和用戶體驗(yàn):在不支持內(nèi)核搶占式內(nèi)核中,低優(yōu)先級(jí)任務(wù)可能會(huì)長(zhǎng)時(shí)間占用CPU,導(dǎo)致高優(yōu)先級(jí)任務(wù)無(wú)法及時(shí)得到處理,主要解決的是latency問(wèn)題。這種情況會(huì)顯著影響系統(tǒng)的響應(yīng)速度,特別是在實(shí)時(shí)應(yīng)用中,可能導(dǎo)致嚴(yán)重的性能問(wèn)題。對(duì)于手機(jī)場(chǎng)景中,當(dāng)用戶在使用應(yīng)用程序時(shí),內(nèi)核搶占可以確保用戶界面關(guān)鍵線程得到足夠的CPU時(shí)間,避免界面卡頓和延遲。
- 避免優(yōu)先級(jí)翻轉(zhuǎn):內(nèi)核搶占結(jié)合優(yōu)先級(jí)繼承(Priority Inheritance)等機(jī)制,可以有效緩解優(yōu)先級(jí)翻轉(zhuǎn)問(wèn)題。當(dāng)?shù)蛢?yōu)先級(jí)任務(wù)持有高優(yōu)先級(jí)任務(wù)需要的資源時(shí),內(nèi)核搶占機(jī)制可以提高低優(yōu)先級(jí)任務(wù)的優(yōu)先級(jí),使其盡快釋放資源,從而減少高優(yōu)先級(jí)任務(wù)的等待時(shí)間。在Linux中,從2.6開始,rtmutex支持優(yōu)先級(jí)繼承,解決優(yōu)先級(jí)翻轉(zhuǎn)的問(wèn)題。
所以需要內(nèi)核搶占的根本原因就是系統(tǒng)在吞吐量和及時(shí)響應(yīng)之間進(jìn)行權(quán)衡的結(jié)果,對(duì)于Linux作為一個(gè)通用的操作系統(tǒng),其最初設(shè)計(jì)就是為了throughput而非確定性時(shí)延而設(shè)計(jì)。但是越來(lái)越多的場(chǎng)景對(duì)及時(shí)響應(yīng)的要求越來(lái)越高,讓更高優(yōu)先級(jí)的任務(wù)和關(guān)鍵的任務(wù)及時(shí)得到調(diào)度,特別對(duì)于我們手機(jī)這種交互式的場(chǎng)景中。
二、內(nèi)核搶占機(jī)制的工作原理
2.1搶占標(biāo)志位 TIF_NEED_RESCHED
TIF_NEED_RESCHED 是內(nèi)核中一個(gè)非常關(guān)鍵的標(biāo)志位,它位于線程的thread_info結(jié)構(gòu)體的flags字段中 。這個(gè)標(biāo)志位就像是一個(gè)信號(hào)燈,用于向內(nèi)核表明 “有更緊急的任務(wù)需要調(diào)度,該進(jìn)行任務(wù)切換啦” 。當(dāng)系統(tǒng)中發(fā)生一些特定事件時(shí),TIF_NEED_RESCHED 標(biāo)志位就會(huì)被設(shè)置。比如,當(dāng)一個(gè)正在運(yùn)行的任務(wù)時(shí)間片耗盡時(shí),就像一個(gè)運(yùn)動(dòng)員的比賽時(shí)間到了,內(nèi)核會(huì)設(shè)置這個(gè)標(biāo)志位,提醒系統(tǒng)需要重新分配 CPU 資源,讓其他任務(wù)有機(jī)會(huì)執(zhí)行 。又比如,當(dāng)一個(gè)高優(yōu)先級(jí)的任務(wù)被喚醒時(shí),好比一個(gè)更重要的客人突然到訪,內(nèi)核也會(huì)設(shè)置該標(biāo)志位,以便盡快讓高優(yōu)先級(jí)任務(wù)獲取 CPU 資源,優(yōu)先執(zhí)行 。
設(shè)置 TIF_NEED_RESCHED 標(biāo)志位后,并不會(huì)立即觸發(fā)任務(wù)調(diào)度,而是在內(nèi)核執(zhí)行到一些特定的 “安全點(diǎn)” 時(shí),才會(huì)檢查這個(gè)標(biāo)志位 。這些安全點(diǎn)就像是道路上的檢查關(guān)卡,內(nèi)核會(huì)在這些地方停下來(lái)檢查是否有緊急任務(wù)需要處理。例如,當(dāng)中斷處理程序返回內(nèi)核空間時(shí),或者內(nèi)核代碼執(zhí)行完畢再次變得可搶占時(shí),內(nèi)核就會(huì)檢查 TIF_NEED_RESCHED 標(biāo)志位 。如果標(biāo)志位被設(shè)置了,并且此時(shí)內(nèi)核上下文允許被搶占(這就涉及到后面要講的搶占計(jì)數(shù)preempt_count ),那么內(nèi)核就會(huì)調(diào)用調(diào)度程序schedule(),進(jìn)行任務(wù)的重新調(diào)度 。這就好比在關(guān)卡處,警察檢查到有緊急任務(wù)(TIF_NEED_RESCHED 標(biāo)志位被設(shè)置),并且當(dāng)前道路條件允許(內(nèi)核上下文允許被搶占),就會(huì)重新安排車輛(任務(wù))的通行順序 。
2.2搶占計(jì)數(shù) preempt_count
preempt_count 是一個(gè)用于記錄內(nèi)核上下文是否允許被搶占的計(jì)數(shù)器 ,它的實(shí)現(xiàn)與體系結(jié)構(gòu)相關(guān) 。在 x86 架構(gòu)中,它被實(shí)現(xiàn)為一個(gè)percpu變量;在 ARM 架構(gòu)中,它是當(dāng)前進(jìn)程描述符thread_info中的一個(gè)變量 。preempt_count 就像是一個(gè) “門鎖計(jì)數(shù)器”,用來(lái)控制內(nèi)核是否可以被搶占 。
每次調(diào)用preempt_disable()函數(shù)時(shí),就相當(dāng)于給門鎖上加了一把鎖,preempt_count 會(huì)增加 。這通常發(fā)生在內(nèi)核執(zhí)行一些不希望被打斷的關(guān)鍵代碼時(shí),比如訪問(wèn)共享數(shù)據(jù)結(jié)構(gòu)時(shí),為了防止其他任務(wù)在這個(gè)過(guò)程中搶占 CPU,導(dǎo)致數(shù)據(jù)不一致,就會(huì)調(diào)用preempt_disable()函數(shù) 。相反,每次調(diào)用preempt_enable()函數(shù)時(shí),就像是打開了一把鎖,preempt_count 會(huì)減少 。當(dāng) preempt_count 的值等于 0 時(shí),意味著所有的鎖都被打開了,即沒有阻止搶占的條件,內(nèi)核允許被搶占 。
preempt_count 與 TIF_NEED_RESCHED 標(biāo)志位緊密配合,共同實(shí)現(xiàn)內(nèi)核搶占 。當(dāng) TIF_NEED_RESCHED 標(biāo)志位被設(shè)置,表示有更緊急的任務(wù)需要調(diào)度 。但只有當(dāng) preempt_count 等于 0,即內(nèi)核上下文允許被搶占時(shí),才會(huì)真正觸發(fā)內(nèi)核搶占 。例如,當(dāng)一個(gè)中斷處理程序返回內(nèi)核空間時(shí),如果 TIF_NEED_RESCHED 標(biāo)志位被設(shè)置,并且 preempt_count 為 0,那么內(nèi)核就會(huì)立即進(jìn)行任務(wù)調(diào)度,將 CPU 資源分配給更緊急的任務(wù) 。這就好比一個(gè)緊急通知(TIF_NEED_RESCHED 標(biāo)志位)來(lái)了,但只有當(dāng)房間的門(preempt_count 為 0,允許進(jìn)入)打開時(shí),緊急任務(wù)才能進(jìn)入房間(獲取 CPU 資源) 。
2.3內(nèi)核搶占的時(shí)機(jī)
內(nèi)核搶占會(huì)在多種情況下發(fā)生,下面我們?cè)敿?xì)分析幾種常見的情況,并配合示意圖進(jìn)行說(shuō)明 。
(1)中斷返回內(nèi)核空間時(shí):當(dāng)中斷發(fā)生時(shí),內(nèi)核會(huì)暫停當(dāng)前任務(wù)的執(zhí)行,去處理中斷 。當(dāng)中斷處理程序執(zhí)行完畢返回內(nèi)核空間時(shí),如果此時(shí) TIF_NEED_RESCHED 標(biāo)志位被設(shè)置,并且 preempt_count 為 0,就會(huì)發(fā)生內(nèi)核搶占 。假設(shè)現(xiàn)在有一個(gè)低優(yōu)先級(jí)任務(wù) A 正在內(nèi)核態(tài)運(yùn)行,突然發(fā)生了一個(gè)硬件中斷,比如硬盤數(shù)據(jù)讀取完成的中斷 。內(nèi)核會(huì)立即保存任務(wù) A 的現(xiàn)場(chǎng)(包括寄存器的值等),然后跳轉(zhuǎn)到中斷處理程序執(zhí)行 。在中斷處理程序中,可能會(huì)喚醒一個(gè)高優(yōu)先級(jí)任務(wù) B 。當(dāng)中斷處理程序執(zhí)行完畢返回內(nèi)核空間時(shí),內(nèi)核檢查到 TIF_NEED_RESCHED 標(biāo)志位被設(shè)置(因?yàn)楦邇?yōu)先級(jí)任務(wù) B 被喚醒),并且 preempt_count 為 0(沒有阻止搶占的條件),那么內(nèi)核就會(huì)搶占任務(wù) A 的執(zhí)行,將 CPU 資源分配給高優(yōu)先級(jí)任務(wù) B 。
內(nèi)核中斷處理和搶占機(jī)制的代碼示例:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <stdbool.h>
// 模擬任務(wù)結(jié)構(gòu)
typedef struct {
const char* name;
int priority; // 優(yōu)先級(jí):數(shù)值越大優(yōu)先級(jí)越高
volatile bool running; // 任務(wù)是否運(yùn)行中
pthread_t tid; // 線程ID
} Task;
// 模擬內(nèi)核狀態(tài)
volatile int preempt_count = 0; // 搶占計(jì)數(shù)
volatile bool tif_need_resched = false; // 重調(diào)度標(biāo)志
volatile Task* current_task = NULL; // 當(dāng)前運(yùn)行任務(wù)
// 任務(wù)列表
Task taskA = {"TaskA", 1, false, 0}; // 低優(yōu)先級(jí)任務(wù)
Task taskB = {"TaskB", 3, false, 0}; // 高優(yōu)先級(jí)任務(wù)
// 模擬內(nèi)核調(diào)度器:選擇最高優(yōu)先級(jí)的就緒任務(wù)
Task* scheduler() {
if (taskB.running) return &taskB;
if (taskA.running) return &taskA;
return NULL;
}
// 模擬上下文切換
void context_switch(Task* prev, Task* next) {
if (prev == next) return;
printf("\n[調(diào)度器] 發(fā)生上下文切換: %s -> %s\n",
prev ? prev->name : "None", next->name);
current_task = next;
}
// 模擬中斷處理程序
void interrupt_handler(int signum) {
printf("\n[中斷處理] 收到硬盤數(shù)據(jù)讀取完成中斷!\n");
preempt_count++; // 進(jìn)入中斷,禁止搶占
// 在中斷處理中喚醒高優(yōu)先級(jí)任務(wù)B
if (!taskB.running) {
printf("[中斷處理] 喚醒高優(yōu)先級(jí)任務(wù)B\n");
taskB.running = true;
tif_need_resched = true; // 設(shè)置重調(diào)度標(biāo)志
}
preempt_count--; // 離開中斷,恢復(fù)搶占計(jì)數(shù)
}
// 低優(yōu)先級(jí)任務(wù)A的執(zhí)行函數(shù)
void* taskA_func(void* arg) {
taskA.running = true;
current_task = &taskA;
printf("[%s] 開始運(yùn)行 (內(nèi)核態(tài))\n", taskA.name);
// 模擬長(zhǎng)時(shí)間運(yùn)行的內(nèi)核操作
for (int i = 0; i < 5; i++) {
printf("[%s] 正在執(zhí)行內(nèi)核操作... (%d/5)\n", taskA.name, i+1);
sleep(1); // 模擬耗時(shí)操作
}
taskA.running = false;
printf("[%s] 完成內(nèi)核操作\n", taskA.name);
return NULL;
}
// 高優(yōu)先級(jí)任務(wù)B的執(zhí)行函數(shù)
void* taskB_func(void* arg) {
// 等待被喚醒
while (!taskB.running) {
usleep(100);
}
current_task = &taskB;
printf("[%s] 開始運(yùn)行 (內(nèi)核態(tài))\n", taskB.name);
// 模擬任務(wù)B的內(nèi)核操作
for (int i = 0; i < 3; i++) {
printf("[%s] 正在執(zhí)行內(nèi)核操作... (%d/3)\n", taskB.name, i+1);
sleep(1);
}
taskB.running = false;
printf("[%s] 完成內(nèi)核操作\n", taskB.name);
return NULL;
}
// 模擬內(nèi)核搶占檢查(在中斷返回時(shí)調(diào)用)
void check_preemption() {
if (tif_need_resched && preempt_count == 0) {
printf("\n[搶占檢查] 滿足搶占條件,觸發(fā)調(diào)度!\n");
Task* next_task = scheduler();
if (next_task && next_task != current_task) {
context_switch(current_task, next_task);
}
tif_need_resched = false; // 清除重調(diào)度標(biāo)志
}
}
// 模擬中斷返回過(guò)程
void interrupt_return() {
printf("[中斷返回] 從中斷處理程序返回內(nèi)核空間\n");
check_preemption(); // 返回時(shí)檢查是否需要搶占
}
int main() {
// 注冊(cè)信號(hào)處理函數(shù)模擬中斷
signal(SIGUSR1, interrupt_handler);
// 創(chuàng)建任務(wù)線程
pthread_create(&taskA.tid, NULL, taskA_func, NULL);
pthread_create(&taskB.tid, NULL, taskB_func, NULL);
// 等待任務(wù)A運(yùn)行一段時(shí)間后發(fā)送中斷
sleep(2);
printf("\n[主程序] 觸發(fā)硬件中斷\n");
pthread_kill(taskA.tid, SIGUSR1); // 向任務(wù)A發(fā)送信號(hào)模擬中斷
// 模擬中斷返回過(guò)程(實(shí)際內(nèi)核中由硬件自動(dòng)完成)
interrupt_return();
// 等待所有任務(wù)完成
pthread_join(taskA.tid, NULL);
pthread_join(taskB.tid, NULL);
return 0;
}程序運(yùn)行后會(huì)出現(xiàn)以下過(guò)程:
- 低優(yōu)先級(jí)任務(wù) A 開始運(yùn)行
- 2 秒后觸發(fā)硬件中斷
- 中斷處理程序喚醒高優(yōu)先級(jí)任務(wù) B
- 中斷返回時(shí)檢查到搶占條件,觸發(fā)任務(wù)切換
- 高優(yōu)先級(jí)任務(wù) B 搶占 CPU 并執(zhí)行
- 任務(wù) B 完成后,任務(wù) A 繼續(xù)執(zhí)行剩余操作
這個(gè)示例模擬了內(nèi)核中 "中斷觸發(fā)→喚醒高優(yōu)先級(jí)任務(wù)→中斷返回時(shí)搶占" 的完整過(guò)程,幫助理解內(nèi)核搶占機(jī)制的基本原理。實(shí)際內(nèi)核中的實(shí)現(xiàn)會(huì)更復(fù)雜,涉及真實(shí)的上下文切換、寄存器保存等操作。
(2)顯式調(diào)用 preempt_enable () 函數(shù)時(shí):當(dāng)內(nèi)核代碼執(zhí)行完一些關(guān)鍵的不可搶占部分,調(diào)用preempt_enable()函數(shù)使 preempt_count 減 1 。如果此時(shí) preempt_count 變?yōu)?0,并且 TIF_NEED_RESCHED 標(biāo)志位被設(shè)置,就會(huì)觸發(fā)內(nèi)核搶占 。例如,內(nèi)核在訪問(wèn)共享數(shù)據(jù)結(jié)構(gòu)時(shí),先調(diào)用preempt_disable()函數(shù)禁止搶占,以保證數(shù)據(jù)訪問(wèn)的一致性 。當(dāng)數(shù)據(jù)訪問(wèn)完成后,調(diào)用preempt_enable()函數(shù) 。如果在這期間有高優(yōu)先級(jí)任務(wù)被喚醒,導(dǎo)致 TIF_NEED_RESCHED 標(biāo)志位被設(shè)置,那么當(dāng)preempt_enable()函數(shù)執(zhí)行后,preempt_count 變?yōu)?0,就會(huì)發(fā)生內(nèi)核搶占 。內(nèi)核中preempt_enable()觸發(fā)搶占機(jī)制的代碼示例:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>
// 任務(wù)結(jié)構(gòu)定義
typedef struct {
const char* name;
int priority; // 優(yōu)先級(jí)(數(shù)值越大優(yōu)先級(jí)越高)
volatile bool running; // 任務(wù)運(yùn)行狀態(tài)
pthread_t tid; // 線程ID
} Task;
// 內(nèi)核狀態(tài)模擬
volatile int preempt_count = 0; // 搶占計(jì)數(shù)器
volatile bool tif_need_resched = false; // 重調(diào)度標(biāo)志位
volatile Task* current_task = NULL; // 當(dāng)前運(yùn)行任務(wù)
volatile bool shared_data_busy = false; // 共享數(shù)據(jù)鎖定標(biāo)志
// 定義兩個(gè)任務(wù):低優(yōu)先級(jí)A和高優(yōu)先級(jí)B
Task taskA = {"TaskA", 1, false, 0};
Task taskB = {"TaskB", 3, false, 0};
// 模擬內(nèi)核調(diào)度器:選擇最高優(yōu)先級(jí)的就緒任務(wù)
Task* scheduler() {
if (taskB.running) return &taskB;
if (taskA.running) return &taskA;
return NULL;
}
// 模擬上下文切換
void context_switch(Task* prev, Task* next) {
if (prev == next) return;
printf("\n[調(diào)度器] 發(fā)生上下文切換: %s -> %s\n",
prev ? prev->name : "None", next->name);
current_task = next;
}
// 模擬搶占啟用函數(shù)
void preempt_enable() {
preempt_count--;
printf("[preempt_enable] 搶占計(jì)數(shù)變?yōu)? %d\n", preempt_count);
// 檢查是否滿足搶占條件
if (preempt_count == 0 && tif_need_resched) {
printf("[preempt_enable] 滿足搶占條件,觸發(fā)調(diào)度!\n");
Task* next_task = scheduler();
if (next_task && next_task != current_task) {
context_switch(current_task, next_task);
}
tif_need_resched = false; // 清除重調(diào)度標(biāo)志
}
}
// 模擬搶占禁用函數(shù)
void preempt_disable() {
preempt_count++;
printf("[preempt_disable] 搶占計(jì)數(shù)變?yōu)? %d\n", preempt_count);
}
// 模擬訪問(wèn)共享數(shù)據(jù)結(jié)構(gòu)的函數(shù)
void access_shared_data() {
printf("[%s] 準(zhǔn)備訪問(wèn)共享數(shù)據(jù),禁止搶占\n", current_task->name);
preempt_disable(); // 進(jìn)入不可搶占區(qū)域
shared_data_busy = true;
// 模擬共享數(shù)據(jù)操作
printf("[%s] 正在操作共享數(shù)據(jù)...\n", current_task->name);
sleep(2); // 模擬耗時(shí)操作
shared_data_busy = false;
printf("[%s] 共享數(shù)據(jù)操作完成,允許搶占\n", current_task->name);
preempt_enable(); // 離開不可搶占區(qū)域,可能觸發(fā)搶占
}
// 高優(yōu)先級(jí)任務(wù)B的執(zhí)行函數(shù)
void* taskB_func(void* arg) {
while (1) {
if (!taskB.running) {
usleep(100); // 等待被喚醒
continue;
}
current_task = &taskB;
printf("\n[%s] 開始執(zhí)行 (高優(yōu)先級(jí)任務(wù))\n", taskB.name);
// 執(zhí)行任務(wù)B的工作
for (int i = 0; i < 3; i++) {
printf("[%s] 執(zhí)行核心工作... (%d/3)\n", taskB.name, i+1);
sleep(1);
}
taskB.running = false;
printf("[%s] 任務(wù)執(zhí)行完畢\n", taskB.name);
break;
}
return NULL;
}
// 低優(yōu)先級(jí)任務(wù)A的執(zhí)行函數(shù)
void* taskA_func(void* arg) {
taskA.running = true;
current_task = &taskA;
printf("[%s] 開始執(zhí)行 (低優(yōu)先級(jí)任務(wù))\n", taskA.name);
// 執(zhí)行一些前期工作
printf("[%s] 執(zhí)行前期準(zhǔn)備工作...\n", taskA.name);
sleep(1);
// 訪問(wèn)共享數(shù)據(jù)(不可搶占區(qū)域)
access_shared_data();
// 繼續(xù)執(zhí)行剩余工作
printf("[%s] 繼續(xù)執(zhí)行剩余工作...\n", taskA.name);
sleep(2);
taskA.running = false;
printf("[%s] 任務(wù)執(zhí)行完畢\n", taskA.name);
return NULL;
}
// 模擬喚醒高優(yōu)先級(jí)任務(wù)的函數(shù)(可在中斷或其他任務(wù)中調(diào)用)
void wake_high_priority_task() {
if (!taskB.running) {
printf("\n[系統(tǒng)] 喚醒高優(yōu)先級(jí)任務(wù)B\n");
taskB.running = true;
tif_need_resched = true; // 設(shè)置重調(diào)度標(biāo)志
printf("[系統(tǒng)] 設(shè)置TIF_NEED_RESCHED標(biāo)志\n");
}
}
int main() {
// 創(chuàng)建任務(wù)線程
pthread_create(&taskA.tid, NULL, taskA_func, NULL);
pthread_create(&taskB.tid, NULL, taskB_func, NULL);
// 等待任務(wù)A進(jìn)入不可搶占區(qū)域后喚醒任務(wù)B
sleep(2); // 確保taskA已進(jìn)入共享數(shù)據(jù)操作
wake_high_priority_task();
// 等待所有任務(wù)完成
pthread_join(taskA.tid, NULL);
pthread_join(taskB.tid, NULL);
return 0;
}(3)內(nèi)核代碼執(zhí)行完畢,再次變得可搶占時(shí):有些內(nèi)核代碼在執(zhí)行過(guò)程中會(huì)暫時(shí)禁止搶占,當(dāng)這些代碼執(zhí)行完畢,再次變得可搶占時(shí),如果滿足 TIF_NEED_RESCHED 標(biāo)志位被設(shè)置且 preempt_count 為 0 的條件,也會(huì)發(fā)生內(nèi)核搶占 。比如,在一些內(nèi)核模塊的初始化函數(shù)中,可能會(huì)禁止搶占,以確保初始化過(guò)程的順利進(jìn)行 。當(dāng)初始化完成后,內(nèi)核恢復(fù)可搶占狀態(tài),如果此時(shí)有高優(yōu)先級(jí)任務(wù)等待調(diào)度,就會(huì)觸發(fā)內(nèi)核搶占 。
內(nèi)核代碼在禁止搶占后恢復(fù)可搶占狀態(tài)時(shí)觸發(fā)內(nèi)核搶占的示例:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdbool.h>
// 任務(wù)結(jié)構(gòu)定義
typedef struct {
const char* name;
int priority; // 優(yōu)先級(jí):數(shù)值越大優(yōu)先級(jí)越高
volatile bool running; // 任務(wù)運(yùn)行狀態(tài)
pthread_t tid; // 線程ID
} Task;
// 內(nèi)核狀態(tài)模擬
volatile int preempt_count = 0; // 搶占計(jì)數(shù)器
volatile bool tif_need_resched = false; // 重調(diào)度標(biāo)志位
volatile Task* current_task = NULL; // 當(dāng)前運(yùn)行任務(wù)
// 定義任務(wù):低優(yōu)先級(jí)初始化任務(wù)和高優(yōu)先級(jí)等待任務(wù)
Task init_task = {"InitTask", 1, false, 0}; // 模擬內(nèi)核模塊初始化任務(wù)
Task high_prio_task = {"HighPrioTask", 5, false, 0}; // 高優(yōu)先級(jí)任務(wù)
// 調(diào)度器:選擇最高優(yōu)先級(jí)的就緒任務(wù)
Task* scheduler() {
if (high_prio_task.running) return &high_prio_task;
if (init_task.running) return &init_task;
return NULL;
}
// 模擬上下文切換
void context_switch(Task* prev, Task* next) {
if (prev == next) return;
printf("\n[調(diào)度器] 上下文切換: %s -> %s\n",
prev ? prev->name : "None", next->name);
current_task = next;
}
// 禁止搶占(preempt_disable模擬)
void preempt_disable() {
preempt_count++;
printf("[內(nèi)核] 禁止搶占,preempt_count = %d\n", preempt_count);
}
// 允許搶占(preempt_enable模擬)
void preempt_enable() {
preempt_count--;
printf("[內(nèi)核] 允許搶占,preempt_count = %d\n", preempt_count);
// 檢查搶占條件:計(jì)數(shù)為0且需要重調(diào)度
if (preempt_count == 0 && tif_need_resched) {
printf("[內(nèi)核] 滿足搶占條件,觸發(fā)調(diào)度\n");
Task* next = scheduler();
if (next && next != current_task) {
context_switch(current_task, next);
}
tif_need_resched = false; // 清除重調(diào)度標(biāo)志
}
}
// 模擬內(nèi)核模塊初始化函數(shù)(需要禁止搶占的關(guān)鍵代碼)
void kernel_module_init() {
printf("\n[InitTask] 開始內(nèi)核模塊初始化(禁止搶占以保證原子性)\n");
preempt_disable(); // 進(jìn)入不可搶占區(qū)域
// 模擬初始化過(guò)程(耗時(shí)操作)
for (int i = 0; i < 3; i++) {
printf("[InitTask] 執(zhí)行初始化步驟 %d/3\n", i+1);
sleep(1);
}
printf("[InitTask] 初始化完成,恢復(fù)搶占狀態(tài)\n");
preempt_enable(); // 離開不可搶占區(qū)域,可能觸發(fā)搶占
}
// 高優(yōu)先級(jí)任務(wù)執(zhí)行函數(shù)
void* high_prio_task_func(void* arg) {
while (1) {
if (!high_prio_task.running) {
usleep(100); // 等待被喚醒
continue;
}
current_task = &high_prio_task;
printf("\n[%s] 獲得CPU,開始執(zhí)行高優(yōu)先級(jí)任務(wù)\n", high_prio_task.name);
// 執(zhí)行高優(yōu)先級(jí)任務(wù)工作
for (int i = 0; i < 2; i++) {
printf("[%s] 處理緊急任務(wù) %d/2\n", high_prio_task.name, i+1);
sleep(1);
}
high_prio_task.running = false;
printf("[%s] 高優(yōu)先級(jí)任務(wù)執(zhí)行完畢\n", high_prio_task.name);
break;
}
return NULL;
}
// 初始化任務(wù)執(zhí)行函數(shù)
void* init_task_func(void* arg) {
init_task.running = true;
current_task = &init_task;
printf("[%s] 啟動(dòng)內(nèi)核模塊初始化任務(wù)\n", init_task.name);
// 執(zhí)行初始化(包含不可搶占區(qū)域)
kernel_module_init();
// 初始化后的后續(xù)工作
printf("\n[%s] 繼續(xù)執(zhí)行初始化后的收尾工作\n", init_task.name);
sleep(1);
init_task.running = false;
printf("[%s] 所有工作完成\n", init_task.name);
return NULL;
}
// 喚醒高優(yōu)先級(jí)任務(wù)的函數(shù)
void wake_high_prio_task() {
if (!high_prio_task.running) {
printf("\n[系統(tǒng)] 高優(yōu)先級(jí)任務(wù)等待調(diào)度,設(shè)置TIF_NEED_RESCHED\n");
high_prio_task.running = true;
tif_need_resched = true;
}
}
int main() {
// 創(chuàng)建任務(wù)線程
pthread_create(&init_task.tid, NULL, init_task_func, NULL);
pthread_create(&high_prio_task.tid, NULL, high_prio_task_func, NULL);
// 等待初始化任務(wù)進(jìn)入不可搶占區(qū)域后喚醒高優(yōu)先級(jí)任務(wù)
sleep(2); // 確保init_task已進(jìn)入禁止搶占狀態(tài)
wake_high_prio_task();
// 等待所有任務(wù)完成
pthread_join(init_task.tid, NULL);
pthread_join(high_prio_task.tid, NULL);
return 0;
}(4)任務(wù)顯式調(diào)用 schedule () 函數(shù)時(shí):當(dāng)任務(wù)在執(zhí)行過(guò)程中,主動(dòng)調(diào)用schedule()函數(shù)時(shí),無(wú)論 TIF_NEED_RESCHED 標(biāo)志位和 preempt_count 的狀態(tài)如何,都會(huì)觸發(fā)任務(wù)調(diào)度 。這就好比一個(gè)運(yùn)動(dòng)員主動(dòng)放棄比賽,把機(jī)會(huì)讓給其他運(yùn)動(dòng)員 。例如,當(dāng)一個(gè)任務(wù)在等待某個(gè)資源時(shí),它會(huì)調(diào)用schedule()函數(shù),主動(dòng)讓出 CPU 資源,以便其他可運(yùn)行的任務(wù)能夠執(zhí)行 。任務(wù)主動(dòng)調(diào)用schedule()函數(shù)主動(dòng)讓出 CPU 的代碼示例:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>
// 任務(wù)結(jié)構(gòu)定義
typedef struct {
const char* name;
int priority; // 優(yōu)先級(jí):數(shù)值越大優(yōu)先級(jí)越高
volatile bool running; // 任務(wù)運(yùn)行狀態(tài)
pthread_t tid; // 線程ID
} Task;
// 內(nèi)核狀態(tài)模擬
volatile int preempt_count = 0; // 搶占計(jì)數(shù)器
volatile bool tif_need_resched = false; // 重調(diào)度標(biāo)志位
volatile Task* current_task = NULL; // 當(dāng)前運(yùn)行任務(wù)
volatile bool resource_available = false; // 共享資源可用性
// 定義任務(wù):等待資源的任務(wù)A和就緒任務(wù)B
Task taskA = {"TaskA", 2, false, 0}; // 會(huì)主動(dòng)調(diào)用schedule()的任務(wù)
Task taskB = {"TaskB", 2, false, 0}; // 可運(yùn)行的其他任務(wù)
// 調(diào)度器:選擇就緒的任務(wù)(此處簡(jiǎn)化為輪詢)
Task* scheduler() {
if (taskB.running) return &taskB;
if (taskA.running) return &taskA;
return NULL;
}
// 模擬上下文切換
void context_switch(Task* prev, Task* next) {
if (prev == next) return;
printf("\n[調(diào)度器] 上下文切換: %s -> %s\n",
prev ? prev->name : "None", next->name);
current_task = next;
}
// 模擬主動(dòng)調(diào)度函數(shù)(無(wú)論狀態(tài)如何都觸發(fā)調(diào)度)
void schedule() {
printf("\n[%s] 主動(dòng)調(diào)用schedule(),讓出CPU\n", current_task->name);
Task* next_task = scheduler();
if (next_task && next_task != current_task) {
context_switch(current_task, next_task);
}
}
// 模擬資源等待函數(shù)(任務(wù)會(huì)在此處主動(dòng)調(diào)度)
void wait_for_resource() {
printf("[%s] 嘗試獲取資源...\n", current_task->name);
while (!resource_available) {
// 資源未就緒,主動(dòng)讓出CPU
schedule();
// 調(diào)度回來(lái)后再次檢查資源
usleep(100000); // 短暫延遲后重試
}
printf("[%s] 成功獲取資源!\n", current_task->name);
}
// 任務(wù)A的執(zhí)行函數(shù)(會(huì)等待資源并主動(dòng)調(diào)度)
void* taskA_func(void* arg) {
taskA.running = true;
current_task = &taskA;
printf("[%s] 開始執(zhí)行,需要等待資源\n", taskA.name);
// 等待資源(會(huì)主動(dòng)調(diào)用schedule())
wait_for_resource();
// 獲得資源后執(zhí)行后續(xù)操作
printf("[%s] 使用資源完成工作...\n", taskA.name);
sleep(1);
taskA.running = false;
printf("[%s] 任務(wù)執(zhí)行完畢\n", taskA.name);
return NULL;
}
// 任務(wù)B的執(zhí)行函數(shù)(可被調(diào)度的任務(wù))
void* taskB_func(void* arg) {
taskB.running = true;
// 等待被調(diào)度
while (current_task != &taskB) {
usleep(100000);
}
printf("\n[%s] 獲得CPU,開始執(zhí)行任務(wù)\n", taskB.name);
// 執(zhí)行任務(wù)B的工作
for (int i = 0; i < 3; i++) {
printf("[%s] 執(zhí)行任務(wù)步驟 %d/3\n", taskB.name, i+1);
sleep(1);
}
// 任務(wù)B完成后釋放資源
resource_available = true;
printf("[%s] 任務(wù)完成,釋放資源\n", taskB.name);
taskB.running = false;
return NULL;
}
int main() {
// 創(chuàng)建任務(wù)線程
pthread_create(&taskA.tid, NULL, taskA_func, NULL);
pthread_create(&taskB.tid, NULL, taskB_func, NULL);
// 等待所有任務(wù)完成
pthread_join(taskA.tid, NULL);
pthread_join(taskB.tid, NULL);
return 0;
}(5)任務(wù)因?yàn)槟承┰蜃枞麜r(shí):當(dāng)任務(wù)因?yàn)榈却硞€(gè)事件(如等待信號(hào)量、等待 I/O 操作完成等)而進(jìn)入阻塞狀態(tài)時(shí),會(huì)觸發(fā)內(nèi)核搶占 。因?yàn)榇藭r(shí)該任務(wù)暫時(shí)無(wú)法繼續(xù)執(zhí)行,內(nèi)核會(huì)將 CPU 資源分配給其他可運(yùn)行的任務(wù) 。比如,一個(gè)任務(wù)在等待從網(wǎng)絡(luò)接收數(shù)據(jù),由于數(shù)據(jù)還未到達(dá),它會(huì)進(jìn)入阻塞狀態(tài) 。這時(shí),內(nèi)核會(huì)檢查是否有其他可運(yùn)行的任務(wù),如果有,就會(huì)將 CPU 資源分配給這些任務(wù),發(fā)生內(nèi)核搶占 。任務(wù)因等待事件進(jìn)入阻塞狀態(tài)而觸發(fā)內(nèi)核搶占的代碼示例:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdbool.h>
#include <semaphore.h>
// 任務(wù)結(jié)構(gòu)定義
typedef struct {
const char* name;
int priority; // 優(yōu)先級(jí):數(shù)值越大優(yōu)先級(jí)越高
volatile bool running; // 任務(wù)運(yùn)行狀態(tài)
volatile bool blocked; // 是否阻塞狀態(tài)
pthread_t tid; // 線程ID
} Task;
// 內(nèi)核狀態(tài)模擬
volatile Task* current_task = NULL; // 當(dāng)前運(yùn)行任務(wù)
sem_t network_data_sem; // 模擬網(wǎng)絡(luò)數(shù)據(jù)到達(dá)的信號(hào)量
volatile bool network_data_ready = false; // 網(wǎng)絡(luò)數(shù)據(jù)是否就緒
// 定義任務(wù):等待網(wǎng)絡(luò)數(shù)據(jù)的任務(wù)A和就緒任務(wù)B
Task taskA = {"TaskA", 2, false, false, 0}; // 會(huì)阻塞等待數(shù)據(jù)的任務(wù)
Task taskB = {"TaskB", 2, false, false, 0}; // 可運(yùn)行的其他任務(wù)
// 調(diào)度器:選擇最高優(yōu)先級(jí)的非阻塞任務(wù)
Task* scheduler() {
// 優(yōu)先選擇非阻塞的任務(wù)
if (taskB.running && !taskB.blocked) return &taskB;
if (taskA.running && !taskA.blocked) return &taskA;
return NULL;
}
// 模擬上下文切換
void context_switch(Task* prev, Task* next) {
if (prev == next) return;
printf("\n[調(diào)度器] 發(fā)生搶占:%s (阻塞) -> %s\n",
prev->name, next->name);
current_task = next;
}
// 模擬等待網(wǎng)絡(luò)數(shù)據(jù)(會(huì)進(jìn)入阻塞狀態(tài))
void wait_for_network_data() {
printf("[%s] 等待網(wǎng)絡(luò)數(shù)據(jù)到達(dá)...\n", current_task->name);
// 進(jìn)入阻塞狀態(tài)
current_task->blocked = true;
printf("[%s] 數(shù)據(jù)未就緒,進(jìn)入阻塞狀態(tài)\n", current_task->name);
// 觸發(fā)調(diào)度(因?yàn)楫?dāng)前任務(wù)已阻塞,必須切換)
Task* next_task = scheduler();
if (next_task) {
context_switch(current_task, next_task);
}
// 被喚醒后繼續(xù)執(zhí)行(數(shù)據(jù)已就緒)
printf("[%s] 收到網(wǎng)絡(luò)數(shù)據(jù),退出阻塞狀態(tài)\n", current_task->name);
current_task->blocked = false;
}
// 模擬網(wǎng)絡(luò)數(shù)據(jù)到達(dá)(喚醒阻塞任務(wù))
void network_data_arrived() {
printf("\n[系統(tǒng)] 網(wǎng)絡(luò)數(shù)據(jù)到達(dá)!喚醒等待任務(wù)\n");
network_data_ready = true;
// 將任務(wù)A標(biāo)記為非阻塞
taskA.blocked = false;
}
// 任務(wù)A的執(zhí)行函數(shù)(等待網(wǎng)絡(luò)數(shù)據(jù))
void* taskA_func(void* arg) {
taskA.running = true;
current_task = &taskA;
printf("[%s] 開始執(zhí)行,需要接收網(wǎng)絡(luò)數(shù)據(jù)\n", taskA.name);
// 等待網(wǎng)絡(luò)數(shù)據(jù)(會(huì)進(jìn)入阻塞)
wait_for_network_data();
// 處理收到的數(shù)據(jù)
printf("[%s] 開始處理網(wǎng)絡(luò)數(shù)據(jù)...\n", taskA.name);
sleep(1);
taskA.running = false;
printf("[%s] 任務(wù)執(zhí)行完畢\n", taskA.name);
return NULL;
}
// 任務(wù)B的執(zhí)行函數(shù)(可被調(diào)度的任務(wù))
void* taskB_func(void* arg) {
taskB.running = true;
// 等待被調(diào)度
while (current_task != &taskB) {
usleep(100000);
}
printf("[%s] 獲得CPU,開始執(zhí)行任務(wù)\n", taskB.name);
// 執(zhí)行任務(wù)B的工作
for (int i = 0; i < 3; i++) {
printf("[%s] 執(zhí)行處理步驟 %d/3\n", taskB.name, i+1);
sleep(1);
}
// 任務(wù)B完成后,模擬網(wǎng)絡(luò)數(shù)據(jù)到達(dá)
network_data_arrived();
// 觸發(fā)調(diào)度,讓任務(wù)A繼續(xù)執(zhí)行
Task* next_task = scheduler();
if (next_task) {
context_switch(current_task, next_task);
}
taskB.running = false;
printf("[%s] 任務(wù)執(zhí)行完畢\n", taskB.name);
return NULL;
}
int main() {
// 初始化信號(hào)量(模擬網(wǎng)絡(luò)數(shù)據(jù)同步)
sem_init(&network_data_sem, 0, 0);
// 創(chuàng)建任務(wù)線程
pthread_create(&taskA.tid, NULL, taskA_func, NULL);
pthread_create(&taskB.tid, NULL, taskB_func, NULL);
// 等待所有任務(wù)完成
pthread_join(taskA.tid, NULL);
pthread_join(taskB.tid, NULL);
sem_destroy(&network_data_sem);
return 0;
}通過(guò)以上對(duì)內(nèi)核搶占時(shí)機(jī)的分析,我們可以看到內(nèi)核搶占機(jī)制能夠在各種情況下,根據(jù)任務(wù)的優(yōu)先級(jí)和系統(tǒng)的狀態(tài),靈活地進(jìn)行 CPU 資源的分配,確保系統(tǒng)的高效運(yùn)行 。
三、內(nèi)核搶占機(jī)制的實(shí)現(xiàn)方式
3.1自旋鎖與內(nèi)核搶占
自旋鎖是一種用于多線程同步的鎖機(jī)制,在 Linux 內(nèi)核中被廣泛應(yīng)用,主要用于保護(hù)臨界區(qū)免受并發(fā)訪問(wèn)的干擾 。自旋鎖的核心原理基于一個(gè)原子操作的標(biāo)志變量,該變量通常用 0 表示鎖可用,1 表示鎖已被占用 。當(dāng)一個(gè)線程嘗試獲取鎖時(shí),會(huì)通過(guò)原子操作檢查鎖的狀態(tài) 。如果鎖可用(標(biāo)志變量為 0),則線程將標(biāo)志變量設(shè)置為 1,成功獲取鎖并進(jìn)入臨界區(qū);如果鎖已被占用(標(biāo)志變量為 1),線程不會(huì)進(jìn)入睡眠狀態(tài),而是在一個(gè)循環(huán)中不斷檢查鎖的狀態(tài),即 “自旋”,直到鎖變?yōu)榭捎?。這種機(jī)制在多處理器系統(tǒng)中非常有效,因?yàn)樗苊饬司€程進(jìn)入睡眠和被喚醒的開銷,減少了上下文切換 。例如,在多核處理器中,當(dāng)一個(gè) CPU 上的線程持有自旋鎖時(shí),其他 CPU 上的線程如果想要訪問(wèn)相同的臨界區(qū),只需在原地自旋等待,而無(wú)需進(jìn)行復(fù)雜的上下文切換操作 。
自旋鎖在防止并發(fā)訪問(wèn)臨界區(qū)方面起著關(guān)鍵作用 。在多線程環(huán)境中,臨界區(qū)是指同一時(shí)間內(nèi)不允許有超過(guò)一個(gè)線程進(jìn)入執(zhí)行的代碼區(qū)域,例如對(duì)共享數(shù)據(jù)結(jié)構(gòu)的訪問(wèn)代碼 。自旋鎖通過(guò)保證在任何時(shí)刻只有一個(gè)線程能夠持有鎖,從而確保了同一時(shí)間只有一個(gè)線程可以進(jìn)入臨界區(qū),避免了多個(gè)線程同時(shí)訪問(wèn)臨界區(qū)導(dǎo)致的數(shù)據(jù)競(jìng)爭(zhēng)和不一致問(wèn)題 。比如,在多個(gè)線程同時(shí)訪問(wèn)共享鏈表時(shí),如果沒有自旋鎖保護(hù),可能會(huì)出現(xiàn)一個(gè)線程正在插入節(jié)點(diǎn),而另一個(gè)線程同時(shí)刪除節(jié)點(diǎn)的情況,這會(huì)導(dǎo)致鏈表結(jié)構(gòu)被破壞 。有了自旋鎖,當(dāng)一個(gè)線程獲取到鎖并進(jìn)入臨界區(qū)操作鏈表時(shí),其他線程只能自旋等待,直到該線程完成操作并釋放鎖 。
然而,自旋鎖的使用對(duì)內(nèi)核搶占有著重要影響 。在持有自旋鎖期間,禁止內(nèi)核搶占 。這是因?yàn)樽孕i的設(shè)計(jì)目的是為了在短時(shí)間內(nèi)保護(hù)臨界區(qū),假設(shè)在持有自旋鎖時(shí)允許內(nèi)核搶占,當(dāng)一個(gè)低優(yōu)先級(jí)任務(wù)持有自旋鎖進(jìn)入臨界區(qū),此時(shí)如果高優(yōu)先級(jí)任務(wù)搶占了 CPU,而高優(yōu)先級(jí)任務(wù)又試圖獲取同一個(gè)自旋鎖,就會(huì)發(fā)生死鎖 。因?yàn)榈蛢?yōu)先級(jí)任務(wù)被搶占后無(wú)法繼續(xù)執(zhí)行以釋放自旋鎖,高優(yōu)先級(jí)任務(wù)則會(huì)一直自旋等待該鎖,導(dǎo)致兩個(gè)任務(wù)都無(wú)法繼續(xù)執(zhí)行 。例如,在一個(gè)多核系統(tǒng)中,任務(wù) A 在 CPU1 上持有自旋鎖訪問(wèn)共享資源,此時(shí)任務(wù) B(高優(yōu)先級(jí))在 CPU2 上被喚醒并試圖獲取相同的自旋鎖,如果允許內(nèi)核搶占,任務(wù) B 搶占了 CPU1,而任務(wù) A 因?yàn)楸粨屨紵o(wú)法釋放自旋鎖,任務(wù) B 就會(huì)陷入自旋等待,從而造成死鎖 。
在單核系統(tǒng)和多核系統(tǒng)中,自旋鎖與內(nèi)核搶占的關(guān)系有所不同 。在單核系統(tǒng)中,自旋鎖的實(shí)現(xiàn)實(shí)際上是通過(guò)關(guān)閉內(nèi)核搶占來(lái)防止其他進(jìn)程進(jìn)入臨界區(qū) 。因?yàn)樵趩魏讼到y(tǒng)中,同一時(shí)間只有一個(gè)進(jìn)程能夠運(yùn)行,不存在多個(gè) CPU 同時(shí)訪問(wèn)臨界區(qū)的問(wèn)題,但如果系統(tǒng)開啟了搶占,一個(gè)進(jìn)程進(jìn)入臨界區(qū)后可能會(huì)被其他進(jìn)程搶占,導(dǎo)致新進(jìn)程再次進(jìn)入臨界區(qū),從而破壞數(shù)據(jù)結(jié)構(gòu) 。所以,在單核系統(tǒng)中,自旋鎖通過(guò)關(guān)閉搶占來(lái)保證臨界區(qū)的獨(dú)占訪問(wèn) 。例如,當(dāng)一個(gè)進(jìn)程在單核系統(tǒng)中獲取自旋鎖進(jìn)入臨界區(qū)時(shí),其他進(jìn)程無(wú)法搶占 CPU,也就無(wú)法進(jìn)入該臨界區(qū) 。而在多核系統(tǒng)中,自旋鎖不僅要防止同一 CPU 上的并發(fā)訪問(wèn),還要防止不同 CPU 上的并發(fā)訪問(wèn) 。自旋鎖通過(guò)讓其他 CPU 上的線程自旋等待,確保了在任何時(shí)刻只有一個(gè) CPU 上的線程能夠進(jìn)入臨界區(qū),同時(shí)配合禁止內(nèi)核搶占,避免了因搶占導(dǎo)致的死鎖問(wèn)題 。
3.2調(diào)度器與內(nèi)核搶占
調(diào)度器在整個(gè)內(nèi)核搶占機(jī)制中扮演著核心角色,它是操作系統(tǒng)中負(fù)責(zé)管理工作負(fù)載、決定哪些任務(wù)(進(jìn)程或線程)在什么時(shí)候獲得 CPU 資源的關(guān)鍵組件 。調(diào)度器的基本職責(zé)是根據(jù)一定的調(diào)度策略和算法,從就緒隊(duì)列中選擇一個(gè)合適的任務(wù),將 CPU 的控制權(quán)分配給它,使任務(wù)能夠在 CPU 上運(yùn)行 。在 Linux 系統(tǒng)中,調(diào)度器的設(shè)計(jì)和實(shí)現(xiàn)對(duì)系統(tǒng)的性能有著決定性的影響,它直接關(guān)系到系統(tǒng)資源的合理利用、用戶體驗(yàn)以及系統(tǒng)的響應(yīng)速度 。
調(diào)度器主要根據(jù)任務(wù)優(yōu)先級(jí)和搶占條件來(lái)實(shí)現(xiàn)任務(wù)的調(diào)度和切換 。在 Linux 內(nèi)核中,任務(wù)被分為不同的優(yōu)先級(jí),實(shí)時(shí)進(jìn)程的優(yōu)先級(jí)通常高于普通進(jìn)程 。調(diào)度器會(huì)優(yōu)先調(diào)度高優(yōu)先級(jí)的任務(wù),以確保對(duì)時(shí)間要求苛刻的任務(wù)能夠及時(shí)得到處理 。例如,對(duì)于實(shí)時(shí)音頻播放任務(wù),為了保證音頻播放的流暢性,調(diào)度器會(huì)給予其較高的優(yōu)先級(jí),使其能夠在 CPU 資源競(jìng)爭(zhēng)中優(yōu)先獲得執(zhí)行機(jī)會(huì) 。當(dāng)有高優(yōu)先級(jí)任務(wù)進(jìn)入就緒狀態(tài)時(shí),如果當(dāng)前運(yùn)行的是低優(yōu)先級(jí)任務(wù),且滿足搶占條件,調(diào)度器就會(huì)執(zhí)行任務(wù)切換,將 CPU 資源分配給高優(yōu)先級(jí)任務(wù) 。
調(diào)度器實(shí)現(xiàn)任務(wù)調(diào)度和切換的過(guò)程涉及多個(gè)關(guān)鍵步驟 。當(dāng)一個(gè)任務(wù)的時(shí)間片耗盡、被阻塞(如等待 I/O 操作完成、等待信號(hào)量等)或者有更高優(yōu)先級(jí)的任務(wù)被喚醒時(shí),調(diào)度器會(huì)被觸發(fā) 。調(diào)度器首先會(huì)檢查是否有更高優(yōu)先級(jí)的任務(wù)在就緒隊(duì)列中等待 。如果有,調(diào)度器會(huì)根據(jù)調(diào)度算法選擇優(yōu)先級(jí)最高的任務(wù) 。對(duì)于實(shí)時(shí)調(diào)度器,它會(huì)從最高優(yōu)先級(jí)的隊(duì)列開始選擇任務(wù);對(duì)于普通調(diào)度器(如完全公平調(diào)度器 CFS),它會(huì)根據(jù)任務(wù)的虛擬運(yùn)行時(shí)間等因素來(lái)選擇下一個(gè)要運(yùn)行的任務(wù) 。在選擇好任務(wù)后,調(diào)度器會(huì)執(zhí)行上下文切換操作 。
上下文切換包括保存當(dāng)前任務(wù)的上下文(如 CPU 寄存器的值、堆棧指針等),然后加載新任務(wù)的上下文,使新任務(wù)能夠在 CPU 上繼續(xù)執(zhí)行 。例如,當(dāng)一個(gè)任務(wù)因?yàn)榈却?I/O 操作而被阻塞時(shí),調(diào)度器會(huì)將其上下文保存起來(lái),然后從就緒隊(duì)列中選擇一個(gè)可運(yùn)行的任務(wù),加載該任務(wù)的上下文,讓其在 CPU 上運(yùn)行 。在上下文切換過(guò)程中,調(diào)度器還會(huì)處理一些與任務(wù)調(diào)度相關(guān)的操作,如更新任務(wù)的運(yùn)行狀態(tài)、調(diào)整任務(wù)的優(yōu)先級(jí)等 。
調(diào)度器與內(nèi)核搶占密切配合,共同實(shí)現(xiàn)高效的 CPU 資源分配 。當(dāng)內(nèi)核搶占發(fā)生時(shí),調(diào)度器會(huì)根據(jù)搶占條件和任務(wù)優(yōu)先級(jí),及時(shí)進(jìn)行任務(wù)的調(diào)度和切換 。例如,當(dāng)中斷處理程序返回內(nèi)核空間時(shí),如果滿足內(nèi)核搶占條件(如 TIF_NEED_RESCHED 標(biāo)志位被設(shè)置且 preempt_count 為 0),調(diào)度器會(huì)立即進(jìn)行任務(wù)調(diào)度,將 CPU 資源分配給更緊急的任務(wù) 。在多核系統(tǒng)中,調(diào)度器還需要負(fù)責(zé)負(fù)載均衡,確保各個(gè) CPU 上的任務(wù)負(fù)載相對(duì)均衡,避免出現(xiàn)某個(gè) CPU 過(guò)于繁忙,而其他 CPU 閑置的情況 。通過(guò)合理的調(diào)度和負(fù)載均衡,調(diào)度器能夠充分利用 CPU 資源,提高系統(tǒng)的整體性能 。
四、案例分析:內(nèi)核搶占機(jī)制的實(shí)際應(yīng)用
在一個(gè)基于 Linux 系統(tǒng)的工業(yè)自動(dòng)化控制系統(tǒng)中,負(fù)責(zé)實(shí)時(shí)數(shù)據(jù)采集和處理的線程被設(shè)置為高優(yōu)先級(jí),它需要及時(shí)采集傳感器數(shù)據(jù)并進(jìn)行處理,以保證生產(chǎn)過(guò)程的精準(zhǔn)控制。然而,在系統(tǒng)運(yùn)行一段時(shí)間后,工程師們發(fā)現(xiàn)這個(gè)高優(yōu)先級(jí)線程有時(shí)會(huì)長(zhǎng)時(shí)間無(wú)法獲取 CPU 資源,導(dǎo)致數(shù)據(jù)采集和處理出現(xiàn)延遲,嚴(yán)重影響了生產(chǎn)的穩(wěn)定性和產(chǎn)品質(zhì)量。經(jīng)過(guò)深入分析,發(fā)現(xiàn)問(wèn)題出在一個(gè)低優(yōu)先級(jí)的線程上。這個(gè)低優(yōu)先級(jí)線程負(fù)責(zé)定期更新系統(tǒng)的配置信息,它在執(zhí)行過(guò)程中需要訪問(wèn)共享的配置文件。為了保證數(shù)據(jù)一致性,該線程在訪問(wèn)配置文件時(shí)使用了自旋鎖。由于配置文件的更新操作較為復(fù)雜,涉及大量的數(shù)據(jù)讀取和解析,導(dǎo)致低優(yōu)先級(jí)線程持有自旋鎖的時(shí)間過(guò)長(zhǎng) 。
根據(jù)內(nèi)核搶占機(jī)制的原理,當(dāng)?shù)蛢?yōu)先級(jí)線程持有自旋鎖時(shí),會(huì)禁止內(nèi)核搶占。這就意味著,即使高優(yōu)先級(jí)的實(shí)時(shí)數(shù)據(jù)采集線程被喚醒,由于低優(yōu)先級(jí)線程一直持有自旋鎖,使得 preempt_count 不為 0,內(nèi)核無(wú)法進(jìn)行搶占,高優(yōu)先級(jí)線程只能等待低優(yōu)先級(jí)線程釋放自旋鎖后才能獲取 CPU 資源 。這就好比一條單行道,低優(yōu)先級(jí)線程就像一輛行駛緩慢且占用道路時(shí)間很長(zhǎng)的大貨車,它一直占據(jù)著道路(持有自旋鎖),導(dǎo)致后面著急趕路的高優(yōu)先級(jí)車輛(高優(yōu)先級(jí)線程)無(wú)法通行,只能無(wú)奈等待 。
為了解決這個(gè)問(wèn)題,工程師們對(duì)低優(yōu)先級(jí)線程的代碼進(jìn)行了優(yōu)化。他們將配置文件的更新操作進(jìn)行了拆分,減少每次持有自旋鎖的時(shí)間 。同時(shí),將一些非關(guān)鍵的數(shù)據(jù)解析操作放到低優(yōu)先級(jí)線程釋放自旋鎖之后執(zhí)行,這樣就大大縮短了低優(yōu)先級(jí)線程持有自旋鎖的時(shí)長(zhǎng) 。經(jīng)過(guò)這樣的優(yōu)化,高優(yōu)先級(jí)的實(shí)時(shí)數(shù)據(jù)采集線程能夠及時(shí)獲取 CPU 資源,系統(tǒng)的數(shù)據(jù)采集和處理恢復(fù)正常,生產(chǎn)過(guò)程也恢復(fù)了穩(wěn)定 。
代碼示例:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdbool.h>
#include <sched.h>
// 系統(tǒng)狀態(tài)模擬
pthread_spinlock_t config_lock; // 保護(hù)配置文件的自旋鎖
volatile bool system_running = true; // 系統(tǒng)運(yùn)行標(biāo)志
// 高優(yōu)先級(jí)實(shí)時(shí)數(shù)據(jù)采集線程函數(shù)
void* realtime_data_thread(void* arg) {
// 設(shè)置線程為高優(yōu)先級(jí)
struct sched_param param = {.sched_priority = 90};
pthread_setschedparam(pthread_self(), SCHED_FIFO, ?m);
int count = 0;
while (system_running) {
// 嘗試獲取CPU執(zhí)行數(shù)據(jù)采集
printf("\n[高優(yōu)先級(jí)線程] 嘗試采集傳感器數(shù)據(jù)...\n");
// 模擬數(shù)據(jù)采集和處理(需要及時(shí)執(zhí)行)
printf("[高優(yōu)先級(jí)線程] 成功采集并處理數(shù)據(jù) #%d\n", ++count);
// 正常情況下10ms采集一次
usleep(10000);
}
return NULL;
}
// 未優(yōu)化的低優(yōu)先級(jí)配置更新線程函數(shù)(問(wèn)題版本)
void* unoptimized_config_thread(void* arg) {
// 設(shè)置線程為低優(yōu)先級(jí)
struct sched_param param = {.sched_priority = 10};
pthread_setschedparam(pthread_self(), SCHED_FIFO, ?m);
int update_count = 0;
while (system_running) {
// 定期更新配置(每2秒一次)
sleep(2);
printf("\n[低優(yōu)先級(jí)線程] 開始更新配置 #%d\n", ++update_count);
// 獲取自旋鎖(會(huì)禁用內(nèi)核搶占)
pthread_spin_lock(&config_lock);
printf("[低優(yōu)先級(jí)線程] 持有自旋鎖,開始處理配置文件...\n");
// 模擬長(zhǎng)時(shí)間持有鎖進(jìn)行復(fù)雜處理(問(wèn)題根源)
// 實(shí)際系統(tǒng)中這可能是大量文件IO和數(shù)據(jù)解析
printf("[低優(yōu)先級(jí)線程] 正在進(jìn)行復(fù)雜的配置解析(長(zhǎng)時(shí)間占用鎖)...\n");
sleep(3); // 長(zhǎng)時(shí)間持有鎖,導(dǎo)致高優(yōu)先級(jí)線程無(wú)法搶占
// 釋放自旋鎖
pthread_spin_unlock(&config_lock);
printf("[低優(yōu)先級(jí)線程] 釋放自旋鎖,配置更新完成\n");
}
return NULL;
}
// 優(yōu)化后的低優(yōu)先級(jí)配置更新線程函數(shù)
void* optimized_config_thread(void* arg) {
// 設(shè)置線程為低優(yōu)先級(jí)
struct sched_param param = {.sched_priority = 10};
pthread_setschedparam(pthread_self(), SCHED_FIFO, ?m);
int update_count = 0;
while (system_running) {
// 定期更新配置(每2秒一次)
sleep(2);
printf("\n[低優(yōu)先級(jí)線程] 開始優(yōu)化版配置更新 #%d\n", ++update_count);
// 準(zhǔn)備工作:在獲取鎖之前進(jìn)行必要的準(zhǔn)備
printf("[低優(yōu)先級(jí)線程] 準(zhǔn)備配置數(shù)據(jù)(無(wú)鎖操作)...\n");
usleep(500000); // 0.5秒準(zhǔn)備時(shí)間(不持有鎖)
// 獲取自旋鎖(僅在必要時(shí)持有)
pthread_spin_lock(&config_lock);
printf("[低優(yōu)先級(jí)線程] 持有自旋鎖,執(zhí)行關(guān)鍵更新...\n");
// 僅在鎖內(nèi)執(zhí)行必要的關(guān)鍵操作(大幅縮短持有時(shí)間)
printf("[低優(yōu)先級(jí)線程] 執(zhí)行核心配置寫入(短時(shí)間占用鎖)...\n");
usleep(500000); // 0.5秒關(guān)鍵操作(大幅縮短)
// 立即釋放自旋鎖
pthread_spin_unlock(&config_lock);
printf("[低優(yōu)先級(jí)線程] 釋放自旋鎖\n");
// 非關(guān)鍵操作放到鎖外執(zhí)行
printf("[低優(yōu)先級(jí)線程] 執(zhí)行后續(xù)解析和處理(無(wú)鎖操作)...\n");
usleep(2000000); // 2秒非關(guān)鍵操作(不影響搶占)
}
return NULL;
}
int main(int argc, char* argv[]) {
// 初始化自旋鎖
pthread_spin_init(&config_lock, PTHREAD_PROCESS_PRIVATE);
pthread_t realtime_tid, config_tid;
// 創(chuàng)建高優(yōu)先級(jí)實(shí)時(shí)線程
pthread_create(&realtime_tid, NULL, realtime_data_thread, NULL);
// 根據(jù)參數(shù)選擇運(yùn)行未優(yōu)化或優(yōu)化版本
if (argc > 1 && argv[1][0] == '1') {
printf("=== 運(yùn)行未優(yōu)化版本(會(huì)出現(xiàn)優(yōu)先級(jí)反轉(zhuǎn)問(wèn)題) ===\n");
pthread_create(&config_tid, NULL, unoptimized_config_thread, NULL);
} else {
printf("=== 運(yùn)行優(yōu)化版本(解決優(yōu)先級(jí)反轉(zhuǎn)問(wèn)題) ===\n");
pthread_create(&config_tid, NULL, optimized_config_thread, NULL);
}
// 運(yùn)行5秒后結(jié)束
sleep(5);
system_running = false;
// 等待線程結(jié)束
pthread_join(realtime_tid, NULL);
pthread_join(config_tid, NULL);
// 銷毀自旋鎖
pthread_spin_destroy(&config_lock);
return 0;
}在系統(tǒng)運(yùn)行過(guò)程中,高優(yōu)先級(jí)的 realtime_data_thread 需要每 10ms 采集一次傳感器數(shù)據(jù)以滿足實(shí)時(shí)控制需求,而低優(yōu)先級(jí)的 unoptimized_config_thread 使用自旋鎖更新配置文件并長(zhǎng)時(shí)間持有該鎖(約3秒)。由于自旋鎖持有期間會(huì)禁用內(nèi)核搶占(preempt_count不為0),導(dǎo)致高優(yōu)先級(jí)線程在此期間無(wú)法搶占執(zhí)行,從而影響實(shí)時(shí)性能。
在優(yōu)化方案中,我們將配置更新操作拆分為三個(gè)關(guān)鍵階段:首先,在無(wú)鎖準(zhǔn)備階段完成所有數(shù)據(jù)預(yù)處理工作,確保高優(yōu)先級(jí)線程不被阻塞;其次,通過(guò)重構(gòu)代碼將必須受保護(hù)的關(guān)鍵操作集中在0.5秒內(nèi)完成,大幅縮短自旋鎖持有時(shí)間;最后,在釋放鎖后執(zhí)行非關(guān)鍵的配置解析等后續(xù)處理。這種設(shè)計(jì)既保證了數(shù)據(jù)一致性,又顯著降低了實(shí)時(shí)線程被阻塞的風(fēng)險(xiǎn)。
在編譯與運(yùn)行階段,用戶可通過(guò)執(zhí)行未優(yōu)化版本程序(使用命令./program 1)來(lái)模擬原始配置更新導(dǎo)致的實(shí)時(shí)線程阻塞問(wèn)題,而通過(guò)運(yùn)行優(yōu)化版本(直接執(zhí)行./program)則可驗(yàn)證拆分三階段操作后縮短鎖持有時(shí)間的解決方案效果。

























