進(jìn)程調(diào)度簡介
在linux中,進(jìn)程是最基本的執(zhí)行單位。進(jìn)程調(diào)度在整個操作系統(tǒng)中屬于核心地位,是操作系統(tǒng)實現(xiàn)多任務(wù)處理的關(guān)鍵操作,確保每個進(jìn)程在有限的cpu資源下有序的完成相應(yīng)操作。

在Linux操作系統(tǒng)中,同一時間下不僅僅只有一個進(jìn)程在執(zhí)行任務(wù)而是多個進(jìn)程同時競爭有限的CPU資源。若沒有進(jìn)程調(diào)度操作,整個系統(tǒng)可能會陷入混亂,例如你正在聽著歌卻突然把歌停了給你播放視頻。因此,進(jìn)程調(diào)度尤為重要。
進(jìn)程調(diào)度的高效性會直接影響到系統(tǒng)的性能。一個高效的進(jìn)程調(diào)度算法能夠迅速完成大量進(jìn)程間的切換,從而確保CPU資源得到最大程度的利用。以輪轉(zhuǎn)調(diào)度(Round Robin, RR)算法為例,該算法將CPU時間分割成多個固定的時間片,每個進(jìn)程依次占用一個時間片進(jìn)行執(zhí)行。這種方式確保了所有進(jìn)程都能獲得一定的執(zhí)行時間,進(jìn)而提升了系統(tǒng)的整體處理能力。據(jù)統(tǒng)計,在合理設(shè)定時間片長度的情況下,輪轉(zhuǎn)調(diào)度算法能顯著提升系統(tǒng)吞吐量,增幅可達(dá)20%至30%。
公平性同樣是進(jìn)程調(diào)度中的一項關(guān)鍵考量。它要求每個進(jìn)程都能獲得適當(dāng)?shù)腃PU時間片,以防止某些進(jìn)程因長時間無法執(zhí)行而陷入饑餓狀態(tài)。例如,在優(yōu)先級調(diào)度算法中,若高優(yōu)先級進(jìn)程持續(xù)不斷地產(chǎn)生,可能會導(dǎo)致低優(yōu)先級進(jìn)程長時間得不到執(zhí)行機(jī)會。為解決這一問題,可以采取老化等技術(shù)手段,逐步提高那些等待時間過長的進(jìn)程的優(yōu)先級。
公平性也是進(jìn)程調(diào)度的重要目標(biāo)之一。每個進(jìn)程都應(yīng)該有機(jī)會獲得合理的 CPU 時間片,避免饑餓現(xiàn)象的發(fā)生。例如,優(yōu)先級調(diào)度算法中,如果源源不斷地產(chǎn)生高優(yōu)先級的進(jìn)程,那么低優(yōu)先級的進(jìn)程可能會長時間得不到執(zhí)行。為了解決這個問題,可以采用老化等技術(shù),逐漸增加等待很長時間的進(jìn)程的優(yōu)先級。
1.2進(jìn)程查看命令 -e 或 –every:顯示系統(tǒng)中所有的進(jìn)程。 -f 或 –full:提供完整的格式輸出,包括進(jìn)程樹狀關(guān)系和環(huán)境變量等額外信息。 -l 或 –long:長格式輸出,包含更多詳細(xì)信息,如F旗表示進(jìn)程正在等待文件鎖。 -u 或 –user:按照用戶來顯示進(jìn)程,并顯示每個進(jìn)程的CPU和內(nèi)存使用情況。 -aux 是一個常見的組合選項,用于顯示系統(tǒng)中所有用戶的全部進(jìn)程,包括后臺進(jìn)程(不與終端關(guān)聯(lián)的進(jìn)程)。 進(jìn)程列表:按照默認(rèn)排序(通常是CPU使用率或優(yōu)先級)列出正在運行的進(jìn)程及其相關(guān)信息,如PID、USER(執(zhí)行進(jìn)程的用戶)、PR(優(yōu)先級)、NI(nice值,影響優(yōu)先級)、VIRT(虛擬內(nèi)存大小)、RES(常駐內(nèi)存大小)、%CPU和%MEM(CPU和內(nèi)存使用百分比)等。 系統(tǒng)總體狀態(tài):包括系統(tǒng)運行時間、登錄用戶數(shù)、系統(tǒng)負(fù)載、CPU和內(nèi)存的整體使用狀況等統(tǒng)計數(shù)據(jù)。 交互式操作:在top運行過程中,用戶可以通過鍵盤輸入相應(yīng)的命令(如按P鍵切換到按CPU使用率排序,按M鍵切換到按內(nèi)存使用率排序,或使用k鍵殺死指定進(jìn)程等)來進(jìn)行進(jìn)一步的進(jìn)程管理和監(jiān)控。
1.3進(jìn)程的幾個要素 有一段程序待其執(zhí)行 有進(jìn)程專用的系統(tǒng)堆棧空間 在內(nèi)核有task_struct結(jié)構(gòu)體 進(jìn)程有獨立的存儲空間,擁有專用的用戶空間
二、進(jìn)程的生命周期2.1進(jìn)程狀態(tài)文字描述
Linux操作系統(tǒng)屬于多任務(wù)操作系統(tǒng),系統(tǒng)中的每個進(jìn)程能夠分時復(fù)用CPU時間片,通過有效的進(jìn)程調(diào)度策略實現(xiàn)多任務(wù)并行執(zhí)行。而進(jìn)程在被CPU調(diào)度運行,等待CPU資源分配以及等待外部事件時會屬于不同的狀態(tài)。
共有五種狀態(tài):
創(chuàng)建狀態(tài):新進(jìn)程剛剛被創(chuàng)建,尚未開始執(zhí)行。 就緒狀態(tài):進(jìn)程已準(zhǔn)備好所有必需資源,等待CPU分配時間片執(zhí)行。 執(zhí)行狀態(tài):進(jìn)程已獲得CPU資源并在其中運行。 阻塞狀態(tài):進(jìn)程因等待某個資源或事件而暫時停止運行,從CPU隊列中移除。 終止?fàn)顟B(tài):進(jìn)程已完成執(zhí)行或被終止,不再存在。
進(jìn)程狀態(tài)程序中的體現(xiàn):
代碼語言:JavaScript代碼運行次數(shù):0運行復(fù)制
#define TASK_RUNNING0x00000000#define TASK_intERRUPTIBLE0x00000001#define TASK_UNINTERRUPTIBLE0x00000002#define __TASK_STOPPED0x00000004#define __TASK_TRACED0x00000008
TASK_RUNNING 表示進(jìn)程處于可運行狀態(tài)。這意味著進(jìn)程已經(jīng)準(zhǔn)備好在CPU上執(zhí)行,并且調(diào)度器可以選擇它來進(jìn)行運行。當(dāng)進(jìn)程獲取到CPU時間片時,它就會進(jìn)入運行狀態(tài)。 TASK_INTERRUPTIBLE 表示進(jìn)程處于可中斷睡眠狀態(tài)。這種狀態(tài)下,進(jìn)程正在等待某個事件發(fā)生(例如I/O操作完成、鎖可用等),并且如果收到信號或者等待的條件滿足,它可以被喚醒并重新加入到可運行隊列中。在可中斷睡眠期間,進(jìn)程可以響應(yīng)信號并改變其狀態(tài)。 TASK_UNINTERRUPTIBLE 表示進(jìn)程處于不可中斷睡眠狀態(tài)。類似可中斷睡眠,進(jìn)程同樣在等待某種資源或事件,但是在此狀態(tài)下,進(jìn)程不會響應(yīng)任何信號,即使接收到信號也不會立即醒來,除非等待的資源變?yōu)榭捎没蛱囟l件達(dá)成。 __TASK_STOPPED 標(biāo)志意味著進(jìn)程已停止執(zhí)行,通常是因為收到了SigsTOP或SIGTSTP這樣的停止信號,或者是調(diào)試器暫停了進(jìn)程。停止的進(jìn)程不會消耗CPU資源,直到收到SIGCONT信號恢復(fù)執(zhí)行。 __TASK_TRACED 表示進(jìn)程正在被調(diào)試器或其他跟蹤工具追蹤,并進(jìn)入了跟蹤停止?fàn)顟B(tài)。在這種狀態(tài)下,進(jìn)程同樣不會執(zhí)行,等待調(diào)試器的進(jìn)一步操作,比如單步執(zhí)行、繼續(xù)執(zhí)行等。
這些狀態(tài)標(biāo)志會被組合在一個進(jìn)程控制塊(PCB,在Linux內(nèi)核中表現(xiàn)為task_struct結(jié)構(gòu)體的一個成員變量state)中,以表示進(jìn)程的當(dāng)前狀態(tài)。調(diào)度器根據(jù)這些狀態(tài)決定何時何地將進(jìn)程投入運行或從運行狀態(tài)移除。在實際的內(nèi)核源碼中,為了準(zhǔn)確反映進(jìn)程狀態(tài),這些宏可能會與其他標(biāo)志位一起使用或組合起來形成更復(fù)雜的狀態(tài)標(biāo)識。
2.2進(jìn)程狀態(tài)的切換
如下圖,便是進(jìn)程進(jìn)行狀態(tài)之間的切換,這些工作都是有調(diào)度器來完成的。

