Linux,Windows和UNIX的進程調度的分析


摘要 本文以Linux ,Unix ,Windows 操作系統為例,分析其進程調度策略,以期對進程調度過程有更深層次的認識

    關鍵詞 進程調度 優先級 時間片輪轉 實時進程 分時技術

 

 

    引言 :無論是在批處理系統還是分時系統中,用戶進程數一般都多於處理機數、這將導致它們互相爭奪處理機。另外,系統進程也同樣需要使用處理機。這就要求進程調度程序按一定的策略,動態地把處理機分配給處於就緒隊列中的某一個進程,以使之執行。

 

進程調度的具體功能可總結為如下幾點:

    作為進程調度的准備,進程管理模塊必須 將系統中各進程的執行情況和狀態特征記錄在各進程的PCB 表中。並且,根據各進程的狀態特征和資源需求等、進程管理模塊還將各進程的PCB 表排成相應的隊列並進行動態隊列轉接。進程調度模塊通過PCB 變化來掌握系統中存在的所有進程的執行情況和狀態特征,並在適當的時機從就緒隊列中選擇出一個進程占據處理機。

選擇占有處理機的進程

  進程調度的主要功能是按照一定的策略選擇—個處於就緒狀態的進程,使其獲得處理機執行。根據不同的系統設計目的,有各種各樣的選擇策略,例如系統開銷較少的靜態優先數調度法,適合於分時系統的輪轉法(Round RoLin) 和多級互饋輪轉法(Round Robin with Multip1e feedback) 等。這些選擇策略決定了調度算法的性能。

進行進程上下文切換

  —個進程的上下文(context) 包括進程的狀態、有關變量和數據結構的值、機器寄存器的值和PCB 以及有關程序、數據等。一個進程的執行是在進程的上下文中執行。當正在執行的進程由於某種原因要讓出處理機時,系統要做進程上下文切換,以使另一個進程得以執行。當進行上下文切換時點統要首先檢查是否允許做上下文切換( 在有些情況下,上下文切換是不允許的,例如系統正在執行某個不允許中斷的原語時) 。然后,系統要保留有關被切換進程的足夠信息,以便以后切換回該進程時,順利恢復該進程的執行。在系統保留了CPU 現場之后,調度程序選擇一個新的處於就緒狀態的進程、並裝配該進程的上下文,使CPU 的控制權掌握在被選中進程手中。

 

Unix 進程調度

   

   進程是程序的執行系統中活動的實體. 在UNIX 系統中進程被定義為映像的執行. 映像是計算機的執行環境, 它包括各種寄存器及存儲器的值、打開文件的狀態及現行目錄等等. 進程映像的組成部分: 寄存器、進程控制塊proc 結構和user 結構、進程數據區ppda ( 共享正文段( 由text 結構控制) 、數據段和棧段( 含用戶棧和核心棧) ) . 對於一個進程的映像來說,proc 結構、進程頁表、text 結構是常駐內存的, 而user 結構、正文段、數據段是非常駐內存部分. 它們在用戶虛擬空間形成一個整體, 一起換進換出.

    在Unix 操作系統中,所以的程序,不論是用戶級上還是在內核級上執行的,都出現在某個進程的現場內,所有的用戶程序都在它們自己的進程現場中運行。當這些用戶進程通過系統調用請求內核服務的時候,實現該系統調用的內核代碼繼續在請求進程的現場內執行,這就能讓內核方便的訪問進程的所有狀態及其他地址空間。它還提供了一種代表用戶程序記錄內核執行的當前狀態的方式。例如,如果需要掛起一次系統調用的執行來等待I/O 操作完成,那么內核有關系統調用處理的狀態就要保存在進程中。

    因為系統的所有活動,無論是用戶級上的還是內核級上的,都發生在某個進程的現場內,所有UNIX 內核只調度需要執行的進程。當使用傳統的分時調度策略的時候,在用戶級執行的進程不會被分入時間內執行。只有當前的內核進程明確允許的情況下,才能切換到在內核執行的另一個進程。

   

UNIX 進程調度核心思想


                           進程狀態轉換圖                               

  

