前言
? ? ? ? 進程是資源分配的基本單位, 在OS中存在這很多的進程, 那么就必然存在著資源競爭的問題, 操作系統是如何進行資源分配的? 對于多個進程同時運行, 操作系統又是如何調度達到并發呢? 本文將以Linux kernel 2.6為例 , 向大家介紹進程在操作系統中 (OS) 的調度原理;
1. 進程優先級
? ? ? 進程優先級是操作系統中用來確定進程獲取 CPU 資源的先后順序的一種機制;
? ? ? ? ?為什么要排隊? 本質是資源不足; 在一臺電腦中可能只有一個CPU, 但是可能會同時啟動多個進程, 那么進程在分配CPU資源時就需要排隊(等待CPU資源);
? ? ? ? ?對于較為重要的進程, 可以設置高優先級,? 高優先級進程有優先執行權利。配置進程優先權對多任務環境的linux很有用,可以改善系統性能。還可以把進程運行到指定的CPU上,這樣一來,把不重要的進程安排到某個CPU,可以大大改善系統整體性能。
使用ps -l查看系統進程:
其中較為重要的信息:
- UID : 代表執行者的身份
- PID : 代表這個進程的代號
- PPID :代表這個進程是由哪個進程發展衍生而來的,亦即父進程的代號
- PRI :代表這個進程可被執行的優先級,其值越小越早被執行
- NI :代表這個進程的nice值(在進程PCB中)
?
Linux中進程優先級范圍:? 60 ~ 99;
Linux中創建進程,默認進程優先級是 80
?在Linux中支持優先級的動態調整,?? 動態調整的規則:
nice值最小是-20, 超過 -20就統一成 -20;
nice值最大為19, 超過19 統一成19;
?優先級(PRI) 的計算 :? PRI (new) = PRI (old) + nice ;
PRI+ nice值是基于默認值80計算的(不會累計),比如: 先把nice值設為10,那么PRI就會變成90,使用root賬戶將nice值設為-10,PRI就變成了70 ;
用top命令更改已存在進程的nice:
- top
- 進入top后按“r”–>輸入進程PID–>輸入nice值
?注意: OS只允許普通用戶把優先級調低, 不允許把優先級調高, root賬戶無限制;
?為什么設置限制?
OS在調度時,為了讓每一個進程較為均衡得到調度;? 如果nice值可以隨意亂改, 就會存在用戶惡意的將自己的進程優先級調高,導致優先級低的進程長時間得不到CPU資源 ;
需要注意的點是,進程的nice值不是進程的優先級,PR I和 NI 他們不是一個概念,但是進程nice值會影響到進程的優先級變化。可以理解nice值是進程優先級的修正修正數據
?幾個較為重要的概念:
- 競爭性: 系統進程數目眾多,而CPU資源只有少量,甚至1個,所以進程之間是具有競爭屬性的。為了高效完成任務,更合理競爭相關資源,便具有了優先級
- 獨立性:? 多進程運行,需要獨享各種資源,多進程運行期間互不干擾
- 并行:??多個進程在多個CPU下分別,同時進行運行,這稱之為并行
- 并發:?多個進程在一個CPU下采用進程切換的方式,在一段時間之內,讓多個進程都得以推進,稱之為并發
2. 并發
?本文的重點是并發 , 先來介紹一下什么是并發??
? ? ? ? 一個進程在被CPU調度時, 并不是一直占用CPU直至運行結束 ,?? 而是每隔一段時間(這個時間段也叫做時間片)?,它就會被從CPU上被剝離下來 ,? ????????然后會重新放進運行隊列等待被調度,如此反復,直到進程運行完畢; CPU每次調度進程時, 都會到運行隊列中去取;
????????Linux內核支持進程之間CPU資源的搶占,它是一種基于時間片輪轉式搶占式內核,時間片非常的短,輪轉速度非常快(一秒內進程可能被調度了100次),所以我們很難察覺;
新的問題: 進程在運行時會被從CPU上剝離下來, 那下次調度時, CPU是如何知道進程執行到哪里的呢?
?小方框表示寄存器;
????????在CPU當中有很多各種各樣的寄存器:eax、ebx、ecx、edx、ss、ds、cs、gs、fs、ebp、esp、eip..
?寄存器的功能有很多,比如記錄程序/進程的運行狀態(走到那一步);
比如: cpu內:eip:程序計數器;
????????進程在運行時會使用這些寄存器,進程會產生各種各樣的數據,在寄存器中臨時保存 !
????????如果有多個進程,各個進程在CPU內形成的臨時數據,都是不一樣的每個進程運行到哪里,產生的臨時數據,叫做進程硬件上下文;?
????????在進行輪轉切換時會暫時將這個數據存儲到進程PCB里;
注意:
????????在以前老的Linux中是這樣,現在的不是直接保存到PCB,原因是PCB內容太多,太大,但都與PCB有聯系,這里只是可以理解為放在PCB當中;本質就是將CPU寄存器當中的數據保存到內存當中;
????????CPU寄存器硬件只有一套,進程上下文數據有很多套,比如10個進程有10套上下文數據;
????????寄存器 != 寄存器內容
3. Linux kernel 2.6?內核調度隊列與調度原理
? ? ? ? 有了前邊的基礎知識補充, 接下來我們介紹一下Linux kernel 2.6?內核調度隊列以及基本調度原理;
一個CPU擁有一個runqueue(如果有多個CPU就要考慮進程個數的負載均衡問題)
?下圖就是Linux2.6內核中進程隊列的數據結構:
?優先級:
- ?普通優先級: 100~ 139
- ?實時優先級: 0~ 99(不關心)
100~139就是我們使用指令看到的40個優先級; 從第100號開始(PRI: 60), 優先級依次向下遞減;
?如下圖:
?CUP調度隊列中的進程是如果直接遍歷一遍隊列, 然后依次調度進程, 遍歷的過程也會造成資源的浪費; 所以在設計時加入了nr_active 和?bitmap[5];
????????int整形占4個字節,32個bit位 5 x 32 也就是 160個 bit位 (足夠表示140個優先級) ; 利用位圖映射可以極大的提高效率;?
?????????nr_active判斷隊列是否有進程,bitmap位圖映射快速找到進程位置,這樣下來輪轉一次的效率就會非常高,時間復雜度接近O(1) ;
在操作系統中會維護兩個這樣的隊列 (活動對列 和 過期隊列);?
????????進程在活動隊列并不一定就運行完了,可能是時間片結束了,被調度完之后就會加入到了過期隊列;
同時還會維護兩個指針:
- void *active? ?活動隊列
- void *expired? ?過期隊列
CPU只會執行active指針指向的隊列???????
?????????當進程優先級為99的進程正在被執行時,新插入一些優先級較高的進程,這些進程會被插入
到過期隊列當中;? (如果直接插入到活動隊列,那就會導致優先級較低的隊列一直等待,進而引發進程饑餓問題)
????????這樣一來,?過期隊列的進程不斷增多,?由于不會插入新的進程,所以它的進程數量一定會越來越少;當active指針指向的活動隊列執行完畢,就將兩個指針指向的隊列進行交換即可
????????原本的活動隊列執行空了,再來新的進程就插入到這個隊列,這個隊列會繼續作為過期隊列
如此循環,最終就完成了進程的調度;
???????????????這樣的設計方式不僅提高了效率,并且也解決了進程饑餓問題;
總結
? ? ? ? ?進程是資源分配的基本單位, 在OS中存在這很多的進程, 那么就必然存在著資源競爭的問題, 于是便有了進程優先級來確認進程調度的先后順序;? 但這也可能會伴隨著進程饑餓的問題,? 而在Linux2.6版本中的進程調度設計很好的解決這些問題; 具有很高的參考學習的價值;? 好了以上便是本文的全部內容, 希望對你有所幫助 , 感謝閱讀 !