2.3task_struct數(shù)據(jù)結(jié)構(gòu)
進(jìn)程是操作系統(tǒng)調(diào)度的一個實體,需要對進(jìn)程所必須資源做一個抽象化,此抽象化為進(jìn)程控制塊 (PCB,Process Control BLock) ,PCB在Linux內(nèi)核里面采用task_struct結(jié)構(gòu)體來描述進(jìn)程控制塊。Linux內(nèi)核涉及進(jìn)程和程序的所有算法都圍繞名為task_struct的數(shù)據(jù)結(jié)構(gòu)而建立操作。具體Linux內(nèi)核源碼task_struct結(jié)構(gòu)體核心成員如下(task_struct結(jié)構(gòu)體過于龐大,暫時了解幾個重要成員)task_struct定義在includelinuxsched.h:
__state:表示當(dāng)前進(jìn)程狀態(tài),例如可運行、睡眠、僵死等。 stack:指向進(jìn)程的內(nèi)核棧。 usage:引用計數(shù),用于跟蹤進(jìn)程使用情況。 prio、static_prio和normal_prio:描述進(jìn)程的調(diào)度優(yōu)先級和策略。 se、rt和dl:分別對應(yīng)CFS(完全公平調(diào)度器)、實時調(diào)度和Deadline調(diào)度的調(diào)度實體。 mm:指向進(jìn)程的內(nèi)存描述符結(jié)構(gòu)(mm_struct),管理進(jìn)程的虛擬內(nèi)存。 active_mm:在沒有獨立內(nèi)存空間時,指向當(dāng)前活動的內(nèi)存描述符。 exit_state、exit_code和exit_signal:進(jìn)程退出時的狀態(tài)、退出碼和發(fā)送給父進(jìn)程的信號。 pid和tgid:分別代表進(jìn)程ID和線程組ID。 real_parent、parent、children和sibling:用于構(gòu)建進(jìn)程間的父子、兄弟關(guān)系,形成進(jìn)程樹。 files:指向進(jìn)程打開的文件表,即files_struct結(jié)構(gòu)體,記錄所有已打開的文件描述符。 signal和sighand:管理和處理進(jìn)程接收到的信號。 blocked、real_blocked和saved_sigmask:記錄進(jìn)程當(dāng)前屏蔽的信號集合。 nsproxy:命名空間代理,用于管理和切換不同命名空間。 fs:指向文件系統(tǒng)信息結(jié)構(gòu),記錄進(jìn)程的當(dāng)前工作目錄、根目錄等文件系統(tǒng)相關(guān)信息。
其他字段還包括了進(jìn)程的調(diào)度統(tǒng)計信息、時間統(tǒng)計、內(nèi)存頁面錯誤統(tǒng)計、POSIX定時器、安全特性、審計信息等。
2.4進(jìn)程優(yōu)先級⑴優(yōu)先級的代碼表示
描述進(jìn)程的調(diào)度優(yōu)先級和策略,之后的任務(wù)調(diào)度以及時間片分配都要用到優(yōu)先級:
代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
intprio;intstatic_prio;intnormal_prio;unsigned intrt_priority;
int prio: 這個字段代表進(jìn)程的動態(tài)優(yōu)先級,它是根據(jù)進(jìn)程的行為和系統(tǒng)負(fù)載動態(tài)調(diào)整的。在傳統(tǒng)的Linux調(diào)度器(如CFS調(diào)度器)中,這個優(yōu)先級通常被映射到調(diào)度實體(sched_entity)的一個虛擬運行時間(vruntime),而不是一個直觀意義上的數(shù)字大小,較大的vruntime意味著較低的優(yōu)先級。 int static_prio: 靜態(tài)優(yōu)先級,也稱為nice值,在Linux中范圍是-20至19,數(shù)值越小表示優(yōu)先級越高。靜態(tài)優(yōu)先級可以通過nice值或者用戶權(quán)限改變,但不會像動態(tài)優(yōu)先級那樣頻繁變化。 int normal_prio: 此字段在某些Linux調(diào)度器實現(xiàn)中可能用來表示經(jīng)過nice值調(diào)整后的正常優(yōu)先級,它結(jié)合了靜態(tài)優(yōu)先級和可能的額外優(yōu)先級調(diào)整因素。 unsigned int rt_priority: 實時優(yōu)先級,僅適用于實時調(diào)度策略(如SCHED_FIFO或SCHED_RR)。實時進(jìn)程有固定的優(yōu)先級分配,rt_priority值越大,表示進(jìn)程的實時優(yōu)先級越高,搶占其他進(jìn)程的可能性也就越大。實時進(jìn)程一般不受nice值的影響,其優(yōu)先級高于普通進(jìn)程。在實時調(diào)度策略下,rt_priority用于確定進(jìn)程在實時進(jìn)程隊列中的相對位置。 ⑵Linux內(nèi)核下的進(jìn)程分類
在Linux內(nèi)核中,進(jìn)程可以按照其調(diào)度需求和優(yōu)先級的不同分為不同的類別,主要包括:
SCHED_FIFO:實時進(jìn)程中,優(yōu)先級高的進(jìn)程總是優(yōu)先執(zhí)行,一旦開始運行,除非進(jìn)程主動放棄CPU(如阻塞等待I/O或睡眠),否則不會被優(yōu)先級相同或更低的其他進(jìn)程搶占。 SCHED_RR:同樣是實時進(jìn)程,但它在用完時間片后會重新加入隊列等待下一次調(diào)度,這樣可以保證在相同優(yōu)先級的實時進(jìn)程中實現(xiàn)時間片輪轉(zhuǎn)。 普通進(jìn)程(Normal Process):又稱為分時進(jìn)程,這類進(jìn)程在Linux系統(tǒng)中遵循默認(rèn)的分時調(diào)度策略,如CFS(Completely Fair Scheduler)。它們按照各自權(quán)重(nice值)和虛擬運行時間(vruntime)來獲取CPU時間片。nice值可以在[-20, 19]范圍內(nèi)調(diào)整,數(shù)值越小,優(yōu)先級越高,但總體來說,普通進(jìn)程之間是公平共享CPU資源的。 實時進(jìn)程(Real-time Process):實時進(jìn)程在滿足特定條件的情況下需要得到及時響應(yīng),具有更高的優(yōu)先級。Linux內(nèi)核提供兩種實時調(diào)度策略:SCHED_FIFO(先進(jìn)先出)和SCHED_RR(輪轉(zhuǎn)調(diào)度)。 ⑶優(yōu)先級的在不同類型進(jìn)程的分配 限期進(jìn)程的優(yōu)先級是-1; 實時進(jìn)程的優(yōu)先級1-99,優(yōu)先級數(shù)值最大,表示優(yōu)先級越高; 普通進(jìn)程的靜態(tài)優(yōu)先級為: 100-139,優(yōu)先級數(shù)值越小,表示優(yōu)先級越高,可通過修改nice值改變普通進(jìn)程的優(yōu)先級,優(yōu)先級等于120加 上nice值;
2.5進(jìn)程調(diào)度的重要性⑴提升系統(tǒng)性能
進(jìn)程調(diào)度對系統(tǒng)性能的提升起著關(guān)鍵作用。通過合理地分配 CPU 資源,進(jìn)程調(diào)度可以極大地提高 CPU 利用率。例如,在完全公平調(diào)度器(CFS)中,根據(jù)進(jìn)程的虛擬運行時間來分配 CPU 時間,確保每個進(jìn)程都能獲得相對公平的執(zhí)行機(jī)會,從而有效提高 CPU 的利用率。據(jù)統(tǒng)計,采用 CFS 的系統(tǒng)中,CPU 利用率可以提高 15% 至 20%。
系統(tǒng)吞吐量是衡量系統(tǒng)性能的另一個重要指標(biāo)。良好的進(jìn)程調(diào)度算法可以在單位時間內(nèi)完成更多的進(jìn)程,從而提高系統(tǒng)吞吐量。以多級反饋隊列調(diào)度為例,它將就緒隊列分成多個優(yōu)先級不同的隊列,每個隊列采用不同的調(diào)度算法。短作業(yè)可以在高優(yōu)先級隊列中快速得到執(zhí)行,而長作業(yè)則在低優(yōu)先級隊列中逐步執(zhí)行,這樣可以兼顧不同類型進(jìn)程的需求,提高系統(tǒng)的整體吞吐量。實驗表明,使用多級反饋隊列調(diào)度的系統(tǒng),吞吐量可以比傳統(tǒng)的先來先服務(wù)調(diào)度提高 30% 至 40%。
此外,進(jìn)程調(diào)度還可以降低周轉(zhuǎn)時間、等待時間和響應(yīng)時間。周轉(zhuǎn)時間是指進(jìn)程從提交到完成所花費的時間,等待時間是進(jìn)程在就緒隊列中等待的時間,響應(yīng)時間是從進(jìn)程提交到首次獲得 CPU 時間的時間間隔。通過合理的調(diào)度算法,如短作業(yè)優(yōu)先調(diào)度,可以優(yōu)先執(zhí)行短作業(yè),減少這些時間指標(biāo)。研究顯示,在特定的工作負(fù)載下,短作業(yè)優(yōu)先調(diào)度可以將平均周轉(zhuǎn)時間降低 20% 至 30%,響應(yīng)時間降低 15% 至 20%。
⑵確保公平性
公平性是進(jìn)程調(diào)度的核心目標(biāo)之一。為了避免進(jìn)程饑餓現(xiàn)象,各種調(diào)度算法都采取了不同的措施。在優(yōu)先級調(diào)度中,雖然高優(yōu)先級的進(jìn)程會優(yōu)先獲得 CPU 資源,但為了防止低優(yōu)先級進(jìn)程長時間得不到執(zhí)行,可以采用動態(tài)調(diào)整優(yōu)先級的方法。例如,隨著低優(yōu)先級進(jìn)程的等待時間增加,逐漸提高其優(yōu)先級,使其有機(jī)會獲得 CPU 執(zhí)行時間。
同時,一些調(diào)度算法還通過限制高優(yōu)先級進(jìn)程的執(zhí)行時間來確保公平性。例如,在實時調(diào)度中,硬實時任務(wù)雖然要求在嚴(yán)格的時間限制內(nèi)完成,但也不能無限占用 CPU 資源。調(diào)度算法會在保證硬實時任務(wù)按時完成的前提下,合理分配 CPU 時間給其他進(jìn)程,以實現(xiàn)系統(tǒng)的整體公平性。
三、進(jìn)程系統(tǒng)調(diào)用3.1系統(tǒng)調(diào)用函數(shù)
當(dāng)運行應(yīng)用程序的時候,調(diào)用fork()/vfork()/clone()函數(shù)就是系統(tǒng)調(diào)用。系統(tǒng)調(diào)用就是應(yīng)用程序如何進(jìn)入內(nèi)核空間執(zhí)行任務(wù),程序使用系統(tǒng)調(diào)用執(zhí)行一系列的操作: 比如創(chuàng)建進(jìn)程、文件IO等等。
系統(tǒng)調(diào)用框圖(使用Linux版本為6.1的內(nèi)核,不同的內(nèi)核其系統(tǒng)調(diào)用有點差異) 如下所示:

