處理機是計算機系統的核心資源。操作系統的功能之一就是處理機管理。隨著計算機的迅速發展,處理機管理顯得更為重要,這主要由于計算機的速度越來越快,處理機的充分利用有利于系統效率的大大提高;處理機管理是整個操作系統的重心所在,其管理的好壞直接影響到整個系統的運行效率;而且操作系統中并發活動的管理和控制是在處理機管理下實現的,處理機管理集中了操作系統中最復雜的部分,它設計的好壞關系到整個系統的成敗。
進程是處理機管理中最基本的、最重要的概念。進程是系統并發執行的體現。由于在多道程序系統中,眾多的計算機用戶都以各種各樣的任務,隨時隨地爭奪使用處理機。為了動態地看待操作系統,則以進程作為獨立運行的基本單位,以進程作為分配資源的基本單位,從進程的角度來研究操作系統。因此,處理機管理也被稱為進程管理。處理機管理的功能就是組織和協調用戶對處理機的爭奪使用,把處理機分配給進程,對進程進行管理和控制,最大限度也發揮處理機的作用。
1.進程的概念
用靜態的觀點看,操作系統是一組程序和表格的集合。用動態的觀點看,操作系統是進程的動態和并發執行的。而進程的概念實際上是程序這一概念發展的產物。因此,可以從分析程序的基本特征入手,引出“進程”的概念。
順序程序是指程序中若干操作必須按照某種先后次序來執行,并且每次操作前和操作后的數據、狀態之間都有一定的關系。在早期的程序設計中,程序一般都是按順序執行的。
在多道程序系統中,程序的運行環境發生了很大的變化。主要體現在:
(1)資源共享。為了提高資源的利用率,計算機系統中的資源不再由一道程序專用,而是由多道程序共同使用。
(2)程序的并發執行或并行執行。邏輯上講允許多道不同用戶的程序并行運行;允許一個用戶程序內部完成不同操作的程序段之間并行運行;允許操作系統內部不同的程序之間并行運行。物理上講:內存儲器中保存多個程序,I/O 設備被多個程序交替地共享使用;在多處理機系統的情形下,表現為多個程序在各自的處理機上運行,執行時間是重疊的。單處理機系統時,程序的執行表現為多道程序交替地在處理機上相互空插運行。
實際上,在多道程序系統中,程序的并行執行和資源共享之間是相輔相成的。一方面,只有允許程序并行執行,才可能存在資源共享的問題;另一方面,只有有效地實現資源共享,才可能使得程序并行執行。
這樣,可增強計算機系統的處理能力和提高機器的利用率。并發操作實際上是這樣的:大多數程序段只要求操作在時間上是有序的,也就是有些操作必須在其他操作之前,這是有序的,但其中有些操作卻可以同時進行。
2.進程的狀態轉換
由進程運行的間斷性,決定了進程至少具有以下三種狀態:
(1)就緒狀態。當進程已分配了除 CPU 以外的所有必要的資源后,只要能再獲得處理機,便能立即執行,把這時的進程狀態稱為就緒狀態。在一個系統中,可以有多個進程同時處于就緒狀態,通常把它們排成一個隊列,稱為就緒隊列。
(2)執行狀態指進程已獲得處理機,其程序正在執行。在單處理機系統中,只能有一個進程處于執行狀態。
(3)阻塞狀態指進程因發生某事件(如請求 I/O、申請緩沖空間等)而暫停執行時的狀態,亦即進程的執行受到阻塞,故稱這種暫停狀態為阻塞狀態,有時也稱為“等待”狀態,或“睡眠”狀態。通常將處于阻塞狀態的進程排成一個隊列,稱為阻塞隊列。
進程的狀態隨著自身的推進和外界的變化而變化。例如,就緒狀態的進程被進程調度程序選中進入執行狀態;執行狀態的進程因等待某一事件的發生轉入等待狀態;等待狀態的進程所等待事件來到便進入就緒狀態。進程的狀態可以動態地相互轉換,但阻塞狀態的進程不能直接進入執行狀態,就緒狀態的進程不能直接進入阻塞狀態。在任何時刻,任何進程都處于且只能處于這其中一種狀態。進程狀態的變化情況如下:
(1)運行態→等待態:一個進程運行中啟動了外圍設備,它就變成等待外圍設備傳輸信息的狀態;進程在運行中申請資源(主存儲空間及外圍設備因得不到滿足)時,變成等待資源狀態,進程在運行中出現了故障(程序出錯或主存儲器讀寫錯誤等),變成等待干預狀態。
(2)等待態→就緒態:外圍設備工作結束后等待外圍設備傳輸信息的進程結束等待;等待的資源能得到滿足時(另一個進程歸還了資源),則等待資源者就結束等待;故障排隊后讓等待干預的進程結束等待,任何一個結束等待的進程必須先變成就緒狀態,待分配到處理器后才能運行。
(3)運行態→就緒態:進程用完了一個使用處理器的時間后強迫該進程暫時讓出處理器,當有更優先權的進程要運行時也迫使正在運行的進程讓出處理器。由于自身或外界原因成為等待狀態的進程讓出處理器時,它的狀態就變成就緒狀態。
(4)就緒態→運行態:等待分配處理器的進程,系統按一種選定的策略從處于就緒狀態的進程中選擇一個進程,讓它占用處理器,那個被選中的進程就變成了運行態。
圖 2-2 所示為進程的三種基本狀態及各狀態之間的轉換。
3.關于掛起狀態
在不少系統中,進程只有如圖 2-2 所示的三種狀態。但在另一些系統中,又增加了一些新狀態,其中最重要的是掛起狀態。引入掛起狀態的原因有:
(1)對換的需要。為了緩和內存緊張的情況,而將內存中處于阻塞狀態的進程換至外存上,使進程又處于一種有別于阻塞狀態的新狀態。因為即使該進程所期待的事件發生,該進程仍不具備執行條件而不能進入就緒隊列,稱這種狀態為掛起狀態。
(2)終端用戶的請求。當終端用戶在自己的程序運行期間,發現有可疑問題時,往往希望使自己的進程暫停下來。也就是說,使正在執行的進程暫停執行,若是就緒進程,則不接受調度以便研究其執行情況或對程序進行修改。把這種靜止狀態也稱為掛起狀態。
(3)父進程請求。父進程常希望掛起自己的子進程,以便考查和修改子進程,或者協調各子進程間的活動。
(4)負荷調節的需要。當實時系統中的工作負荷較重,有可能影響到對實時任務的控制時,可由系統把一些不重要的進程掛起,以保證系統正常運行。
(5)操作系統的需要。操作系統希望掛起某些進程,以便檢查運行中資源的使用情況及進行記賬。
綜上所述,不難了解掛起狀態具有以下三個屬性。
(1)被掛起的進程,原來可能處于就緒狀態,此時進程(被掛起)的狀態稱為掛起就緒;若被掛起的進程原來處于阻塞狀態,此時的狀態稱為掛起阻塞。不論哪種狀態,該進程都是不可能被調度而執行的。
(2)處于掛起阻塞狀態的進程,其阻塞條件與掛起條件無關;當進程所期待的事件出現后,進程雖不再被阻塞,但仍不能運行,這時,應將該進程從靜止阻塞狀態轉換為掛起就緒狀態。
(3)進程可以由其自身掛起,也可由用戶或操作系統等將之掛起。其目的都在于阻止進程繼續運行,被掛起的進程只能用顯式方式來激活,以便從掛起狀態中解脫出來。
如圖 2-3 所示為具有掛起操作的進程狀態的演變情況。
4.進程互斥與同步進程互斥定義為:一組并發進程中一個或多個程序段,因共享某一共有資源而導致必須以一個不允許交叉執行的單位執行。也就是說互斥是要保證臨界資源在某一時刻只被一個進程訪問。
進程同步定義為:把異步環境下的一組并發進程因直接制約而互相發送消息而進行互相合作、互相等待,使得各進程按一定的速度執行的過程稱為進程同步。也就是說進程之間是異步執行的,同步即是使各進程按一定的制約順序和速度執行。
簡單一點來說,互斥是資源的競爭關系,而同步是進程間的協作關系。
系統中有些資源可以供多個進程同時使用,有些資源則一次僅允許一個進程使用,將一次僅允許一個進程使用的資源稱為臨界資源,很多物理設備如打印機、磁帶機等都屬于臨界資源,某些軟件的變量、數據、表格也不允許兩個進程同時使用,所以也是臨界資源。
進程在并發執行中可以共享系統中的資源。但是臨界資源的訪問則必須互斥進行,即各進程對臨界資源進行操作的那段程序的執行也必須是互斥的,只有這樣才能保證對臨界資源的互斥訪問。把一個進程訪問臨界資源的那段程序代碼稱為臨界區,有了臨界區的概念,進程間的互斥就可以描述為:禁止兩個或兩個以上的進程同時進入訪問同一臨界資源的臨界區。為此,必須有專門的同步機構來協調它們,協調準則如下:
(1)空閑讓進。無進程處于臨界區時,若有進程要求進入臨界區則立即允許其進入;
(2)忙則等待。當已有進程進入其臨界區時,其他試圖進入各自臨界區的進程必須等待,以保證諸進程互斥地進入臨界區;
(3)有限等待。有若干進程要求進入臨界區時,應在有限時間內使一進程進入臨界區,
即它們不應相互等待而誰也不進入臨界區;
(4)讓權等待。對于等待進入臨界區的進程必須釋放其占有的 CPU。信號量可以有效地實現進程的同步和互斥。在操作系統中,信號量是一個整數。當信號量大于等于 0 時,代表可供并發進程使用的資源實體數,當信號量小于零時則表示正在等待使用臨界區的進程數。建立一個信號量必須說明所建信號量代表的意義和設置初值,以及建立相應的數據結構,以便指向那些等待使用該臨界區的進程。
對信號量只能施加特殊的操作:P 操作和 V 操作。P 操作和 V 操作都是不可分割的原子操作,也稱為原語。因此,P 原語和 V 原語執行期間不允許中斷發生。
P(sem)操作的過程是將信號量 sem 值減 l,若 sem 的值成負數,則調用 P 操作的進程暫停執行,直到另一個進程對同一信號量做 V 操作。V(sem)操作的過程是將信號量 sem 值加 1,若 sem 的值小于等于 0,從相應隊列(與 sem 有關的隊列)中選一個進程,喚醒它。
一般 P 操作與 V 操作的定義如下所述。
P 操作:
P(sem){
sem = sem - 1;
if(sem < 0)進程進入等待狀態;
else 繼續進行;}
V 操作:
V(sem){
sem = sem + 1;
if(sem ≤ 0)喚醒隊列中的一個等待進程;
else 繼續進行;}
為了保護共享資源(如公共變量),使它們不被多個進程同時訪問,就要阻止這些進程同時執行訪問這些資源(臨界資源)的代碼段(臨界區);進程互斥不允許兩個以上共享臨界資源的并發進程同時進入臨界區。利用 P、V 原語和信號量可以方便地解決并發進程對臨界區的進程互斥問題。
設信號量 mutex 是用于互斥的信號量,初值為 1,表示沒有并發進程使用該臨界區。于是各并發進程的臨界區可改寫成下列形式的代碼段:
P(mutex);
臨界區
V(mutex);
要用 P,V 操作實現進程同步,需要引進私用信號量。私用信號量只與制約進程和被制約進程有關,而不是與整組并發進程相關。與此相對,進程互斥使用的信號量為公用信號量。首先為各并發進程設置私用信號量,然后為私用信號量賦初值,最后利用 P,V 原語和私用信號量規定各進程的執行順序。
經典同步問題的例子是“生產者-消費者”問題。這要求存后再取,取后再存,即有兩個制約關系,為此,需要兩個信號量,表示緩沖區中的空單元數和非空單元數,記為 Bufempty 和 Buffull,它們的初值分別是 1 和 0,相應的程序段形式是:
生產者
loop
生產一產品 next;
P(Bufempty); ----檢查, 滿足則運行,不滿足則暫停
next 產品存緩沖區;
V(Buffull); ----恢復, 滿足則恢復(別的進程), 不滿足則繼續進行
endloop
消費者
loop
P(Buffulll);
從緩沖區中取產品;使用產品
V(Bufempty);
endloop
S1理解為 緩沖區的容量
S2理解為 當前商品數量
5.前趨圖
前趨圖是一個由結點和有向邊構成的有向無循環圖。該圖通常用于表現事務之間先后順序的制約關系。圖中的每個結點可以表示一個語句、一個程序段或是一個進程,結點間的有向邊表示兩個結點之間存在的前趨關系。
例:在計算機中,經常采用流水線方式執行指令,每一條指令都可以分解為取指、分析和執行三步。取指操作為 Ai,分析操作為 Bi 和執行操作為 Ci(i=1,2,3)。如圖 2-4 所示為三個任務各程序段并發執行的前驅圖。
圖中 A1 沒有前趨結點,稱為開始結點,它不受任何制約,可以直接執行;而 B1 與 A2 只能在 A1 執行完成之后才能開始,而 B2 必須在 B1 與 A2 完成之后才能開始;C3 沒有后繼結點,稱為終止結點。
在前趨圖中,執行先后順序的制約關系可分為兩種:直接制約和間接制約。
直接制約通常是指一個操作中,多個步驟之間的制約關系,也可以說是“同步的進程之間的制約關系”。如圖 2-4 所示,A1、B1、C1 是一條指令的取指、分析、執行的三個步驟,所以它們之間的關系是直接制約。
間接制約通常是指多個操作之間相同步驟的制約關系,也可以說是“互斥的進程之間的制約關系”。如圖 2-4 所示,A1、A2、A3 之間就存在間接制約的關系。
前趨圖的應用廣泛,在項目開發中,可用前趨圖來分析哪些活動可以并行完成。同時項目管理工具:Pert 圖,單(雙)代號網絡圖等都融入了前趨圖的思想。
6.進程調度與死鎖
進程調度即處理器調度(又稱上下文轉換),它的主要功能是確定在什么時候分配處理器,并確定分給哪一個進程,即讓正在執行的進程改變狀態并轉入就緒隊列的隊尾,再由調度原語將就緒隊列的隊首進程取出,投入執行。
引起進程調度的原因有以下幾類:
(1)正在執行的進程執行完畢。
(2)執行中的進程自己調用阻塞原語將自己阻塞起來進入睡眠狀態。
(3)執行中的進程調用了 P 原語操作,從而因資源不足而阻塞;或調用 V 原語操作激活了等待資源的進程隊列。
(4)在分時系統中,當一進程用完一個時間片。
(5)就緒隊列中某進程的優先級變得高于當前執行進程的優先級,也將引起進程調度。
進程調度的方式有兩類:剝奪方式與非剝奪方式。所謂非剝奪方式是指,一旦某個作業或進程占用了處理器,別的進程就不能把處理器從這個進程手中奪走,直到該進程自己因調用原語操作而進入阻塞狀態,或時間片用完而讓出處理機;剝奪方式是指,當就緒隊列中有進程的優先級高于當前執行進程的優先級時,便立即發生進程調度,轉讓處理機。
進程調度的算法是服務于系統目標的策略,對于不同的系統與系統目標,常采用不同的調度算法:
(1)先來先服務(First Come and First Serverd,FCFS)調度算法,又稱先進先出(First In and First Out,FIFO)。就緒隊列按先來后到原則排隊。
(2)優先數調度。優先數反映了進程優先級,就緒隊列按優先數排隊。有兩種確定優先級的方法,即靜態優先級和動態優先級。靜態優先級是指進程的優先級在進程開始執行前確定,執行過程中不變,而動態優先級則可以在進程執行過程中改變。
(3)輪轉法(Round Robin)。就緒隊列按 FCFS 方式排隊。每個進程執行一次占有處理器時間都不超過規定的時間單位(時間片)若超過,則自行釋放自己所占有的 CPU 而排到就緒隊列的末尾,等待下一次調度。同時,進程調度程序又去調度當前就緒隊列中的第一個進程。
進程管理是操作系統的核心,在進程管理的實現中,如果設計不當,會出現一種尷尬的局面——死鎖。
當若干個進程互相競爭對方已占有的資源,無限期地等待,不能向前推進時會造成“死鎖”。例如,P1 進程占有資源 R1,P2 進程占有資源 R2,這時,P1 又需要資源 R2,P2 也需要資源 R1,它們在等待對方占有的資源時,又不會釋放自己占有的資源,因而使雙方都進入了無限等待狀態。
死鎖是系統的一種出錯狀態,它不僅會浪費大量的系統資源,甚至還會導致整個系統的崩潰,所以死鎖是應該盡量預防和避免的。
(1)死鎖條件。產生死鎖的主要原因是供共享的系統資源不足,資源分配策略和進程的推進順序不當。系統資源既可能是可重復使用的永久性資源,也可能是消耗性的臨時資源。產生死鎖的必要條件是:互斥條件、保持和等待條件、不剝奪條件和環路等待條件。
(2)解決死鎖的策略。處于死鎖狀態的進程不能繼續執行但又占用了系統資源,從而阻礙其他作業的執行。
解決死鎖有兩種策略:一種是在死鎖發生前采用的預防和避免策略;另一種是在死鎖發生后采用的檢測與恢復策略。
死鎖的預防主要是通過打破死鎖產生的 4 個必要條件之一來保證不會產生死鎖。采用的死鎖預防策略通常有資源的靜態分配法或有序分配法,它們分別打破了資源動態分配條件和循環等待條件,因此不會發生死鎖。但這樣做會大大降低系統資源的利用率和進程之間的并行程度。
死鎖避免策略,則是在系統進行資源分配時,先執行一個死鎖避免算法(典型的如銀行家算法),以保證本次分配不會導致死鎖發生。由于資源分配很頻繁,因此死鎖避免策略要耗費大量的 CPU 和時間。
什么是銀行家算法?
我們可以把操作系統看作是銀行家,操作系統管理的資源相當于銀行家管理的資金,進程向操作系統請求分配資源相當于用戶向銀行家貸款。
為保證資金的安全,銀行家規定:
(1) 當一個顧客對資金的最大需求量不超過銀行家現有的資金時就可接納該顧客;
(2) 顧客可以分期貸款,但貸款的總數不能超過最大需求量;
(3) 當銀行家現有的資金不能滿足顧客尚需的貸款數額時,對顧客的貸款可推遲支付,但總能使顧客在有限的時間里得到貸款;
(4) 當顧客得到所需的全部資金后,一定能在有限的時間里歸還所有的資金.
操作系統按照銀行家制定的規則為進程分配資源,當進程首次申請資源時,要測試該進程對資源的最大需求量,如果系統現存的資源可以滿足它的最大需求量則按當前的申請量分配資源,否則就推遲分配。當進程在執行中繼續申請資源時,先測試該進程本次申請的資源數是否超過了該資源所剩余的總量。若超過則拒絕分配資源,若能滿足則按當前的申請量分配資源,否則也要推遲分配。
實際上,系統出現死鎖的概率很小,故從系統所花的代價上看,采用死鎖發生后的檢測與恢復策略要比采用死鎖發生前的預防與避免策略代價小一些。