進程調度機制問量

    在傳統Unix 中.進程優先級的設置是通過nice 和set priority 完成的;但不幸的是,速兩種系統調用無法使用最高優先級的進程得以立即進行,這是因為在循環調度( 時間片調度) 機制下.當進程的時聞片用完后.不論擾先級如何都讓出CPU .另外,由於它是非搶占式內核,優先級高的進程不能 立即打斷當前正在運行的進程,獲得CPU 資源.這對一些要求進程立即搶占CPU ,並且一次運行完成的實時應用是不能滿足要求的.

    UNIX 系統是一個多用戶分時系統, 其分時性是通過對用戶進程頻繁的調度來實現的, 系統的調度程序分成兩部分, 即處理機調度程序( swtch) 和進程對換程序(sched) . 在這里我涉及的是處理機調度程序. 在如下幾種情況下會調用處理機調度程序: (1) 若一個進程已到達它不能超過的某個點, 這時它就要調用“sleep ”, 而“sleep ”則調用“swtch ”; (2) 一個在核心態下運行的進程, 當它將要轉入用戶態之前, 會測試變量“runrun ”, 如果其值非0 , 則意味着更高優先權的進程已為運行准備就緒. 此時核心態進程也將調用“swtch ”.UNIX 進程調度策略是基於動態優先數, 優先數的設置有如下特點:

  swtch 由“trap ”、“sleep ”、“expand ”、“exit ”、“stop ”、“xalloc ”調用. 它是一個非常特殊的過程, 分為三段執行, 涉及3 個核心態進程.

①第一階段屬於當前進程的部分, 若當前進程不是進程0 , 則調用savu 過程將當前進程的環境變量保存在u. u- rsav. 調用retu 過程恢復進程0 的環境變量.

②第二階段屬於進程0 , 首先清runrun 標志, 該標志指示一個較當前進程具有更高優先權的進程已為運行准備就緒.swtch 通過do 循環尋找最高優先權進程( 重新計算各進程的優先級) . 若找到了滿足條件的就緒進程, 則將其從就緒隊列中取出, 並將該進程的優先級設置為當前的優先級curpri.

③第三階段屬於被選中的進程. 該進程已成為當前進程開始運行, 根據該進程的SSWAP 標志是否設置, 調用aretu 過程從u. u-ssav 中恢復進程的環境變量(r5 和r6) .

 

    程序發現某進程優先級高於當前運行進程的優先級時, 就要設置該標志, 而在中斷陷入處理程序結束之前, 檢查該標志是否設置, 若已設置則調用swtch() 程序進行調度, 另外, 在時鍾中斷處理中每隔1 秒也將runrun 標志設置一次, 並通過軟件中斷方式, 執行swtch() 程序, 這是為了增加調度機會, 保證良好的分時性.Swtch 程序調用了savu 、retu 、idle 、aretu 等過程。

savu 程序的作用: 這是一個匯編語言過程, 它將r5 和r6 的值存放到一個數組中, 該數組的地址作為一個參數傳遞至savu ;

retu 程序的作用: 匯編語言過程, 它復位第七個核心態段地址寄存器, 然后從“u. u- rsa ”最新可存取副本中復位r6 和r5 ;

aretu 程序的作用: 匯編語言過程, 它從作為參數傳遞過來的地址重裝r5 和r6 ;

idle 程序的作用: 匯編語言過程, 讓處理機空轉等待.

swtch 程序功能強大而其程序代碼簡潔精練( 共71 行代碼, 其中注釋語句等有30 行, 有效代碼是41 行) , 這也是整個UNIX 系統代碼的突出的特點.

 

Unix 系統是多用戶,多任務的操作系統,它通過向進程提供與機器無關的抽象服務,從而在Unix 實現之間提供了高度的程序的可移植性。程序的執行被限制在保持程序當前狀態的進程內,這些狀態包括虛擬地址空間,程序的變量值以及硬件狀態。內核給每個進程提供了一個環境讓這個環境顯得好像該進程是系統中正在執行的唯一進程那樣。這主要是賦予每個進程自己的虛擬地址空間來實現的。系統調用可以創建新進程,改變進程正在執行的程序,以及終止進程,還可以使用其他許多系統調用,其中包括動態分配未初始化數據的系統調用。

   

 

 

