2.1 進程的基本概念
算法
1. 程序的順序執行及其特徵數據結構
特徵:順序性、封閉性、可再現性併發
2. 程序的併發執行及其特徵異步
(1)特徵:間斷性、失去封閉性、不可再現性模塊化
(2)程序併發執行的條件——Bernstein條件:函數
設有讀集R(p_i )={a_1,a_2…a_m}和寫集W(p_i )={b_1,b_2…b_n},若程序知足如下三個條件,則程序能夠併發且具備可再現性(i≠j):R(p_i )∩W(p_j )=∅|R(p_j )∩W(p_i )=∅|W(p_i )∩W(p_j )=∅學習
3. 進程的特徵與狀態測試
進程(動態)是進程實體(靜態)的運行過程,是系統進行資源分配和調度的一個獨立單位。this
(1)進程的特徵:spa
①結構特徵:進程實體由進程控制塊PCB(Process Control Block)、程序段、相關的數據段組成
②動態性(最基本特性)③併發性④獨立性⑤異步性
(2)進程的狀態:
①三種基本狀態:就緒態、運行態、阻塞態
(進程狀態的轉換並不是均可逆,阻塞態沒法轉換爲執行態;只有執行態變爲阻塞態是主動的,其他都是被動的;進程在某一時刻僅有一種狀態)
②掛起態
引發掛起態的緣由:終端用戶的要求、父進程的請求、負荷調節的須要、操做系統的須要
進程狀態的轉換
活動就緒->靜止就緒
活動阻塞->靜止阻塞
靜止就緒->活動就緒
靜止阻塞->活動阻塞
③建立態和終止態
建立態:僅僅爲一個進程建立PCB(未分配資源,進程建立工做還沒有完成)
終止態:當一個進程到達了天然結束點,或發生了沒法克服的錯誤,或被操做系統所終結,或被其餘擁有終止權的進程所終結,該進程進入終止態。
4. 進程控制塊
(1)進程控制塊的做用:
PCB是記錄操做系統所需的、用於描述進程的當前狀況以及控制進程運行的所有信息的數據結構。PCB是進程存在的惟一標識,它常駐內存。系統將全部的PCB組織成若干鏈表或隊列,存放在操做系統專門開闢的PCB區內。
(2)進程控制塊中的信息:
①進程標識符PID(內部標識符提供給系統,外部標識符提供給用戶)
②處理機狀態:包含通用寄存器、指令寄存器、程序狀態字、用戶棧指針等寄存器中的內容
③進程調度信息:包含進程狀態、進程優先級、進程調度所需其餘信息,事件(阻塞的緣由)
④進程控制信息:包含程序和數據地址、進程同步和通訊機制、資源清單、連接指針
(3)進程控制塊的組織方式:連接方式、索引表方式
5. 進程和程序的關係
(1)進程是動態的,程序是靜態的
(2)進程是暫時的,程序是永久的
(3)進程包括程序、數據、PCB
(4)進程可建立其它進程,而程序不能造成新的程序
(5)進程具備並行性
6. 進程與做業的關係
(1)做業是用戶向計算機提交任務的任務實體。在用戶向計算機提交做業後,系統將其放入外存中的等待隊列等待執行;而進程則是完成用戶任務的執行實體,是向系統申請資源的基本單位。
(2)一個做業可有多個進程組成,且至少有一個進程組成,但一個進程不能構成多個做業。
(3)做業的概念主要用於批處理系統中,而進程的概念則用在幾乎全部的多道程序系統中。
2.2 進程控制
進程控制是進程管理中最基本的職能,它用於建立/終止一個進程,還可負責進程運行中的狀態轉換。進程控制通常由OS的內核中的原語(原語由若干條指令構成,用於完成必定功能的一個過程,它是原子操做,是不可分割的基本單位,即一個操做中的全部動做要麼全作,要麼全不作,執行過程不容許被中斷,在管態下運行,常駐內存)實現。
1. 進程的建立
(1)引發進程建立的事件:用戶登陸、做業調度、提供服務、應用請求
(2)進程的建立:調用建立原語Create( )建立新進程
①申請PCB②爲新進程申請資源(若資源不足,則進入阻塞態)③初始化PCB(初始化標識信息、處理機狀態信息、處理機控制信息,優先級通常設置爲最低)④將新進程插入就緒隊列
2. 進程的終止
(1)引發進程終止的事件:正常結束、異常結束(越界錯誤、保護錯、非法指令、特權指令錯、運行超時、等待超時、算術運算錯、I/O故障等)、外界干預(操做員或操做系統干預、父進程請求,父進程終止)
(2)進程的終止:
①根據被終止進程的標識符,從PCB集合中檢索出該進程的PCB,從中讀出該進程狀態
②若被終止進程正處於執行狀態,應當即終止該進程的執行,並置調度標誌爲真。
③若該進程還有子進程也應終止
④被終止進程的全部資源或歸還父進程,或歸還操做系統。
⑤將被終止進程從鏈表或隊列中移除,等待其餘程序來蒐集信息。
3. 進程的阻塞與喚醒(進程的自主行爲)
(1)引發進程阻塞或喚醒的事件:請求系統服務、啓動某個操做、新數據還沒有到達、無新工做可作
(2)進程阻塞過程:進程經過調用block原語阻塞本身,即若處於執行態,則終止並將PCB插入具備相同事件的阻塞隊列,再由轉調度程序從新調度,將處理機分配給另外一進程並進行切換(執行態->阻塞態)。
(3)進程喚醒過程:發現者進程經過wakeup原語喚醒被阻塞的進程,即首先把阻塞的進程從等待該事件的阻塞隊列中移除,將其PCB的現行狀態由阻塞改成就緒,而後再將該進程的PCB插入到就緒隊列中(阻塞態->就緒態)。
4. 進程的掛起與激活
(1)進程的掛起:調用suspend原語掛起進程
(2)進程的激活:調用active原語激活進程,即首先將進程從外存調入內存,更改該進程的運行狀態。再根據調度策略,檢查是否要進行從新調度。
5. 進程的切換
保存處理機上下文->更新PCB->將PCB移入相應的隊列->選擇另外一進程執行,更新其PCB->更新內存管理的數據結構->恢復處理機上下文
2.3 進程同步
1. 進程同步的基本概念
多個相互合做的進程在一些關鍵點上可能須要等待或相互交換信息的相互制約關係。
(1)兩種形式的制約關係:間接制約關係(系統資源的共享)——互斥,直接制約關係(進程之間的合做)——同步
(2)臨界資源和臨界區:
臨界資源:在一段時間內僅容許一個用戶訪問的資源
臨界區:每一個進程中訪問臨界資源的那段代碼
進入區(檢查是否能夠進入臨界區,設置標誌)
臨界區(訪問臨界資源)
退出區(消除標誌)
剩餘區
(3)同步機制應遵循的基本原則:空閒讓進,忙則等待,有限等待、讓權等待
2. 互斥實現方法
(1)軟件方法
在進入區設置和檢查一些標誌來標明是否有進程在臨界區中,若是已有進程在臨界區,則在進入區經過循環檢查進行等待,進程離開臨界區後則在退出區修改標誌。
(1) 算法一:單標誌法。
該算法設置一個公用整型變量turn,用於指示被容許進入臨界區的進程編號,即若turn=0,則容許P0進程進入臨界區。該算法可確保每次只容許一個進程進入臨界區。但兩個進程必須交替進入臨界區,若是某個進程再也不進入臨界區了,那麼另外一個進程想再次進入臨界區,則它將沒法進入臨界區(違背「空閒讓進」)。
int turn=0; P_0:{ do{ while(turn!=0); critical section; turn=1; remainder section; }while(true) } P_1:{ do{ while(turn!=1); critical section; turn=0; remainder section; }while(true) }
(2) 算法二:雙標誌法先檢查。
該算法的基本思想是在每個進程訪問臨界區資源以前,先查看一下臨界資源是否正被訪問,若正被訪問,該進程需等待;不然,進程才進入本身的臨界區。爲此,設置了一個數據flag[i],如第i個元素值爲false,表示Pi進程未進入臨界區,值爲TRUE,表示Pi進程進入臨界區。這個算法解決了「空閒讓進」的問題,但違背了「忙則等待」原則。
enum boolean{true,false}; boolean flag[2]{false,false}; P_0:{ do{ While(flag[1]); flag[0]=true; critical section; flag[0]=false; remainder section; }while(true) } P_1:{ do{ While(flag[0]); flag[1]=true; critical section; flag[1]=false; remainder section; }While(true) }
(3) 算法三:雙標誌法後檢查。
算法二是先檢測對方進程狀態標誌後,再置本身標誌,因爲在檢測和放置中可插入另外一個進程到達時的檢測操做,會形成兩個進程在分別檢測後,同時進入臨界區。爲此,算法三釆用先設置本身標誌爲true後,再檢測對方狀態標誌,若對方標誌爲true,則進程等待;不然進入臨界區。此算法能夠有效防止兩進程同時進入臨界區,但存在兩進程都進不了臨界區的問題,違背了「有限等待」的原則。
enum boolean{true,false}; boolean flag[2]{false,false}; P_0:{ do{ flag[0]=true; While(flag[1]); critical section; flag[0]=false; remainder section; }while(true) } P_1:{ do{ flag[1]=true; While(flag[0]); critical section; flag[1]=false; remainder section; }while(true) }
(4)算法四:Peterson’s Algorithm。
爲了防止兩個進程爲進入臨界區而無限期等待,又設置變量turn,指示不容許進入臨界區的進程編號,每一個進程在先設置本身標誌後再設置turn 標誌,不容許另外一個進程進入。這時,再同時檢測另外一個進程狀態標誌和不容許進入標誌,這樣能夠保證當兩個進程同時要求進入臨界區,只容許一個進程進入臨界區。此算法能夠徹底正常工做,利用flag[]解決臨界資源互斥問題,利用turn解決「飢餓現象」。
enum boolean{true,false}; boolean flag[2]{false,false}; int turn; P_0:{ do{ flag[0]=true; turn=1; While(flag[1]&&turn==1); critical section; flag[0]=false; remainder section; }while(true) } P_1:{ do{ flag[1]=true; turn=0; While(flag[0]&&turn==0); critical section; flag[1]=false; remainder section; }while(true) }
(2) 硬件辦法
本節對硬件實現的具體理解對後面的信號量的學習頗有幫助。計算機提供了特殊的硬件指令,容許對一個字中的內容進行檢測和修正,或者是對兩個字的內容進行交換等。經過硬件支持實現臨界段問題的低級方法或稱爲元方法。
(1) 中斷屏蔽方法
當一個進程正在使用處理機執行它的臨界區代碼時,要防止其餘進程再進入其臨界區訪問的最簡單方法是禁止一切中斷髮生,或稱之爲屏蔽中斷、關中斷。由於CPU只在發生中斷時引發進程切換,這樣屏蔽中斷就能保證當前運行進程將臨界區代碼順利地執行完,從而保證了互斥的正確實現,而後再執行開中斷。其典型模式爲:
…
關中斷;
臨界區;
開中斷;
…
這種方法限制了處理機交替執行程序的能力,所以執行的效率將會明顯下降。對內核來講,當它執行更新變量或列表的幾條指令期間關中斷是很方便的,但將關中斷的權力交給用戶則很不明智,若一個進程關中斷以後再也不開中斷,則系統可能會所以終止。
(2) 硬件指令方法
①TestAndSet指令:這條指令是原子操做,即執行該代碼時不容許被中斷。其功能是讀出指定標誌後把該標誌設置爲真。指令的功能描述以下:
boolean TestAndSet(boolean *lock){ boolean old; old = *lock; *lock=true; return old; }
能夠爲每一個臨界資源設置一個共享布爾變量lock,表示資源的兩種狀態:true表示正被佔用,初值爲false。在進程訪問臨界資源以前,利用TestAndSet檢查和修改標誌lock;如有進程在臨界區,則重複檢查,直到進程退出。利用該指令實現進程互斥的算法描述以下:
while TestAndSet (& 1 ock); // 進程的臨界區代碼段; lock=false; // 進程的其餘代碼
②Swap指令:該指令的功能是交換兩個字節的內容。其功能描述以下。
Swap(boolean *a,boolean *b){ boolean temp; Temp=*a; *a = *b; *b = temp; }
注意:以上對TestAndSet和Swap指令的描述僅僅是功能實現,並不是軟件實現定義,事實上它們是由硬件邏輯直接實現的,不會被中斷。
應爲每一個臨界資源設置了一個共享布爾變量lock,初值爲false;在每一個進程中再設置一個局部布爾變量key,用於與lock交換信息。在進入臨界區以前先利用Swap指令交換lock 與key的內容,而後檢查key的狀態;有進程在臨界區時,重複交換和檢查過程,直到進程退出。利用Swap指令實現進程互斥的算法描述以下:
key=true; while(key!=false) Swap(&lock,&key); // 進程的臨界區代碼段; lock=false; // 進程的其餘代碼;
硬件方法的優勢:適用於任意數目的進程,不論是單處理機仍是多處理機;簡單、容易驗證其正確性。能夠支持進程內有多個臨界區,只需爲每一個臨界區設立一個布爾變量。
硬件方法的缺點:進程等待進入臨界區時要耗費處理機時間,不能實現讓權等待。從等待進程中隨機選擇一個進入臨界區,有的進程可能一直選不上,從而致使「飢餓」現象。
3. 信號量(Semaphore)機制
(1)整型信號量(存在「忙等」問題,未遵循「讓權等待」準則):即整型信號量被定義爲一個用於表示資源數目的整型量S,它除初始化外,僅能經過兩個原子操做wait(S)和signal(S)來訪問,即PV操做。
wait(S){ while (S<=0); S=S-1; } signal(S){ S=S+1; }
在wait操做中只要信號量S<=0,就會不斷進行測試,所以該機制未遵循「讓權等待」的準則,存在「忙等」的狀態。
(2)記錄型信號量:
數據結構:
typedef struct{ int value; struct process *L; } semaphore;
wait(S)和signal(S)操做:
void wait (semaphore S) { //至關於申請資源 S.value--; if(S.value<0) { add this process to S.L; block(S.L); } } void signal (semaphore S) { //至關於釋放資源 S.value++; if(S.value<=0){ remove a process P from S.L; wakeup(P); } }
S爲資源信號量:當|S.value|<=0時表示尚有等待該資源的進程被阻塞,應喚醒;當S.value的初值爲1時,表示只容許一個進程訪問臨界資源,此時的信號量變爲互斥信號量。
(3)AND型信號量
將進程在整個運行過程當中所須要的全部資源,一次性分配給進程,待進程使用完後一塊兒釋放,只要尚有一個資源未能分配給進程,其餘全部可能爲之分配的資源也不分配給它。
同時wait操做(Simultaneous wait,又稱ADD同步):
void Swait(semaphore S1,semaphore S2,…,semaphore Sn,int n){ if (S1>1 && … && Sn>1 ){ for(int i=1;i<=n;i++){ Si - -; } } else{ place the process in the waiting queue associated with first Si found with Si<1,and set the program count of this process to the beginning of Swait operation } } void Ssignal(semaphore S1,semaphore S2,…,semaphore Sn,int n){ for (int i=1;i<=n;i++){ Si++; Remove all the process waiting in the queue associate with Si into ready queue } }
(4)信號量集
Swait操做(S爲信號量,d爲需求值,t爲下限值)
void Swait(semaphore S1,semaphore d1,semaphore t1,…,semaphore Sn,semaphore dn,semaphore tn,int n){ if(Si>t1 && … &&Sn>tn){ for (int i=1;i<=n;i++){ Si-=di; } } else{ place the process in the waiting queue associated with first Si found with Si<ti,and set the program count of this process to the beginning of Swait operation } } void Ssignal(semaphore S1,semaphore d1…,semaphore Sn,semaphore dn,int n) for (int i=1;i<=n;i++){ Si+=di; Remove all the process waiting in the queue associate with Si into ready queue } }
4. 信號量的應用
(1)利用信號量實現進程互斥
semaphore S = 1; //初化信號量 P1 ( ) { … P(S); // 準備開始訪問臨界資源,加鎖 進程P1的臨界區 V(S); // 訪問結束,解鎖 … } P2( ) { … P(S); //準備開始訪問臨界資源,加鎖 進程P2的臨界區; V(S); // 訪問結束,解鎖 … }
(2)利用信號量實現同步
semaphore S = 0; //初始化信號量 P1 ( ) { … x; //語句x V(S); //告訴進程P2,語句乂已經完成 } P2(){ … P(S) ; //檢查語句x是否運行完成 y; // 檢查無誤,運行y語句 … }
(3)利用信號量實現前驅關係
semaphore al=a2=bl=b2=c=d=e=0;//初始化信號量 S1(){ ... V(al); V(a2) ; //S1已經運行完成 } S2(){ P(a1); //檢查S1是否運行完成 ... V(bl); V(b2); // S2已經運行完成 } S3(){ P(a2); //檢查S1是否已經運行完成 ... V(c); //S3已經運行完成 } S4(){ P(b1); //檢查S2是否已經運行完成 ... V(d); //S4已經運行完成 } S5(){ P(b2); //檢查S2是否已經運行完成 ... V(e); // S5已經運行完成 } S6(){ P(c); //檢查S3是否已經運行完成 P(d); //檢查S4是否已經運行完成 P(e); //檢查S5是否已經運行完成 ...; }
分析進程同步和互斥問題的方法步驟:
1) 關係分析。找出問題中的進程數,而且分析它們之間的同步和互斥關係。同步、互斥、前驅關係直接按照上面例子中的經典範式改寫。
2) 整理思路。找出解決問題的關鍵點,而且根據作過的題目找出解決的思路。根據進程的操做流程肯定P操做、V操做的大體順序。
3) 設置信號量。根據上面兩步,設置須要的信號量,肯定初值,完善整理。
5. 管程機制
(1)管程:表明共享資源的數據結構,以及對該共享數據結構實施操做的一組過程所組成的資源管理程序,共同構成了一個操做系統的資源管理模塊。
(2)管程的組成:管程的名稱、局部於管程內部的共享數據結構的說明、對該數據結構進行操做的一組過程、對於局部於該管程內部的共享數據設置初值的語句
(3)管程的語法描述:
type monitor_name=MONITOR; <共享變量說明> define <(能被其餘模塊引用的)過程名列表> use<(要調用的本模塊外定義的)過程名列表> procedure<過程名>(<形式參數表>) begin …… end; …… function<函數名>(<形式參數表>):值類型: begin …… end; …… begin <管程的局部數據初始化語句序列> end
爲了解決一個進程調用了管程,在管程中被阻塞或掛起,使其它進程沒法進入管程,而被迫等待的問題,引入了條件變量condition,它也是抽象數據類型,其形式爲Var x,y:condition;每一個條件變量保存了一個鏈表,用於記錄因該條件變量而阻塞的全部進程,同時提供x.wait和x.signal兩個操做。
①x.wait:正在調用管程的進程因x條件而被阻塞或掛起,則調用x.wait將本身插入到x條件的等待隊列上,並釋放管程,直到x條件變化。
②x.signal:正在調用管程的進程發現條件x發生了變化,則調用x.signal,從新啓動一個因x條件而阻塞或掛起的進程。
此時有兩個進程在管程內,有兩種解決方式:
①P等待,直至Q離開管程或等待另外一條件
②Q等待,直至P離開管程或等待另外一條件
Hoare採用了第一種,Hansan選擇了兩者的折衷。
(4)管程的特性:
①局部於管程內部的數據結構,僅能被管程內部的過程所訪問,任何管程外的過程都不能訪問它。局部於管程內的過程也僅能訪問管程內的數據結構。
②一個進程只有經過調用管程內的過程才能進入管程訪問共享數據。
③每次僅容許一個進程在管程內執行某個內部進程,即進程互斥地經過調用內部過程進入管程。其它想進入管程的過程必須等待,並阻塞在等待序列。
從程序設計語言角度來看,管程有模塊化、抽象數據類型、信息隱蔽的特色。