⑴fork系統(tǒng)調(diào)用代碼代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
#ifdef __ARCH_WANT_SYS_FORKSYSCALL_DEFINE0(fork){#ifdef CONFIG_MMUstruct kernel_clone_args args = {.exit_signal = SIGCHLD,}; return kernel_clone(&args);#else/* can not support in nommu mode */return -EINVAL;#endif}
⑵vfork系統(tǒng)調(diào)用代碼代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
#ifdef __ARCH_WANT_SYS_VFORKSYSCALL_DEFINE0(vfork){struct kernel_clone_args args = {.flags= CLONE_VFORK | CLONE_VM,.exit_signal= SIGCHLD,}; return kernel_clone(&args);}#endif
⑶clone系統(tǒng)調(diào)用代碼代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
#ifdef __ARCH_WANT_SYS_CLONE#ifdef CONFIG_CLONE_BACKWARDSSYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp, int __user *, parent_tidptr, unsigned long, tls, int __user *, child_tidptr)#elif defined(CONFIG_CLONE_BACKWARDS2)SYSCALL_DEFINE5(clone, unsigned long, newsp, unsigned long, clone_flags, int __user *, parent_tidptr, int __user *, child_tidptr, unsigned long, tls)#elif defined(CONFIG_CLONE_BACKWARDS3)SYSCALL_DEFINE6(clone, unsigned long, clone_flags, unsigned long, newsp,int, stack_size,int __user *, parent_tidptr,int __user *, child_tidptr,unsigned long, tls)#elseSYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp, int __user *, parent_tidptr, int __user *, child_tidptr, unsigned long, tls)#endif{struct kernel_clone_args args = {.flags= (lower_32_bits(clone_flags) & ~CSIGNAL),.pidfd= parent_tidptr,.child_tid= child_tidptr,.parent_tid= parent_tidptr,.exit_signal= (lower_32_bits(clone_flags) & CSIGNAL),.stack= newsp,.tls= tls,}; return kernel_clone(&args);}#endif
⑷進(jìn)程退出
①、進(jìn)程主動終止: 從main()函數(shù)返回,鏈接程序會自動添加到exit()系統(tǒng)調(diào)用; exit系統(tǒng)調(diào)用在內(nèi)核定義如下kernelexit.c:
代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
SYSCALL_DEFINE1(exit, int, error_code){do_exit((error_code&0xff)<p>②、進(jìn)程被動終止: 進(jìn)程收到一個自己不能處理的信號;進(jìn)程收到 SIGKILL等終止信息。</p>⑸內(nèi)核線程<p> 定義:它是獨立運行在內(nèi)核空間的進(jìn)程,與普通用戶進(jìn)程區(qū)別在于內(nèi)核線程沒有獨立的進(jìn)程地址空間。task_struct數(shù)據(jù)結(jié)構(gòu)里面有一個成員指針mm設(shè)置為NULL,它只能運行在內(nèi)核空間。內(nèi)核創(chuàng)建一個內(nèi)核線程代碼體現(xiàn)如下:</p>代碼語言:javascript<i class="icon-code"></i>代碼運行次數(shù):<!-- -->0<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewbox="0 0 16 16" fill="none"><path d="M6.66666 10.9999L10.6667 7.99992L6.66666 4.99992V10.9999ZM7.99999 1.33325C4.31999 1.33325 1.33333 4.31992 1.33333 7.99992C1.33333 11.6799 4.31999 14.6666 7.99999 14.6666C11.68 14.6666 14.6667 11.6799 14.6667 7.99992C14.6667 4.31992 11.68 1.33325 7.99999 1.33325ZM7.99999 13.3333C5.05999 13.3333 2.66666 10.9399 2.66666 7.99992C2.66666 5.05992 5.05999 2.66659 7.99999 2.66659C10.94 2.66659 13.3333 5.05992 13.3333 7.99992C13.3333 10.9399 10.94 13.3333 7.99999 13.3333Z" fill="currentcolor"></path></svg>運行<svg width="16" height="16" viewbox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M4.5 15.5V3.5H14.5V15.5H4.5ZM12.5 5.5H6.5V13.5H12.5V5.5ZM9.5 2.5H3.5V12.5H1.5V0.5H11.5V2.5H9.5Z" fill="currentcolor"></path></svg>復(fù)制<pre class="prism-token token line-numbers javascript">/* * Create a kernel thread. */pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags){struct kernel_clone_args args = {.flags= ((lower_32_bits(flags) | CLONE_VM | CLONE_UNTRACED) & ~CSIGNAL),.exit_signal= (lower_32_bits(flags) & CSIGNAL),.fn= fn,.fn_arg= arg,.kthread= 1,}; return kernel_clone(&args);}
3.2常見的進(jìn)程調(diào)度算法⑴先來先服務(wù)(FCFS)
先來先服務(wù)調(diào)度算法是一種最簡單的調(diào)度算法,它按照進(jìn)程到達(dá)的先后順序進(jìn)行調(diào)度。當(dāng)一個進(jìn)程進(jìn)入就緒隊列時,它會按照到達(dá)的順序排隊等待 CPU 的分配。這種算法的優(yōu)點是實現(xiàn)簡單,公平性較高,每個進(jìn)程都按照其到達(dá)的順序依次獲得 CPU 時間。然而,它也存在明顯的缺點。對于長進(jìn)程來說,可能會長時間占用 CPU,導(dǎo)致后續(xù)到達(dá)的短進(jìn)程和 I/O 繁忙型作業(yè)等待時間過長。例如,假設(shè)有三個進(jìn)程 P1、P2 和 P3,P1 的執(zhí)行時間為 30 秒,P2 的執(zhí)行時間為 5 秒,P3 的執(zhí)行時間為 20 秒。如果按照先來先服務(wù)的算法調(diào)度,P1 先執(zhí)行,那么 P2 和 P3 就需要等待 30 秒后才能開始執(zhí)行,這大大增加了短進(jìn)程和 I/O 繁忙型作業(yè)的等待時間,降低了系統(tǒng)的整體效率。
⑵短作業(yè)優(yōu)先(SJF)
短作業(yè)優(yōu)先調(diào)度算法優(yōu)先調(diào)度執(zhí)行時間最短的進(jìn)程。這種算法的目的是減少平均等待時間,提高系統(tǒng)的吞吐量。然而,它也存在一些問題。首先,它可能導(dǎo)致長作業(yè)饑餓,因為長作業(yè)可能一直等待短作業(yè)執(zhí)行完畢后才能獲得 CPU 時間。其次,準(zhǔn)確估計作業(yè)的執(zhí)行時間是非常困難的。在實際應(yīng)用中,程序員很難準(zhǔn)確估計作業(yè)的執(zhí)行時間,通常會偏長估計,這可能導(dǎo)致算法的效果不如預(yù)期。例如,如果有一個長作業(yè)需要執(zhí)行 100 秒,而不斷有短作業(yè)到來,那么長作業(yè)可能永遠(yuǎn)也得不到調(diào)度,從而出現(xiàn)饑餓現(xiàn)象。
⑶優(yōu)先級調(diào)度
優(yōu)先級調(diào)度算法根據(jù)進(jìn)程的優(yōu)先級進(jìn)行調(diào)度。每個進(jìn)程都被賦予一個優(yōu)先級,優(yōu)先級高的進(jìn)程優(yōu)先獲得 CPU 時間。這種算法可以根據(jù)進(jìn)程的重要性或緊急程度來分配 CPU 資源,具有一定的靈活性。但是,它也可能導(dǎo)致低優(yōu)先級進(jìn)程饑餓。如果不斷有高優(yōu)先級進(jìn)程到來,低優(yōu)先級進(jìn)程可能長時間得不到執(zhí)行。靜態(tài)優(yōu)先級調(diào)度在進(jìn)程創(chuàng)建時分配優(yōu)先級,并在整個執(zhí)行過程中保持不變;動態(tài)優(yōu)先級調(diào)度則根據(jù)進(jìn)程的行為和狀態(tài)動態(tài)調(diào)整優(yōu)先級。例如,在一些實時系統(tǒng)中,緊急任務(wù)被賦予高優(yōu)先級,以確保它們能夠及時得到處理。但是,如果高優(yōu)先級任務(wù)過多,低優(yōu)先級任務(wù)可能會被長時間忽略。
⑷輪轉(zhuǎn)調(diào)度(RR)
輪轉(zhuǎn)調(diào)度算法將 CPU 時間分成固定大小的時間片,所有進(jìn)程輪流獲得一個時間片的 CPU 使用權(quán)。這種算法具有較好的公平性,每個進(jìn)程都能在一定時間內(nèi)獲得 CPU 時間。然而,時間片的大小對系統(tǒng)性能有很大影響。如果時間片太小,會導(dǎo)致進(jìn)程切換頻繁,增加系統(tǒng)開銷;如果時間片太大,輪轉(zhuǎn)調(diào)度算法就會退化為先來先服務(wù)算法。此外,輪轉(zhuǎn)調(diào)度算法不利于處理緊急作業(yè),因為每個進(jìn)程都需要等待輪到自己才能獲得 CPU 時間。例如,假設(shè)有一個緊急任務(wù)需要立即執(zhí)行,但按照輪轉(zhuǎn)調(diào)度算法,它可能需要等待很長時間才能獲得 CPU 時間。
⑸多級反饋隊列調(diào)度
多級反饋隊列調(diào)度算法結(jié)合了多種調(diào)度策略,根據(jù)進(jìn)程的特性將其分配到不同的隊列中,每個隊列采用不同的調(diào)度算法。這種算法具有較高的靈活性,可以適應(yīng)不同類型的進(jìn)程。例如,高優(yōu)先級的短作業(yè)可以分配到高優(yōu)先級隊列中,采用短作業(yè)優(yōu)先調(diào)度算法;長作業(yè)可以分配到低優(yōu)先級隊列中,采用先來先服務(wù)調(diào)度算法。然而,這種算法相對復(fù)雜,需要維護(hù)多個隊列,增加了系統(tǒng)的開銷和管理難度。
⑹高響應(yīng)比優(yōu)先調(diào)度算法
高響應(yīng)比優(yōu)先調(diào)度算法權(quán)衡了短作業(yè)和長作業(yè),兼顧了等待時間和執(zhí)行時間。響應(yīng)比是等待時間與執(zhí)行時間的比值,響應(yīng)比高的進(jìn)程優(yōu)先獲得 CPU 時間。這種算法既照顧了短作業(yè),又考慮了作業(yè)到達(dá)的先后次序,不會使長作業(yè)長期得不到服務(wù)。但是,計算響應(yīng)比會增加系統(tǒng)開銷,因為每次調(diào)度都需要計算每個進(jìn)程的響應(yīng)比。例如,在一個有多個進(jìn)程等待調(diào)度的系統(tǒng)中,計算響應(yīng)比需要消耗一定的時間和計算資源。
四、進(jìn)程調(diào)度的實現(xiàn)與優(yōu)化4.1調(diào)度算法的選擇
不同的場景和需求對進(jìn)程調(diào)度算法有著不同的要求。在批處理系統(tǒng)中,主要追求高吞吐量和系統(tǒng)資源的充分利用。例如,短作業(yè)優(yōu)先算法可以在一定程度上提高批處理系統(tǒng)的效率,因為它優(yōu)先處理執(zhí)行時間短的作業(yè),從而在單位時間內(nèi)可以完成更多的作業(yè)。據(jù)統(tǒng)計,在一些大型數(shù)據(jù)處理中心采用短作業(yè)優(yōu)先算法,系統(tǒng)吞吐量可以提高 15% 至 20%。
對于交互式系統(tǒng),響應(yīng)時間是關(guān)鍵指標(biāo)。此時,輪轉(zhuǎn)調(diào)度算法可能更為合適,因為它可以確保每個進(jìn)程都能在較短的時間內(nèi)獲得 CPU 時間,從而提高系統(tǒng)的響應(yīng)速度。例如,在圖形用戶界面環(huán)境下,用戶期望每個操作都能得到及時的反饋,輪轉(zhuǎn)調(diào)度算法可以保證各個進(jìn)程輪流執(zhí)行,使得用戶操作不會被長時間阻塞。
而在實時系統(tǒng)中,滿足截止時間是最重要的目標(biāo)。實時系統(tǒng)通常采用搶占式調(diào)度算法,如實時優(yōu)先級調(diào)度,確保緊急任務(wù)能夠在規(guī)定的時間內(nèi)得到處理。例如,在飛機(jī)飛行控制系統(tǒng)中,對響應(yīng)時間的要求極為嚴(yán)格,任何延遲都可能導(dǎo)致嚴(yán)重后果,實時優(yōu)先級調(diào)度算法可以確保關(guān)鍵任務(wù)優(yōu)先執(zhí)行。
4.2調(diào)度器的設(shè)計與實現(xiàn)
調(diào)度器的設(shè)計通常采用模塊化的方式,以便于適應(yīng)不同的系統(tǒng)需求和優(yōu)化目標(biāo)。以 Linux 的 CFS(完全公平調(diào)度器)為例,它通過為每個進(jìn)程安排一個虛擬運行時鐘 vruntime,實現(xiàn)了公平性。當(dāng)一個進(jìn)程得以執(zhí)行時,vruntime 的值不斷增大,而沒有運行的進(jìn)程的 vruntime 保持不變。調(diào)度器總是選擇 vruntime 最小的進(jìn)程執(zhí)行,從而確保每個進(jìn)程都能獲得相對公平的 CPU 時間。
CFS 的設(shè)計思路簡單而有效,它根據(jù)每個進(jìn)程的權(quán)重分配運行時間。例如,假設(shè)有兩個進(jìn)程 A 和 B,權(quán)重分別為 1 和 2,調(diào)度周期為 30ms。那么進(jìn)程 A 的運行時間是 30*(1)/(1+2)=10ms,進(jìn)程 B 的運行時間是 30*(2)/(1+2)=20ms。同時,CFS 通過調(diào)整 vruntime 的增長速度來體現(xiàn)不同進(jìn)程的優(yōu)先級,優(yōu)先級高的進(jìn)程 vruntime 增長得較慢,從而獲得更多的運行機(jī)會。
為了降低調(diào)度延遲帶來的不公平性,CFS 采用了紅黑樹數(shù)據(jù)結(jié)構(gòu)來管理就緒隊列。紅黑樹可以快速地找到 vruntime 最小的進(jìn)程,從而減少調(diào)度時間。此外,CFS 還支持按組來分配時間片,通過 cgroup 機(jī)制,可以將 CPU 資源劃分為不同的組,以便更好地滿足不同應(yīng)用場景的需求。
4.3進(jìn)程狀態(tài)的轉(zhuǎn)換機(jī)制
進(jìn)程在其生命周期內(nèi)會經(jīng)歷多種狀態(tài)的轉(zhuǎn)換,這些轉(zhuǎn)換受到資源分配的影響。例如,當(dāng)一個進(jìn)程從創(chuàng)建態(tài)轉(zhuǎn)變?yōu)榫途w態(tài)時,它需要獲得除 CPU 以外的所有必要資源。一旦這些資源分配完成,進(jìn)程就處于就緒狀態(tài),等待 CPU 的分配。
當(dāng)進(jìn)程獲得 CPU 資源并開始執(zhí)行時,它處于運行態(tài)。然而,在運行過程中,進(jìn)程可能會因為等待某個事件(如等待 I/O 操作完成、等待資源分配等)而進(jìn)入等待態(tài)。在等待態(tài)下,進(jìn)程會被暫時掛起,以釋放 CPU 資源供其他進(jìn)程使用。當(dāng)?shù)却氖录l(fā)生時,進(jìn)程會被喚醒并重新進(jìn)入就緒態(tài)。
此外,進(jìn)程還可能因為時間片用完或被更高優(yōu)先級的進(jìn)程搶占 CPU 而從運行態(tài)回到就緒態(tài)。而當(dāng)一個進(jìn)程完成其任務(wù)或出現(xiàn)無法克服的錯誤時,它會進(jìn)入終止態(tài),等待操作系統(tǒng)進(jìn)行善后處理。
學(xué)習(xí)編程就得循環(huán)漸進(jìn),扎實基礎(chǔ),勿在浮沙筑高臺