Linux 的進程調度

 

    傳統Unix 操作系統的調度算法必須實現幾個互相沖突的目標:進程響應時間盡可能快,后台作業的吞吐量盡可能高,進程的飢餓現象盡可能避免,低優先級和高優先級進程的需要盡可能調和等等。決定什么時候以怎樣的方式選擇一個新進程運行的這組規則就是所謂的調度策略(scheduling policy )。

    Linux 的進程調度是基於分時技術(time-sharing )。允許多個進程“並發”運行就意味着CPU 的時間被粗略地分成“片”,給每個可運行進程分配一片。

    當然,單處理器在任何給定的時刻只能運行一個進程。當一個並發執行的進程其時間片或時限(quantum )到期時還沒有終止,進程切換就可以發生。分時依賴於定時中斷,因此,對進程是透明的。為保證CPU 分時,不需要在程序中插入額外的代碼。

 

    調度策略也是基於依照優先級排隊的進程。有時用復雜的算法求出進程當前的優先級,但最后的結果是相同的:每個進程都與一個值相關聯,這個值表示把進程如何適當地分配給CPU 。在Linux 中,進程的優先級是動態的。調度程序跟蹤進程做了些什么,並周期性地調整它們的優先級。在這種方式下,在較長的時間間隔內沒有使用CPU 的進程,通過動態地增加它們的優先級來提升它們。相應地,對於已經在CPU 上運行了較長時間的進程,通過減少它們的優先級來處罰它們。每個進程在創建之初有一個基本的優先級,執行期間調度系統會動態調整它的優先級,交互性高的任務會獲得一個高的動態優先級,而交互性低的任務獲得一個低的動態優先級。類進程的時間片計算如下圖 所示:


   

    當談及有關調度問題時,傳統上把進程分類為“I/O 范圍(I/O-bound )”或“CPU

范圍(CPU-bound )”。前者頻繁地使用I/O 設備,並花費很多時間等待I/O 操作的完成;而后者是需要大量CPU 時間的數值計算應用程序。Linux 操作系統支持多進程,進程控制塊PCB(Process Control Block) 是系統中最為重要的數據結構之一。用來存放進程所必需的各種信息,PCB 用結構task —struct 來表示,包括進程的類型、進程狀態、優先級、時鍾信息等。Linux 系統中,進程調度操作由schedule() 函數執行,這是一個只在內核態運行的函數,函數代碼為所有進程共享。

 

   

