🌟🌟作者主頁:ephemerals__
🌟🌟所屬專欄:Linux
目錄
前言
一、進程優先級
1. 什么是進程優先級
2. 為什么有進程優先級
3. 進程優先級的作用
4. Linux進程優先級的本質
5. 修改進程優先級
二、進程切換
1. 概念?
2. 進程切換的過程
3. 進程切換的意義
4. Linux的進程調度(O(1)調度算法)
總結
前言
? ? ? ? 本篇文章我們在了解了進程概念和狀態等基礎知識之上,繼續學習進程的優先級,以及進程切換相關知識。
正文開始
一、進程優先級
1. 什么是進程優先級
????????進程優先級是操作系統分配給進程的一種“權重”或“級別”,用來決定在多個進程同時競爭 CPU 資源時,哪一個進程先獲得運行的機會。優先級高的進程會被優先調度執行,低優先級的進程則可能需要等待。?
2. 為什么有進程優先級
? ? ? ? 大多數情況下,正在執行的程序數量是比較多的,而CPU資源有限。為了使那些重要、急需執行的任務先執行,就有了優先級。這樣一來,資源分配會得到優化,整個系統更加合理高效。
注:現代操作系統更加考慮進程獲取資源的公平性,優先級的差距不會太大。
3. 進程優先級的作用
? ? ? ? 進程優先級可以保證關鍵任務或實時任務及時調度(如操作系統內核進程通常優先級較高),并且可以防止某個進程長期占用 CPU,改善系統性能。其次,優先級可以實現多用戶或多任務下的公平資源分配。
4. Linux進程優先級的本質
? ? ? ? 在Linux下,進程優先級的本質就是一個整數,存儲在task_struct結構體當中,直接影響了進程調度的先后順序。進程優先級的值越小,優先級越高。
可以使用以下命令查看到當前所有進程的優先級:
ps -al
運行結果:
PRI:進程優先級的值,默認是80
NI:也叫做nice值,是進程優先級的修正數據。其取值范圍是[-20, 19]。
PRI的默認值(80) 和 NI 之和表示進程的真實優先級大小。也就是說,Linux進程優先級的范圍是[60, 99],一共40個。
注:ps -l指令還可以看到當前進程的PID,父進程ID等。其中還有一項UID,它用來標識Linux下的用戶,進行用戶區分。當用戶創建進程時,進程會記錄用戶的UID,訪問文件時(本質是進程在訪問),進程就拿這個UID與文件的擁有者或所屬組的UID進行對比。
5. 修改進程優先級
? ? ? ? 在Linux下,修改進程優先級主要是通過修改nice值來完成(注意nice值的取值范圍)。可以使用top命令修改進程優先級:
輸入top --> 按“ r ” --> 輸入進程的PID --> 輸入新的nice值
注意:普通用戶只能給自己的進程設置優先級,且只能將優先級調低(nice值調高);而root用戶可以在取值范圍內隨意調整任意進程的優先級。?
?接下來我們嘗試調整一個進程process的優先級:
process的源碼:
#include <stdio.h>
#include <unistd.h>int main()
{while(1){sleep(1);printf("hello\n");}return 0;
}
首先運行process,查看process的優先級:
可以看到process的優先級是80。接下來將nice修改為10:
這里process的PRI變成了90,而nice值為10。此時90是process的真實優先級。?
如果修改為-10:
可以看到,普通用戶下,將nice值調低是不被允許的。
如果改為100:
由于取值范圍的限制,nice值最高為19,輸入100只能將其改為19。
注意:如果優先級設置不合理,就會導致優先級低的進程長時間獲取不到CPU資源,導致進程饑餓。
二、進程切換
1. 概念?
? ? ? ? CPU在執行一個進程時,由于還有其他進程的存在,它不會一次將所有代碼全部跑完,而是跑一會后,轉而去執行其他進程,再來執行當前進程......這樣才可以在一段時間之內使得多個進程都有所推進。CPU從執行當前進程變為執行其他進程的過程叫做進程切換。
當代操作系統都是分時的,每個進程都有它相應的時間片(本質就是一個計時器)。當前進程的時間片用完后,就會切換到其他進程。
2. 進程切換的過程
????????一個進程在CPU上運行到時間片結束之后,首先存儲自己執行位置的上下文數據到自己的TSS(任務狀態段,存儲在task_struct當中)當中,然后將進程重新放入調度隊列隊尾。進程發生切換時,將新的進程的TSS中的數據拷貝(恢復)到CPU的對應寄存器中,繼續執行。?
進程的上下文數據指的是進程在運行時,需要保存和恢復的所有關鍵信息(例如發生切換時CPU寄存器中的數據),這樣就可以保證進程發生一系列切換后,重新執行該進程時,能從原來的狀態繼續執行。
3. 進程切換的意義
????????進程切換讓操作系統能管理多個進程,保證各進程能按照優先級獲得CPU使用權,是實現多任務的基礎。多個進程可以“輪流”運行,系統利用率和響應速度得以提高。
4. Linux的進程調度(O(1)調度算法)
????????高效的進程切換不僅保證了多任務系統的流暢運行,還直接影響著系統的響應速度與整體性能。為了實現快速、合理的進程調度,Linux內核采用了多種調度算法,其中O(1)調度器就是在2.6版本中所引入的一種重要算法。接下來,我們將深入了解O(1)調度算法,看看它是如何實現對大量進程的高效管理的。
? ? ? ? 在之前的文章中,我們提到過一個CPU維護一個進程調度隊列,該隊列中存放著一個個PCB,等待CPU對它們進行調度。實際上這個“進程調度隊列”遠遠比傳統的隊列復雜。在Linux中,進程調度隊列叫做runqueue,它的結構如圖所示:
一個CPU維護一個運行隊列runqueue,其中數組prio_array_t中的queue是存放進程PCB的關鍵。
queue本質是一個鏈地址法的哈希表,其中有140個哈希桶(前100個哈希桶表示實時優先級,剩余40個剛好對應40種優先級,所以哈希桶的下標 - 40就是進程優先級),相同優先級的進程task_struct存放在同一個哈希桶中,這樣,當需要調度時,按照下標小到大的順序掃描每一個哈希桶,先訪問到的進程就是優先級高的(因為優先級的值越小,優先級越高),然后拿出桶中的task_struct進行調度。
考慮到每次都要遍歷140個哈希桶,效率較低,所以有一個位圖bitmap,位圖中有5個32位的整形變量,加起來一共160位,其中的140位表示對應的哈希桶中是否有task_struct,所以可以直接判斷哪一位是“1”,就表示哪一個桶需要被訪問。除此之外,還有一個變量nr_active,表示整個哈希表中的元素個數,對應正在運行時的進程個數。
queue、bitmap、nr_active共同構成一個結構體,叫做rqueue_elem。
如此,訪問位圖,按順序找到存在元素的哈希桶,進行調度,整個調度過程效率就能達到O(1)。?
但是圖中的prio_array_t數組有兩個元素,也就是說有兩張哈希表,這是為什么呢?這就要從進程切換的角度開始談起了。
試想,如果只有一張哈希表,那么發生進程切換時會怎么樣?
假設第100個哈希桶當中有3個進程,第一個進程的時間片耗盡后,會發生進程切換,此時按照優先級,只能將它重新插入在當前哈希桶的鏈表尾部,當然,其他兩個進程也是如此。這樣雖然能夠完成這3個進程的輪流調度,但是更低優先級(也就是下標更大的哈希桶)的進程呢?當前哈希桶中只要有元素,CPU就會一直輪流調度當前哈希桶中的進程,而會忽略優先級更低的進程,導致進程饑餓。要調度優先級更低的進程,就必須等待當前優先級的進程全部調度完畢,顯然是不合理的,極大地損失了公平性。
因此,前輩們想到了一個巧妙的方法:再創建一個相同的結構體rqueue_elem,其中也包含一個queue、bitmap、nr_active,兩個rqueue_elem構成一個數組prio_array_t[2]。
然后創建兩個指針native和expired,分別指向prio_array_t[0]和prio_array_t[1],native指向的表示“活躍隊列”;expired指向的表示“過期隊列”。當發生進程切換時,被調度結束的進程會插入到“過期隊列”的對應優先級的哈希桶中,然后繼續訪問“活躍隊列”中的哈希桶,一個個地調度。這樣,當前“活躍隊列”哈希桶中的元素就會越來越少,直到為0,就可以訪問優先級更低的哈希桶......最后所有進程都會被調度,而不是高優先級進程運行完畢后才能調度低優先級進程。
當“活躍隊列”整個哈希表內的進程都被調度過后(此時這些進程應全都位于“過期隊列”對應的哈希桶中),交換native和expired兩個指針的指向,這樣“過期隊列”就變成了“活躍隊列”,就可以開始下一輪的調度了。
如果有新進程出現,會直接插入“過期隊列”當中。此時新進程處于“就緒狀態”,等待一輪的調度結束后,新進程就可以被調度。
總結
? ? ? ? 本篇文章,我們系統地了解了進程優先級的定義、意義以及在Linux下的調整方法,也梳理了進程切換的基本流程與核心作用。從進程優先級到切換機制,再到Linux的O(1)調度算法,這些知識共同揭示了操作系統在多任務管理中的高效與精妙。掌握這些原理,有助于更深入理解和優化 Linux 系統的性能,為后續程序地址空間和進程控制的學習打下堅實基礎。如果你覺得博主講的還不錯,就請留下一個小小的贊在走哦,感謝大家的支持???