操作系统概念学习笔记 11 进程同步(一)
操作系統(tǒng)概念學(xué)習(xí)筆記 11
進(jìn)程同步(一)
互相協(xié)作的進(jìn)程之間有共享的數(shù)據(jù),于是這里就有一個(gè)并發(fā)情況下,如何確保有序操作這些數(shù)據(jù)、維護(hù)一致性的問(wèn)題,即進(jìn)程同步。
從底層到高級(jí)應(yīng)用,同步機(jī)制依次有臨界區(qū)、信號(hào)量、管程、原子事務(wù)。
多個(gè)進(jìn)程并發(fā)訪問(wèn)和操作同一數(shù)據(jù)且執(zhí)行結(jié)果與訪問(wèn)發(fā)生的特定順序有關(guān),稱之為競(jìng)爭(zhēng)條件(race condition)。
臨界區(qū)(critical section)
每個(gè)進(jìn)程有一個(gè)代碼段稱為臨界區(qū)(critical section),在該區(qū)中進(jìn)程可能改變共同變量、更新一個(gè)表或?qū)懸粋€(gè)文件等。這種系統(tǒng)的重要特征是當(dāng)一個(gè)進(jìn)程進(jìn)入臨界區(qū),沒有其他進(jìn)程可被允許在臨界區(qū)內(nèi)執(zhí)行,即沒有兩個(gè)進(jìn)程可同時(shí)在臨界區(qū)內(nèi)執(zhí)行。
臨界區(qū)問(wèn)題(critical-section problem)是設(shè)計(jì)一個(gè)以便進(jìn)程協(xié)作的協(xié)議。每個(gè)進(jìn)程必須請(qǐng)求允許進(jìn)入其臨界區(qū)。實(shí)現(xiàn)請(qǐng)求的代碼段稱為進(jìn)入?yún)^(qū)(entry section),臨界區(qū)之后可有退出區(qū)(exit section),其他代碼段成為剩余區(qū)(remainder section)。
一個(gè)典型進(jìn)程Pi的通用結(jié)構(gòu):
do{進(jìn)入?yún)^(qū)臨界區(qū)退出區(qū)剩余區(qū)}while(TRUE)臨界區(qū)問(wèn)題的解答必須滿足三項(xiàng)要求:
(1)互斥(mutual exclusion):
如果進(jìn)程Pi在其臨界區(qū)內(nèi)執(zhí)行,那么其他進(jìn)程都不能在其臨界區(qū)內(nèi)執(zhí)行;(2)前進(jìn)(progress):
如果沒有進(jìn)程在其臨界區(qū)內(nèi)執(zhí)行且有進(jìn)程需進(jìn)入臨界區(qū),那么只有那么不在剩余區(qū)內(nèi)執(zhí)行的進(jìn)程可參加選擇,以確定誰(shuí)能下一個(gè)進(jìn)入臨界區(qū),且這種選擇不能無(wú)限推遲;(3)有限等待(bounded waiting):
從一個(gè)進(jìn)程做出進(jìn)入臨界區(qū)的請(qǐng)求,直到該請(qǐng)求允許為止,其他進(jìn)程允許進(jìn)入其臨界區(qū)內(nèi)的次數(shù)有上限。
一個(gè)操作系統(tǒng),在某個(gè)時(shí)刻,可同時(shí)存在有多個(gè)處于內(nèi)核模式的活動(dòng)進(jìn)程,因此實(shí)現(xiàn)操作系統(tǒng)的內(nèi)核代碼,會(huì)存在競(jìng)爭(zhēng)條件。內(nèi)核開發(fā)人員有必要確保其操作系統(tǒng)不會(huì)產(chǎn)生競(jìng)爭(zhēng)條件。
有兩種方法用于處理操作系統(tǒng)內(nèi)的臨界區(qū)問(wèn)題:
搶占內(nèi)核(preemptive kernel)與非搶占內(nèi)核(nonpreemptive kernel):
搶占內(nèi)核允許處于內(nèi)核模式的進(jìn)程被搶占。
非搶占內(nèi)核不允許內(nèi)核模式的進(jìn)程被搶占。
非搶占內(nèi)核的內(nèi)核數(shù)據(jù)結(jié)構(gòu)從根本上不會(huì)導(dǎo)致競(jìng)爭(zhēng)條件,對(duì)于搶占內(nèi)核需要認(rèn)真設(shè)計(jì)以確保其內(nèi)核數(shù)據(jù)結(jié)構(gòu)不會(huì)導(dǎo)致競(jìng)爭(zhēng)條件。
但搶占內(nèi)核更受歡迎,因?yàn)閾屨純?nèi)核更適合實(shí)時(shí)編程,因?yàn)樗茉试S實(shí)時(shí)進(jìn)程搶占處于內(nèi)核模式運(yùn)行的其他進(jìn)程。再者,搶占內(nèi)核的響應(yīng)更快,因?yàn)樘幱趦?nèi)核模式的進(jìn)程在釋放CPU之前不會(huì)運(yùn)行過(guò)久。
Peterson算法
Peterson算法是一種經(jīng)典的基于軟件的臨界區(qū)問(wèn)題算法,不能確保正確運(yùn)行。
Peterson算法適用于兩個(gè)進(jìn)程在臨界區(qū)與剩余區(qū)間交替執(zhí)行,為了方便,當(dāng)使用Pi時(shí),Pj來(lái)標(biāo)示另一個(gè)進(jìn)程,即j == i - 1。Peterson算法需要在兩個(gè)進(jìn)程之間共享兩個(gè)數(shù)據(jù)項(xiàng):
int turn;boolean flag[2];變量turn表示哪個(gè)進(jìn)程可以進(jìn)入其臨界區(qū),即如果turn==i,那么進(jìn)程Pi允許在其臨界區(qū)內(nèi)執(zhí)行。
數(shù)組flag表示哪個(gè)進(jìn)程想要進(jìn)入臨界區(qū),如果flag[i]為true,即Pi想進(jìn)入其臨界區(qū)。
//進(jìn)程Pi的Peterson算法 do{flag[i]=TRUE;turn=j;while(flag[j]&&turn==j);臨界區(qū)flag[i]=FALSE;剩余區(qū)}while(TRUE)可以證明,滿足三項(xiàng)要求。
硬件同步
通過(guò)要求臨界區(qū)用鎖來(lái)防護(hù),就可以避免競(jìng)爭(zhēng)條件,即一個(gè)進(jìn)程在進(jìn)入臨界區(qū)之前必須得到鎖,而其退出臨界區(qū)時(shí)釋放鎖。
do{請(qǐng)求鎖臨界區(qū)釋放鎖剩余區(qū)}while(TRUE)硬件特性能簡(jiǎn)化編程任務(wù)且提高系統(tǒng)效率。
對(duì)于單處理器環(huán)境,臨界區(qū)問(wèn)題可簡(jiǎn)單地加以解決:在修改共享變量時(shí)要禁止中斷出現(xiàn)。這樣其他指令不可能執(zhí)行,所以共享變量也不會(huì)被意外修改。這種方法通常為搶占式內(nèi)核所采用。
在多處理器環(huán)境下,這種解決方法是不可行的,低效且影響系統(tǒng)時(shí)鐘。
特殊硬件指令以允許能原子地(不可中斷的)檢查和修改字的內(nèi)容或交換兩個(gè)字的內(nèi)容。如TestAndSet(),當(dāng)兩個(gè)指令同時(shí)執(zhí)行在不同的CPU上,那么它們會(huì)按任意順序來(lái)順序執(zhí)行。
TestAndSet指令定義:
boolean TestAndSet(boolean *target){boolean rv=*target;*target=TRUE;return rv;}使用TestAndSet的互斥實(shí)現(xiàn),聲明一個(gè)Boolean變量lock,初始化為false
do{while(TestAndSetLock(&lock));//do nothing//critical sectionlock=FALSE;//remainder section}while(TRUE);使用TestAndSet的有限等待互斥:任何等待進(jìn)入臨界區(qū)的進(jìn)程只需要等待n-1次。
boolean waiting[i] = TRUE;
boolean lock;
初始化為false
Swap指令的定義:
void Swap(boolean *a,boolean *b){booleab temp=*a;*a=*b;*b=temp;}使用Swap的互斥實(shí)現(xiàn):key為每個(gè)進(jìn)程局部變量,lock為全局變量,初始化為false
do{key=TRUE;while(key==TRUE)Swap(&lock,&key);//critical sectionlock=FALSE;//remainder section}while(TRUE);然而,對(duì)于硬件設(shè)計(jì)人員,在多處理器上實(shí)現(xiàn)原子指令TestAndSet并不簡(jiǎn)單。
信號(hào)量(semaphore)
應(yīng)用層面解決臨界區(qū)問(wèn)題:信號(hào)量
信號(hào)量S是個(gè)整數(shù)變量,除了初始化外,它只能通過(guò)兩個(gè)標(biāo)準(zhǔn)原子操作:wait()和signal()來(lái)訪問(wèn)。即P和V。
wait()的定義可表示為:
wait(S){while(S<=0);//no-opS--;}signal()的定義可表示為:
signal(S){S++;}在wait()和signal()操作中,對(duì)信號(hào)量整型值的修改必須不可分地執(zhí)行。
用法:
通常操作系統(tǒng)區(qū)分計(jì)數(shù)信號(hào)量和二進(jìn)制信號(hào)量。計(jì)數(shù)信號(hào)量的值域不受限制,而二進(jìn)制信號(hào)量的值只能為0或1,有的系統(tǒng),。由于二進(jìn)制信號(hào)量是互斥的,因而可以將其應(yīng)用于處理多進(jìn)程的臨界區(qū)問(wèn)題。
使用信號(hào)量的互斥實(shí)現(xiàn):n個(gè)進(jìn)程共享信號(hào)量mutex,初始值1
do{wait(mutex);//critical sectionsignal(mutex);//remainder section}while(TRUE);計(jì)數(shù)信號(hào)量可以用來(lái)控制訪問(wèn)具有若干個(gè)實(shí)例的某種資源。該信號(hào)量初始化為可用資源的數(shù)量。當(dāng)每個(gè)進(jìn)程需要使用資源時(shí),需要對(duì)該信號(hào)量執(zhí)行wait()操作。當(dāng)進(jìn)程釋放資源時(shí),需要對(duì)該信號(hào)執(zhí)行signal()操作。
可以用信號(hào)量來(lái)解決各種同步問(wèn)題。如先執(zhí)行Pi的S1語(yǔ)句,然后再執(zhí)行Pj的S2語(yǔ)句,可以通向一個(gè)信號(hào)量,初始化為0,然后執(zhí)行S1完,執(zhí)行signal(),在執(zhí)行S2前,執(zhí)行wait()。
實(shí)現(xiàn):
信號(hào)量的主要缺點(diǎn)是要求忙等待(busy waiting)。即在進(jìn)入代碼段中連續(xù)地循環(huán)。忙等待浪費(fèi)了CPU時(shí)鐘,這種類型的信號(hào)量也稱為自旋鎖(spinlock),這是因?yàn)檫M(jìn)程在其等待鎖的時(shí)還在運(yùn)行(自旋鎖有其優(yōu)點(diǎn),進(jìn)程在等待鎖時(shí)不進(jìn)行上下文切換,而上下文切換可能需要花費(fèi)相當(dāng)長(zhǎng)的時(shí)間。因此如果鎖占用的時(shí)間短,那么鎖就有用了,自旋鎖常用于多處理器系統(tǒng)中,這樣一個(gè)線程在一個(gè)處理器自旋時(shí),另一線程可在另一個(gè)處理器上在其臨界區(qū)內(nèi)執(zhí)行)
為克服這一缺點(diǎn),修改wait()和signal()的定義,信號(hào)量值不為正時(shí),不是忙等而是阻塞自己,阻塞操作將一個(gè)進(jìn)程放入到與信號(hào)量相關(guān)的等待隊(duì)列中,并將該進(jìn)程的狀態(tài)切換成等待狀態(tài),接著,控制轉(zhuǎn)到CPU調(diào)度程序,以選擇另一個(gè)進(jìn)程來(lái)執(zhí)行。
被阻塞在等待信號(hào)S上的進(jìn)程,可以在其他進(jìn)程執(zhí)行signal()的時(shí)候操作之后重新被執(zhí)行,該進(jìn)程的重新執(zhí)行是通過(guò)wakeup()操作來(lái)進(jìn)行的將進(jìn)程從等待狀態(tài)切換到就緒狀態(tài)。接著進(jìn)程被放到就緒隊(duì)列中。
因而將信號(hào)量定義為如下:
typedef struct{int value; //記錄了這個(gè)信號(hào)量的值 struct process *list; //儲(chǔ)存正在等待這個(gè)信號(hào)量的進(jìn)程(PCB鏈表指針)}semaphore;每個(gè)信號(hào)量都有一個(gè)整型值和一個(gè)進(jìn)程鏈表,當(dāng)一個(gè)進(jìn)程必須等待信號(hào)量時(shí),就加入到進(jìn)程鏈表上,操作signal()會(huì)從等待進(jìn)程鏈表中取一個(gè)進(jìn)程以喚醒。
wait()實(shí)現(xiàn):
wait(semaphore *S){S->value--;if(S->value <0){ //沒有資源add this process to S->list; //進(jìn)入等待隊(duì)列 block(); //堵塞 }}signal()實(shí)現(xiàn):
signal(semaphore *S){S->value++;if(S->value<=0){ //上面++后,S仍然還<=0,說(shuō)明資源供不應(yīng)求,等待者還有很多,于是喚醒等待隊(duì)列中的一個(gè)remove a process P from S->list;wakeup(P); //切換到就緒狀態(tài) }}操作block()掛起調(diào)用他的進(jìn)程。
操作wakeup(P)重新啟動(dòng)阻塞進(jìn)程P的執(zhí)行。
這兩個(gè)操作都是由操作系統(tǒng)作為基本系統(tǒng)調(diào)用來(lái)提供的。
在具有忙等的信號(hào)量經(jīng)典定義下,信號(hào)量的值絕對(duì)不能為負(fù)數(shù),但是本實(shí)現(xiàn)可能造成信號(hào)量為負(fù)值。如果信號(hào)量為負(fù)值,那么其絕對(duì)值就是等待該信號(hào)量的進(jìn)程的個(gè)數(shù)。
等待進(jìn)程的鏈表可以利用進(jìn)程控制塊PCB中的一個(gè)鏈接域來(lái)加以輕松實(shí)現(xiàn)。即每個(gè)信號(hào)量包括一個(gè)整型值和一個(gè)PCB鏈表的指針。
信號(hào)量的關(guān)鍵之處是它們?cè)拥膱?zhí)行。必須確保沒有兩個(gè)進(jìn)程能同時(shí)對(duì)一個(gè)信號(hào)量進(jìn)行操作,在單處理器情況下,可以在執(zhí)行wait()和signal()的時(shí)候簡(jiǎn)單的關(guān)閉中斷,保證只有當(dāng)前進(jìn)程進(jìn)行。
多處理器下,若禁止所有CPU的中斷,則會(huì)嚴(yán)重影響性能,SMP系統(tǒng)必須提供其他加鎖技術(shù)(如自旋鎖),以確保wait()與signal()可原子地執(zhí)行。
死鎖與饑餓:
具有等待隊(duì)列的信號(hào)量的實(shí)現(xiàn)可能會(huì)導(dǎo)致這樣的情況:
兩個(gè)或多個(gè)進(jìn)程無(wú)限地等待一個(gè)事件,而該事件只能由這些等待進(jìn)程之一來(lái)產(chǎn)生。這里的事件是signal()操作的執(zhí)行。當(dāng)出現(xiàn)這樣的狀態(tài)時(shí),這些進(jìn)程就稱為死鎖(deadlocked)。
例如,一個(gè)由P1 P2 組成的系統(tǒng),每個(gè)都訪問(wèn)共享的信號(hào)量S和Q,SQ初值均為1:
P0:
wait(S);wait(Q);//......signal(S);signal(Q);P1:
wait(Q);wait(S);//......signal(Q);signal(S);假設(shè),P0執(zhí)行wait(S),接著P1執(zhí)行wait(Q),P0再執(zhí)行wait(Q)時(shí),必須等待,直到P1執(zhí)行signal(Q),而此時(shí)P1也在等待P0執(zhí)行signal(S),兩個(gè)操作都不能進(jìn)行,P0和P1就死鎖了。
與死鎖相關(guān)的另一個(gè)問(wèn)題是無(wú)限期阻塞(indefinite blocking)或饑餓(starvation):即進(jìn)程在信號(hào)量?jī)?nèi)無(wú)限期等待。
舉個(gè)例子來(lái)理解死鎖與饑餓的區(qū)別:
- 死鎖(deadlock)
指的是兩個(gè)或者兩個(gè)以上的進(jìn)程相互競(jìng)爭(zhēng)系統(tǒng)資源,導(dǎo)致進(jìn)程永久阻塞。
例如:
1、桌子上有慢慢一桌子的美食,但是只有一雙筷子。
2、甲拿了一根,然后在找另一根。
3、乙拿了一根,然后也在找另一根。
4、因?yàn)樗麄兌颊莆樟藢?duì)方必需的資源,導(dǎo)致最后他們倆誰(shuí)都吃不到美食。
- 饑餓(starvation)
指的是等待時(shí)間已經(jīng)影響到進(jìn)程運(yùn)行,此時(shí)成為饑餓現(xiàn)象。如果等待時(shí)間過(guò)長(zhǎng),導(dǎo)致進(jìn)程使命已經(jīng)沒有意義時(shí),稱之為“餓死”。
例如:
1、小明要告訴媽媽明天開家長(zhǎng)會(huì)。
2、小明媽媽因?yàn)楣ぷ魈?#xff0c;在公司加班,沒有回家。
3、于是第二天,小明的媽媽就錯(cuò)過(guò)了家長(zhǎng)會(huì)。(“餓死”)
4、其實(shí)小明的媽媽沒有出現(xiàn)“死鎖”。只是小明的優(yōu)先級(jí)過(guò)低,不如工作重要。
經(jīng)典同步問(wèn)題
有限緩存問(wèn)題—生產(chǎn)者消費(fèi)問(wèn)題:
假設(shè)緩沖池有n個(gè)緩沖項(xiàng),每個(gè)緩沖項(xiàng)能存在一個(gè)數(shù)據(jù)項(xiàng)。信號(hào)量mutex提供了對(duì)緩沖池訪問(wèn)的互斥要求,并初始化為1。信號(hào)量empty和full分別用來(lái)表示空緩沖項(xiàng)和滿緩沖項(xiàng)的個(gè)數(shù),信號(hào)量empty初始化為n;而信號(hào)量full初始化為0。
生產(chǎn)者進(jìn)程結(jié)構(gòu):
do{…//produce an item in next p…wait(empty);wait(mutex);…//add next p to buffer…signal(mutex);signal(full);}while(TRUE);消費(fèi)者進(jìn)程結(jié)構(gòu):
do{wait(full);wait(mutex);…//remove an item from buffer to next c…signal(mutex);signal(empty);…//consume the item in next c…}while(TRUE);讀者-寫者問(wèn)題:
只讀數(shù)據(jù)庫(kù)的進(jìn)程稱為讀者;更新(讀和寫)數(shù)據(jù)庫(kù)的稱為寫者。
第一讀者-寫者問(wèn)題:要求沒有讀者需要保持等待除非已有一個(gè)寫者已獲得允許已使用共享數(shù)據(jù)庫(kù)。換句話說(shuō),沒有讀者會(huì)因?yàn)橐粋€(gè)寫者在等待而會(huì)等待其他讀者的完成。
第二讀者-寫者問(wèn)題:要求一旦寫者就緒,那么寫者會(huì)盡可能快得執(zhí)行其寫操作。換句話說(shuō),如果一個(gè)寫者等待訪問(wèn)對(duì)象,那么不會(huì)有新讀者開始讀操作。
對(duì)于這兩個(gè)問(wèn)題的解答可能導(dǎo)致饑餓問(wèn)題。對(duì)第一種情況,寫者可能饑餓;對(duì)第二種情況,讀者可能饑餓。
對(duì)于第一讀者-寫者問(wèn)題的解決:
讀者進(jìn)程共享以下數(shù)據(jù)結(jié)構(gòu):
semaphore mutex, wrt;int readcount;信號(hào)量mutex和wrt初始化為1,readcount初始化為0,信號(hào)量wrt為讀者和寫者進(jìn)程所共有。信號(hào)量mutex用于確保在更新變量readcount時(shí)的互斥。變量readcount用來(lái)跟蹤有多少進(jìn)程正在讀對(duì)象。信號(hào)量wrt供寫者作為互斥信號(hào)量,它為第一個(gè)進(jìn)入臨界區(qū)和最后一個(gè)離開臨界區(qū)的讀者所使用,而不被其他讀者所使用。
寫者進(jìn)程結(jié)構(gòu):
do{wait(wrt);…;//writing is performed…;signal(wrt);}while(TRUE);讀者進(jìn)程結(jié)構(gòu):
do{wait(mutex);readcount++;if(readcount==1)wait(wrt);signal(mutex);…;//reading is performed…;wait(mutex);readcount--;if(readcount==0)signal(wrt);signal(mutex);}while(TRUE);推廣為讀寫鎖。
在以下情況下最為有用:
一是,當(dāng)可以區(qū)分哪些進(jìn)程只需要讀共享數(shù)據(jù),哪些進(jìn)程只需要寫共享數(shù)據(jù);
二是,當(dāng)讀者進(jìn)程數(shù)比寫進(jìn)程多時(shí)。
哲學(xué)家進(jìn)餐問(wèn)題:
拿起與他相近的兩只筷子,一個(gè)哲學(xué)家一次只能拿起一只筷子,同時(shí)有兩只筷子時(shí),就能吃,吃完,會(huì)放下兩只筷子。
一種簡(jiǎn)單的方法,每只筷子都用一個(gè)信號(hào)量來(lái)表示。一個(gè)哲學(xué)家通過(guò)執(zhí)行wait()操作試圖獲取相應(yīng)的筷子,他會(huì)通過(guò)執(zhí)行signal()操作以釋放相應(yīng)的筷子。
共享數(shù)據(jù)為:semaphore chopstick[5];其中所有chopstick的元素初始化為1。
哲學(xué)家i的結(jié)構(gòu):
do{wait(chopstick[i]);wait(chopstick[(i+1)%5]);…;//eat…;signal(chopstick[i]);signal(chopstick[(i+1)%5]);…;//think…;}while(TRUE);但這種方法會(huì)發(fā)生死鎖,例如,所有哲學(xué)家同時(shí)饑餓,且同時(shí)拿起左邊的筷子。
多種可以解決死鎖的方法:
①最多只允許4個(gè)哲學(xué)家同時(shí)坐在桌子上;
②只有兩只筷子都可用時(shí)才允許一個(gè)哲學(xué)家拿起它們(他必須在臨界區(qū)內(nèi)拿起兩只筷子);
③使用非對(duì)稱解決方法,即技術(shù)哲學(xué)家先拿起左邊的筷子,接著拿起右邊的筷子,而偶數(shù)哲學(xué)家先拿起右邊的筷子,接著拿起左邊的筷子。
總結(jié)
以上是生活随笔為你收集整理的操作系统概念学习笔记 11 进程同步(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 位于运算符:
- 下一篇: FreeBsdb FAMP Lamp环境