Linux 進程調度時機

   

    Linux 的進程調度時機與現代操作系統中的調度時機基本一致,為了判斷是否可以執行內核的進程調度程序來調度進程,Linux 中設置了進程調度標志need —resched ,當標志為1 時,可執行調度程序.通常,Linux 調度時機分以下兩種情況:(1) 主動調度:指顯式調用schedule() 函數明確釋放CPU ,引起新一輪調度.一般發生在當前進程狀態改變,如:進程終止、進程睡眠、進程對某些信號處理過程中等.(2) 被動調度:指不顯示調用schedule() 函數,只是PCB 中的need_resched 進程調度標志,該域置位為1 將引起新的進程調度,而每當中斷處理和系統調用返回時,核心調度程序都會主動查詢need —resched 的狀態( 若置位,則主動調用schedule() 函數。一般發生在新的進程產生時、某個進程優先級改變時、某個進程等待的資源可用被喚醒時、當前進程時間片用完等 .

 

Linux 進程調度策略

  

一般來說,不同用途的操作系統的調度策略是不同的,Linux 進程調度是將優先級調度、時間片輪轉法調度、先進先出調度綜合起來應用.Linux 系統中。不同類型的進程調度策略也不一樣。

1 .與進程調度相關的數據結構

    每個進程都是一個動態的個體,其生命周期依次定義的數據結構為:TASK —RUNNING ,TASK —INTERRUPTIBLE 。TASK —UNINTERRUPTIBLE ,TASK —ZOMBIE 和TASK —STOPPED ,一個進程在其生存期間,狀態會發生多次變化。與其數據結構相對應的即是Linux 進程的狀態,分別是:運行態、等待態、暫停態和僵死態。

2 .進程狀態及其轉換過程的描述

    進程創建時的狀態為不可打斷睡眠,在do —fork() 結束前被父進程喚醒后。變為執行狀態,處於執行狀態的進程被移到run —queue 就緒任務隊列中等待調度。適當時候由schedule0 按調度算法選中,獲得CPU ,若采用輪轉法,即時,由時鍾中斷觸發timer —interrupt() ,其內部調用schedule() ,引起新一輪調度,當前進程的狀態仍處於執行狀態,因而把當前進程掛蟄Jruil —queue 隊尾。獲得CPU 且正在運行的進程若申請不到某資源。則調用sleep —on() 或interruptible —sleep —on() 睡眠,其task —struct 進程控制塊掛到相應資源的wait —queue 等待隊列如果調用sleep —on() 。則其狀態變為不可打斷睡眠,如果調用interruptible —sleep —on() ,則其狀態變為可打斷睡眠,Sleep —on() 或interruptible —sleep —on() 將調用schedule() 函數把睡眠進程釋放。

3. 進程分類和相應的進程調度策略


 

 

Linux 系統中,為了高效地調度進程,將進程分威兩類:實時進程和普通進程( 又稱非實時進程或一般進程) ,實時進程的優先級要高於其他進程,如果一個實時進程處於可執行狀態,它將先得到執行.實時進程又有兩種策略:時間片輪轉和先進先出,在時間片輪轉策略中。每個可執行實時進程輪流執行一個時間片,而先進先出策略每個進程按各自在運行隊列中的順序執行且順序不能變化。

在Linux 中,進程調度策略共定義了3 種:

Linux 系統中的每個進程用task ,struct 結構來描述,進程調度的依據是task —struct 結構中的policy 、priority 、counter 和rt —priority ,PCB 中設置Policy 數據項,其值用於反映針對不同類型的進程而采用的調度策略。SCHED —RR 和SCHED —FIFO 用於實時進程。分別表示輪轉調度策略和先進先出調度策略;SCHED —OTHER 表示普通進程,也按照輪轉調度策略處理。這三類調度策略均基於優先級.PCB 中設置Priority 數據項,其值為普通進程的調度優先級.普通進程的可用時間片的初始值即為該值,該值通過系統調用是可以改變的。PCB 中設置rt ~p riority 數據項,其值是實時進程專用的調度優先級,實時進程的可用時間片的初始值即為該值.該優先級也可以用系統調用來修改,PCB 中設置counter 數據項。用於進程可用時間片時值的計數,初始值為rt —priority 或Priority 。進程啟動后該值隨時鍾周期遞減。

 

 

Windows 進程

   

與進程調度相關的數據結構

    每個windows 進程都是由一個執行體進程塊來表示的。EPROCESS 塊中除了包含許多與進程有關的屬性以外,還包含和指向了許多其他的相關數據結構。例如,每個進程都有一個或多個 線程,這些線程由執行體線程塊來表示。執行體進程塊和相關的數據結構位於系統空間中,不過,進程塊環境是個例外,它位於進程地址空間中(因為它包含了一些需要由用戶模式代碼來修改的信息)。

    除了EPROCESS 塊以外,windows 子系統進程為每個windows 進程維護了一個類似的結構。而且,windows 子系統的內核模式部分有一個針對每個進程的數據結構,當一個線程第一次調用windows 的USER 或GDI 函數時,此數據結構就會被創建。

 

下圖是一個簡化的關於進程和線程數據結構的框圖:


 

 

Windows 系統的進程調度方法分析

 

    Windows 中的每一個進程都是EPROCESS 結構體。此結構體中除了進程的屬性之外還引用了其它一些與實現進程緊密相關的結構體。例如,每個進程都有一個或幾個線程, 線程在系統中就是ETHREAD 結構體。簡要描述一下存在於這個結構體中的主要的信息, 這些信息都是由對內核函數的研究而得知的。首先,結構體中有KPROCESS 結構體, 這個結構體中又有指向這些進程的內核線程(KTHREAD) 鏈表的指針( 分配地址空間), 基優先級, 在內核模式或是用戶模式執行進程的線程的時間, 處理器affini ty( 掩碼,定義了哪個處理器能執行進程的線程) ,時間片值。在ETHREAD 結構體中還存在着這樣的信息: 進程ID 、父進程ID 、進程映象名。

    在EPROCESS 結構體中還有指向PEB 的指針。ETHREAD 結構體還包含有創建時間和退出時間、進程ID 和指向EPROCESS 的指針, 啟動地址,I/O 請求鏈表和KTHREAD 結構體。在KTHREAD 中包含有以下信息: 內核模式和用戶模式線程的創建時間,指向內核堆棧基址和頂點的指針、指向服務表的指針、基優先級與當前優先級、指向APC 的指針和指向T E B 的指針。KTHREAD 中包含有許多其它的數據, 通過觀察這些數據可以分析出KTHREAD 的結構。通過遍歷KPROCESS 結構體中的ETHREAD, 找到系統中當前所有的KTHREAD 結構, 這個結構中的偏移量為0x124 處的Affinity 域(WindowsXPsp3) 即為設置CPU 親緣性掩碼的內存地址。

 

Windows 實現了一個優先驅動的,搶先式的調度系統——具有最高優先級的可運行線程總是運行,而該線程可能僅限於在允許它運行的處理器上運行,這種現象稱為處理器親和性,在默認的情況下,線程可以在任何一個空閑的處理器上運行,但是,你可以使用windows 調度函數,或者在映像頭部設置一個親和性掩碼來改變處理器親和性。

在此重點解釋CPU 親緣性的概念,CPU 親緣性就是指在系統中能夠將一個或多個進程或線程綁定到一個或多個處理器上運行, 這是期待已久的特性。也就是說“: 在1 號處理器上一直運行該程序”或者是“在所有的處理器上運行這些程序,而不是在0 號處理器上運行”。然后, 調度器將遵循該規則,程序僅僅運行在允許的處理器上。

    Windows 的進程調度代碼是在它的System 進程下的,所以它不屬於任何用戶進程上下文。調度代碼在適當的時機會切換進程上下文, 這里的切換進程上下文是指進程環境的切換, 包括內存中的可執行程序, 提供程序運行的各種資源. 進程擁有虛擬的地址空間, 可執行代碼, 數據, 對象句柄集, 環境變量, 基礎優先級, 以及最大最小工作集等的切換。

    而Windows 最小的調度單位是線程, 只有線程才是真正的執行體,進程只是線程的容器。Windows 調程序在時間片到期,或有切換線程指令執行( 如Sleep,KeWaitForSingleObject 等函數) 時, 將會從進程線程隊列中找到下一個要調度的線程執行體,並裝入到KPCR(Kernel 's Processor Control Region , 內核進程控制區域) 結構中,CPU 根據KPCR 結構中的KPRCB 結構執行線程執行體代碼。

    而在多核CPU 下,當Windows 調度代碼執行時, 從當前要調度執行的KTHREAD 結構中取出Affinity, 並與當前PC 機上的硬件配置數據中的CPU 掩碼作與操作, 結果寫入到指定的CPU ,例如雙核CPU 的設備掩碼為0x03, 如果當前KTHREAD 里的Affinity 為0x01, 那么0x01&0x03=0x01, 這樣執行體線程會被裝入CPU1 的KPRCB 結構中得以執行, 調度程序不會把這個線程交給CPU2 去執行。此過程如圖2 所示。這就是為線程選擇指定CPU 核的原理。

 

 

小結 :進程調度策略的選擇對整個系統性能有至關重要的影響,一個好的調度算法應該考慮很多方面:公平、有效、響應時間、周轉時間、系統吞吐量等等。但這些因素之間又是相互矛盾的,最終的取舍根據系統要達到的目標而定, 同時我們也 可 以看出多進程的管理是~種非常復雜的並發程序設計.每個進程的狀態不僅由其自身決定,而且還要受諸多外在因素的影響.而在此基礎上的進程調度,為了保證操作系統的穩定性、提高效率和增加靈活性,還必須采用很多方法,這些都是值得我們去研究和探討的:。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM