亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解C語言進程同步機制

 更新時間:2020年06月17日 16:32:16   作者:李子樹_  
這篇文章主要介紹了詳解C語言進程同步機制的的相關(guān)資料,文中代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下

本文是對進程同步機制的一個大總結(jié)(9000+字吐血總結(jié)),涵蓋面非常的全,包括了進程同步的一些概念、軟件同步機制、硬件同步機制、信號量機制和管程機制,對每種機制結(jié)合代碼做了詳細(xì)的介紹,并且對瑣碎的知識點和概念解釋的非常清晰。

​ 在前面的博客中講述了進程的狀態(tài)及其狀態(tài)的轉(zhuǎn)換,每種狀態(tài)的含義和轉(zhuǎn)換的原因。同樣我們也知道,在OS引入了進程后,可以使系統(tǒng)中的多道程序可以并發(fā)的執(zhí)行,進程的并發(fā)執(zhí)行一方面極大的提高了系統(tǒng)的資源利用率和吞吐量,但是另一方面卻使系統(tǒng)變得更加復(fù)雜,如果不能采取有效的措施,對多個進程的并發(fā)執(zhí)行進行妥善的管理,必然會因為這些進程對系統(tǒng)資源的無序爭奪給系統(tǒng)造成混亂,致使每次的處理結(jié)果顯現(xiàn)出不可再現(xiàn)性。

對于上面的問題,大家想一想這么一個場景,如果我們在買火車票(just for 舉栗子)時,沒有排隊這個機制,大家亂糟糟的圍在售票員旁邊,手里舉著錢大叫來一張到xxx的硬座、來張到xxx的臥鋪。。。咦,不寒而栗、可怕、腦殼痛。但是如果我們有序的排隊購票,大家就都可以快速的買到自己想要的通往幸福的車票。

進程同步機制就是這么一個保障OS中多個進程能夠有條不紊的運行的“規(guī)則”。本文中,我們將會詳細(xì)的介紹幾種進程同步機制。(本章中所講的OS是單處理機系統(tǒng),多處理機系統(tǒng)中的情況過于復(fù)雜,不利于理解)

1.進程同步的幾個重要概念

 進程同步機制的主要任務(wù),是對多個相關(guān)的進程在執(zhí)行次序上進行協(xié)調(diào),使并發(fā)執(zhí)行的諸多進程之間能夠按照一定的規(guī)則共享系統(tǒng)資源,并能很好的相互合作,從而是程序之間的執(zhí)行具有可再現(xiàn)性。

​進程間的兩種制約關(guān)系:

  1. 間接相互制約(互斥):因為進程在并發(fā)執(zhí)行的時候共享臨界資源而形成的相互制約的關(guān)系,需要對臨界資源互斥地訪問;
  2. 直接制約關(guān)系(同步):多個進程之間為完成同一任務(wù)而相互合作而形成的制約關(guān)系。

臨界資源:只同一時刻只允許一個進程可以訪問的資源稱之為臨界資源,諸進程之間應(yīng)采取互斥方式,實現(xiàn)對臨界資源的共享。比如打印機、磁帶機等都是臨界資源。我們通過打印機來說明為什么臨界資源同一時刻只允許一個進程使用,假設(shè)同一時刻A、B進程同時訪問打印機,兩個進程同時執(zhí)行打印任務(wù),因為進程的并發(fā)性,最后可能導(dǎo)致的就是打印機打出來的內(nèi)容就是混雜著兩方的文字,這樣得到的打印結(jié)果既不是A進程想要的也不是B進程想要的,只會造成資源的浪費。

​臨界區(qū):進程中訪問臨界資源的那段代碼。顯然若能保證諸進程互斥的進入自己的臨界區(qū),便可實現(xiàn)進程間對臨界資源的互斥訪問。因為每個進程每個進程在進入臨界區(qū)之前,應(yīng)先對欲訪問的臨界資源的“大門”狀態(tài)進行檢測(主要檢查該臨界資源是否有進程正在訪問,如果此時臨界資源未被訪問,對應(yīng)的“大門”是敞開的狀態(tài)),如果“大門”敞開,進程便可進入臨界區(qū),并將臨界區(qū)的“大門”關(guān)上;否則就表示有進程在臨界區(qū)內(nèi),當(dāng)前進程無法進入臨界區(qū)。

​指令:指令就是機器語言的一個語句,它是一組有意義的二進制代碼。因為是機器語言的一條指令,所以指令就可以等價于是原子性的,只有在執(zhí)行完一條指令后才會去響應(yīng)中斷(如果有的話)。

​原語:由若干條指令組成的,用戶完成一定功能的一個過程。原語操作的一個特點就是“原子操作”,因此原語在執(zhí)行的過程中不允許被中斷。原子操作在系統(tǒng)態(tài)下執(zhí)行,常駐內(nèi)存。

​同步機制應(yīng)遵循的規(guī)則

  1. 空閑讓進:當(dāng)臨界區(qū)的“大門”敞開時,應(yīng)當(dāng)允許一個請求的進入臨界區(qū)的進程立即進入臨界區(qū);
  2. 忙則等待:當(dāng)臨界區(qū)的“大門”關(guān)閉時,因而其他試圖進入臨界區(qū)的進程必須等待,以保證對臨界資源的互斥訪問;
  3. 有限等待:對要求進入臨界區(qū)的進程,應(yīng)保證在有限的時間能進入自己的臨界區(qū),一面陷入“死等”狀態(tài);
  4. 讓權(quán)等待:當(dāng)進程不能進入自己的臨界區(qū)時,應(yīng)立即釋放處理機,以免進程陷入“忙等”狀態(tài)。

在這里解釋一下“死等”和“忙等”兩個狀態(tài),因為這兩個狀態(tài)在我們看來似乎是沒有什么區(qū)別的,比如“死等”和“忙等”都是沒能進入臨界區(qū),那兩者的區(qū)別到底是什么呢?

  1. 死等:首先對于“死等”的進程來說,這個進程可能是處于阻塞狀態(tài),等著別的進程將其喚醒(Signal原語),但是喚醒原語一直無法執(zhí)行,對于阻塞的進程來說,就是一直處于“死等”(睡死了,沒有人喊你起床)的狀態(tài)了,并且在死等狀態(tài),是沒有獲得處理機的;
  2. 忙等:忙等狀態(tài)比較容易理解,處于忙等狀態(tài)的進程是一直占有著處理機去不斷的判斷臨界區(qū)是否可以進入,在此期間,進程一直在運行(忙死了都,但是都是白忙),這也就是“忙等”狀態(tài),有一點需要注意的是,在單處理機系統(tǒng)中,忙等是非??膳碌模驗樘幱诿Φ鹊倪M程會一直霸占著處理機(不會去釋放處理機,發(fā)生了忙等的單CPU OS,無法執(zhí)行別的進程,除非系統(tǒng)重啟,即忙等的狀態(tài)在單CPU系統(tǒng)中是無法被打破的)。

其實不管是“忙等”還是“死等”,都是對OS有害的,都是應(yīng)該努力避免的。

​進程的同步機制包含軟件同步機制、硬件同步機制、信號量機制、管程機制等,也就是這些機制,可以保證程序并發(fā)執(zhí)行時的可再現(xiàn)性。

2.軟件同步機制

其實說到使用軟件來實現(xiàn)同步機制,大家想到的最多的應(yīng)該就是Java多線程了,Java通過鎖、synchronized、信號量等機制實現(xiàn)了線程的同步,但是線程間是共享父進程中的所有資源的,就比如多個職員坐在一間辦公室里,大家可以面對面的交談,這樣可以方便的解決共享資源的問題;但是在線程之間(就像是分布在世界各地的多個辦公室),如果需要共享系統(tǒng)資源時,進程之間很難直接通過軟件來進行交流,對系統(tǒng)資源的互斥共享就很麻煩了,需要借助硬件資源來完成同步了,需要在內(nèi)存中單獨使用一塊區(qū)域來保存進程是否在臨界區(qū)內(nèi)的標(biāo)志。

​ 我們先來看下面一段代碼:

//inside1、inside2是進程P1和P2在內(nèi)存中保存的標(biāo)志,表示進程是否在臨界區(qū)內(nèi)
inside1 = false;
inside2 = false;

//進程P1
process P1
begin
 while(inside2) ; //循環(huán)測試
	inside1 := true;
	臨界區(qū);
	inside1 := false;
end;

//進程P2
process P2
begin
 while(inside1) ; //循環(huán)測試
	inside2 := true;
	臨界區(qū);
	inside2 := false;
end;

​代碼邏輯很清晰,就是進程P1或者P2想進入臨界區(qū)之前,先去判斷對方是否在臨界區(qū)內(nèi),如果在的話,就一直循環(huán)等待,否則就進入臨界區(qū),然后“關(guān)門”(掛鎖)。第一眼看似乎是沒什么問題,但是如果進程在執(zhí)行期間,比如P1先執(zhí)行,在執(zhí)行掛鎖(inside1 = true)之前,發(fā)生了中斷,進程P2也開始了執(zhí)行,此錢P1的鎖還沒有掛上,因此進程P2可以進入臨界區(qū),在臨界區(qū)內(nèi)執(zhí)行的時候,P2也發(fā)生了中斷,P1恢復(fù)執(zhí)行,因為之前已經(jīng)執(zhí)行過判斷是否可以進入臨界區(qū)的代碼,因此此時同樣的可以進入臨界區(qū),在這種情況下,兩個進程同時的進入了臨界區(qū),進程的執(zhí)行就會出現(xiàn)錯誤。

​雖然在上面的進程P1和P2執(zhí)行的過程中發(fā)生了很多恰巧的事(小概率事件),P1在掛鎖前中斷、P2在臨界區(qū)執(zhí)行時中斷、P1和P2進程能在對方在中斷時搶占CPU,這幾個事件組合在一起,概率就更加的小了,但是仍然的是存在問題的。

​這個時候你可能會想,我在判斷之前先掛上鎖呢,我們把代碼的順序調(diào)整一下,大家請看:

//...

//進程P1
process P1
begin
 inside1 := true;
 while(inside2) ; //循環(huán)測試
	臨界區(qū);
	inside1 := false;
end;

//進程P2
process P2
begin
 inside2 := true;
 while(inside1) ; //循環(huán)測試
	臨界區(qū);
	inside2 := false;
end;

​這樣的話,進程P1、P2在并發(fā)執(zhí)行的時候就沒有問題了么,我們來看這樣一種情況,P1先執(zhí)行,掛鎖成功,假設(shè)在成功之后,P1發(fā)生了中斷,進程P2開始執(zhí)行,此時P2同樣可以掛鎖,但是在判斷是否可以進入臨界區(qū)時,則無法成功,會一直在循環(huán)中判斷,當(dāng)P1再次恢復(fù)執(zhí)行時,尷尬的事情發(fā)生了,P1也無法進入臨界區(qū)了,因為P2同樣把鎖給掛上了。

​上面是兩種軟件同步機制的實現(xiàn),第一個是雙標(biāo)志法先檢查,第二個是雙標(biāo)志法后檢查,但是兩個方法都無法真正的解決進程同步問題。雙標(biāo)志法先檢查法可能會讓兩個進程同時進入臨界區(qū),雙標(biāo)志法后檢查法可能會讓兩個進程都無法進入臨界區(qū),形成死鎖問題。

​雖然通過軟件方式也可以實現(xiàn)諸進程互斥的進入臨界區(qū)的問題,比如Peterson算法,但是有一定的難度,并且存在很大的局限性,因而現(xiàn)在已經(jīng)很少使用了,下面我們來看下別的幾種方式。

3.硬件同步機制

我們在軟件同步機制里講的兩個例子,都是在落鎖和判斷之間發(fā)生中斷,乃至導(dǎo)致無法實現(xiàn)互斥的進入臨界區(qū),那么,我們是不是可以在這個期間不允許發(fā)生中斷呢?這個就需要用到硬件了,下面我們就一起來看一下。

3.1 關(guān)中斷

這個方法就非常之霸氣了,進程在落鎖和判斷之間不是有可能會發(fā)生中斷么,那么我在開始測試之前關(guān)閉中斷(OS內(nèi)核不響應(yīng)中斷信號),到測試并上鎖之后在打開中斷。這樣可以保證兩個操作之間的連續(xù)性,保證臨界資源的互斥訪問。

​但是關(guān)中斷也必然會存在許多缺點:1.濫用關(guān)中斷的權(quán)利可能導(dǎo)致嚴(yán)重后果;2.關(guān)中斷時間過長會影響系統(tǒng)的并發(fā)性,直接的影響系統(tǒng)的資源利用率;3.關(guān)中斷無法適應(yīng)多CPU系統(tǒng)(多CPU系統(tǒng)不在本文的討論范圍內(nèi))

3.2 測試并建立(Test-and-Set, TS)指令

我們使用關(guān)中斷來解決落鎖和判斷之間不允許響應(yīng)中斷,但是我們?nèi)绻堰@兩個執(zhí)行變成一條指令呢,這樣是不是就可以保證中斷不會再落鎖和判斷之間被響應(yīng)?

​我們可以借助一條硬件指令-----“測試并建立”指令TS(Test-and-Set),來實現(xiàn)臨界資源的互斥訪問。TS指令的一般性描述如下:

//TS指令
boolean TS(boolean *lock){
 if(*lock == false){
  *lock = true;
  return true;
 }else{
  return false;
 }
}

//內(nèi)存中保存的鎖的值
boolean lock;
lock = false; //臨界區(qū)可以進入

//進程P1,P2,P3...Pn
process Pi{
 //...
 while(!TS(&lock));//循環(huán)請求鎖
 臨界區(qū);
 lock = false;//解鎖,歸還臨界資源
 
}

我們可以把TS指令看成上面的TS函數(shù)的執(zhí)行過程,其執(zhí)行過程是不可分割的,即是一條原語。當(dāng)lock的值為false時,表示臨界資源空閑,當(dāng)lock的值為true時,表示該資源正在被使用。

​使用TS指令來管理臨界區(qū)時,需要為每個臨界資源設(shè)置一個布爾變量lock。當(dāng)有進程需要進入臨界區(qū)時,需要先用TS指令測試臨界區(qū)對應(yīng)的那把“鎖”,如果返回true,臨界區(qū)空閑,可以進入,并落鎖,阻止別的進程再進入臨界區(qū);如果TS返回false,則必須要循環(huán)請求,直到TS返回的值變?yōu)閠rue。

3.3 對換指令

該指令也稱為swap指令,用于交換兩個字的內(nèi)容,其處理過程描述如下:

void swap(boolean *a, boolean *b){
 boolean tmp;
 tmp = *a;
 *a = *b;
 *b = tmp;
}

//內(nèi)存中保存的鎖的值
boolean lock;
lock = false; //臨界區(qū)可以進入

//進程P1,P2,P3...Pn
process Pi{
 //...
 boolean key = true;
 do{
  swap(&lock, &key);
 }while(key != false)//循環(huán)請求鎖
 臨界區(qū);
 lock = false;//解鎖,歸還臨界資源
}

Swap指令和TS指令類似,也需要為每個臨界資源設(shè)置一個布爾變量lock,不同的在進程中使用一個局部變量Key字段去替換出lock中的值,通過判斷key的值就可以判斷臨界資源是否空閑。

​有一點需要注意的,因為原語是將多個指令合并成一個指令,在原語的執(zhí)行過程中也是不響應(yīng)中斷的,使之成為原子操作,這個期間,等于是屏蔽中斷,也就等價于我們講的第一種硬件方式—關(guān)中斷,因此原語操作的指令長度應(yīng)該是短小精悍的,這樣才能保證系統(tǒng)的效率。

​利用上述的硬件指令能有效的實現(xiàn)進程的互斥,但是當(dāng)資源忙碌時,其他訪問進程的必須不斷的進程測試,處于一種“忙等”的狀態(tài),違背了讓權(quán)等待的原則,造成處理機時間的浪費,同時也很難將它們用于解決復(fù)雜的進程問題。

4.信號量機制

信號量機制是1965年迪杰斯特拉(Edsger Wybe Dijkstra)提出的一種卓有成效的進程同步工具。信號量機制在長期的應(yīng)用中得到了很大的發(fā)展,從整型信號量經(jīng)記錄型信號量,進而發(fā)展為“信號量集”機制,在目前來講,信號量。

​ 需要著重說明的一點是:信號量除了初始化外,僅能被通過兩個標(biāo)準(zhǔn)的原子操作wait(S)和signal(S)來訪問,這兩個操作也被稱為P、V操作,這幾個操作都是原語操作。

4.1 整型信號量

整型信號量是最開始由迪杰斯特拉定義的,整型信號量也很簡單,里面只有一個表示資源的數(shù)量的整形量,一般使用S符號來表示,整型信號量下的wait和signal操作可描述如下:

//整型信號量定義
int S;

//P操作
wait(S){
 while(S<=0);
 S--;
}

//V操作
signal(S){
 S++;
}

​因為wait和signal操作是原子的,因此他們在執(zhí)行的過程中是不可被中斷的。也就是說當(dāng)一個進程在修改某個信號量時,沒有其他的進程可以同時對該信號量進行修改。

​需要注意的是,整型信號量的wait操作,只要是信號量S<=0,就會不斷的測試,讓進程處于一種“忙等”的狀態(tài),沒有遵循讓權(quán)等待的原則。還有就是,把信號量的初值置為1,表示只允許一個進程訪問臨界資源,此時的信號量就可以轉(zhuǎn)換為互斥信號量,用于完成進程的互斥(對所有的信號量機制都是一樣)。

4.2 記錄型型號量

為了解決整型信號量存在的忙等問題,應(yīng)當(dāng)采取讓權(quán)等待原則。但是又會出現(xiàn)一個新的問題,如果多個進程并發(fā)請求訪問臨界資源,除了第一個搶到了信號量外,其余的進程都應(yīng)該釋放處理機,但是這些等待的進程要如何保存呢?為此,除了wait操作需要遵循讓權(quán)等待原則,還需在信號量中增加一個進程的鏈表指針list,用與鏈接上面描述的多個等待訪問臨界資源的進程。也因為記錄了等待的進程,這種信號量集之被稱為記錄型信號量。

​ 記錄型信號量具體的描述以及對應(yīng)的wait和signal操作如下所示:

//記錄型信號量定義
typedef struct{
 int value;
 struct process_control_block *list;
}semaphore;

//P操作
wait(semaphore *S){
 S->value--;
 if(s->value < 0) {
  block(S->list);
 }
}

//V操作
signal(semaphore *S){
 S->value++;
 if(S->value <= 0){
  wakeup(S->list);
 }
}

​在記錄型型號量中,S->value的初值表示系統(tǒng)中某類資源的數(shù)目;對它每次wait操作,意味進程請求一個單位的該類資源,使得系統(tǒng)中可分配的該類資源數(shù)減少一個,因此描述為S->value–;當(dāng)S.value<0時,表示在執(zhí)行此次分配之前,系統(tǒng)中的該類資源已經(jīng)全部分配完了,因此該訪問進程應(yīng)調(diào)用block原語進行自我阻塞,放棄處理機并插入到等待進程的隊列S->list中。我們再來分析下signal操作,首先釋放一個單位資源(S->value++),然后判斷是否有進程在等待申請信號量,如果有的話,就應(yīng)該調(diào)用wakeup原語從等待隊列(list所鏈接的進程隊列)中喚醒一個進程。

​通過上面的描述,我們來說一下在記錄型型號量中,value值所代表的意義:1.value>0,此時表示系統(tǒng)中還剩余的該類資源的數(shù)量;2.value=0,此時恰好處于一個平衡狀態(tài),系統(tǒng)中的資源分配完了,同樣也沒有進程在等待資源,即list隊列中是沒有等待進程的;3.value<0,此時,value的絕對值表示有多少個進程在等待申請信號量,也即是list隊列的長度。并且P、V操作必須成對的出現(xiàn),有一個P操作就必定有一個與之配對的V操作(當(dāng)為互斥操作時,它們同處于同一進程當(dāng)為同步操作時,則不在同一進程中出現(xiàn))。

4.3 AND型信號量

AND型號量正如其名,其基本思想是:將進程在整個運行過程中需要的所有資源,一次性的全部分配給進程,待進程使用完后再一起釋放。AND型號量可以滿足某些進程需要申請多個資源后才可以執(zhí)行任務(wù)的場景,并且AND型信號量可以解決死鎖問題,比如哲學(xué)家進餐問題中,一次給哲學(xué)家分配左右兩支筷子,那么就不會有哲學(xué)家會因為吃不到空心粉而餓死了。

​AND型信號量使用的還是記錄型信號量的數(shù)據(jù)結(jié)構(gòu),下面是Swait操作和Ssignal操作(此處和下面的信號量集都用此符號):

//記錄型信號量定義
...

//P操作
Swait(S1, S2, ..., Sn){
 while(true){
  if(S1>=1&&...&&Sn>=1){
   for(i=1;i<n;i++) Si--;
   break;
  } else {
   將進程插入到第一個無法滿足條件(即Si<1)的信號量對應(yīng)的等待隊列中,并且將程序計數(shù)器放置到
    Swait操作的開始處;
  }
	}
}

//V操作
Ssignal(S1, S2, ..., Sn){
 while(true){
  for(i=1;i<n;i++) {
   Si++;
   將Si中的等待隊列中的所有進程全部移除,插入到就緒隊列中;
  }
  break;
 }
}

​需要注意的就是,因為一次申請多個資源,所以在申請的過程中,如果因為哪一類資源不足而阻塞(請求N多個資源時第一個發(fā)現(xiàn)不滿足的資源,即資源數(shù)<1),就要將進程插入到對應(yīng)信號量的list中;與之對應(yīng)的喚醒操作也有所不同,不再是喚醒阻塞隊列中的某一個進程,而是將等待隊列中的所有進程全部移除,插入到就緒隊列中,讓這些進程再次執(zhí)行一次資源請求操作(這里因為是一次請求多個資源,后面可能依舊有資源無法滿足進程的需求)。

4.4 信號量集

在前面講的信號量機制中,wait、signal操作僅能對信號量施以加1或者減1操作,當(dāng)一次需要N個單位的資源時,便要執(zhí)行N次的wait(S)操作,這顯然是低效的,并且會增大發(fā)生死鎖的概率(需要執(zhí)行N次,在這N次執(zhí)行的過程中可能會發(fā)生中斷,資源也可能會被別的進程搶占)。此外,在某些情況下,為了確保系統(tǒng)的安全性,當(dāng)所申請的資源數(shù)量低于某一個下限時,就不予分配(保證地主家里有余糧)。

為了滿足上述的兩個需求,信號量機制又升級了,在AND型信號量的基礎(chǔ)上進行擴充,對進程所申請的所有資源,在一次P、V操作中完成申請或釋放。并且進程對每類信號量的測試值也不在是1,而是該資源的分配下限ti,也就是要求Si≥ti,否則不予分配。因此這里就不在給出具體的Swait和Ssignal的代碼了,而是給出函數(shù)聲明:

Swait(S1, t1, d1, ... , Sn, tn, dn);
Ssignal(S1, d1, ... , Sn, dn);

​這里與記錄性型號量稍有不同的地方就是判斷每類資源是否滿足需求時,判斷的條件由Si>=1變?yōu)?code>Si>=ti,并且分配資源由Si--變?yōu)?code>Si=Si-ti;與之對應(yīng)的Ssignal操作,不同的一點就是,一次可以歸還多個資源,相對應(yīng)的資源釋放代碼由Si++變?yōu)?code>Si=Si+ti。

​需要注意的是,因為AND型信號量和信號量集一次申請進程執(zhí)行所需的全部資源,這樣的優(yōu)點就是簡單、易行且安全,但是缺點也很明顯:1.資源被嚴(yán)重浪費,嚴(yán)重的惡化了資源的利用率;2.使進程經(jīng)常會發(fā)生饑餓現(xiàn)象(因為個別資源被占用的概率很大,會導(dǎo)致進程因為申請不到所有資源遲遲得不到執(zhí)行)。

5.管程機制

雖然信號量機制是一種既方便、又有效的進程同步機制,但是每個訪問臨界資源的進程都需要自備同步操作wait(S)和signal(S)操作,這就使大量的同步操作分散在各個進程中,不利于大家去集中的思考、抽象,使得程序設(shè)計的時候難度非常大,容易產(chǎn)生各種各樣的程序設(shè)計錯誤。在這樣的情況下,便產(chǎn)生了一種新的進程同步工具----管程(Monitors)。值得一提的是,管程也是迪杰斯特拉提出的。

​hansen對管程的定義如下:一個管程定義了一個數(shù)據(jù)結(jié)構(gòu)和能力為并發(fā)進程所執(zhí)行(在該數(shù)據(jù)結(jié)構(gòu)上)的一組操作,這組操作能同步進程和改變管程中的數(shù)據(jù)。

​由上述的定義可知,管程有四部分組成:1.管程的名稱;2.共享數(shù)據(jù)結(jié)構(gòu)說明;3.對數(shù)據(jù)結(jié)構(gòu)進行操作的一組過程;4.初始化語句。下面我們來看下管程的語法描述:

//管程的描述
Monitor monitor_name {//管程名
 share variable declarations;  //共享變量說明
 cond cond_declarationas;    //條件變量說明
 public:												 //能被進程調(diào)用的過程
 void P1(...){									 //對數(shù)據(jù)結(jié)構(gòu)操作過程
  ...
 }
 void P2(...){
  ...
 }
...
 void(...){
  ...
 }
...
 {
  initilization code;     //初始化代碼
 }
}

​通過上面的代碼描述,你是不是覺得很熟悉!實際上,管程中包含了面向?qū)ο蟮乃枷?,它將共享資源、對共享資源的操作抽象成變量和方法,并與同步機制一同封裝在一個對象內(nèi)部,隱藏了實現(xiàn)細(xì)節(jié)。但是你所不知道的是,在管程出現(xiàn)的時候,還沒有面向?qū)ο蟪绦蛟O(shè)計,是不是很意外。

​封裝于管程內(nèi)部的數(shù)據(jù)結(jié)構(gòu)僅能被管程內(nèi)部的過程訪問(類似于Java中的私有變量),如果想在管程外部訪問管程內(nèi)部的數(shù)據(jù)結(jié)構(gòu),就必須使用內(nèi)部過程(管程內(nèi)部的public修飾的方法,Java 類中的公共方法)。所有的進程想要訪問臨界資源,都只能通過管程間接的訪問,并且管程每次只允許一個進程進入管程,從而實現(xiàn)了進程互斥。

​有一點需要說明的是,管程為了實現(xiàn)更加復(fù)雜的進程同步方式,增加了一個條件變量。通常會根據(jù)進程被阻塞或者掛起的原因,設(shè)置不同的條件變量,每個條件變量保存一個鏈表指針,用與鏈接所有因為該條件變量而阻塞或掛起的所有進程,同時提供兩個P、V操作也可以表示為x.wait和x.signal,這兩個操作的含義如下:

  1. x.wait:正在調(diào)用管程的進程因x條件需要被阻塞或掛起,則調(diào)用x.wait將自己插入到x條件的等待隊列中,并釋放管程,直至x條件發(fā)生變化。
  2. x.signal:正在調(diào)用管程的進程因為x條件發(fā)生了變化(資源使用完,歸還),則調(diào)用x.signal,重新啟動一個因x條件而阻塞或掛起的進程,如果存在多個,則選擇其中的一個,如果沒有,繼續(xù)執(zhí)行原進程,不產(chǎn)生任何喚醒操作。

對于上面的操作,其實是有些問題的,我們設(shè)想一下,如果進程P1因x條件處于阻塞狀態(tài),那么當(dāng)進程P2執(zhí)行了x.signal操作喚醒P1后,進程P1和P2此時同時處于管程中了,這是不被允許的,那么如何確定哪個執(zhí)行哪個等待?這個問題也很簡單,可采用下面的兩種方式之一進行處理:

  1. P2等待,直至P1離開管程或者等待另一個條件;
  2. P1等待,直至P2離開管程或者等待另一個條件。

采用哪種處理方式,也存在很多爭論。Hoare采用了第一種處理方式,而Hansen采用了兩者的折中,它規(guī)定管程中的所有過程執(zhí)行的signal操作是過程體的最后一個操作,于是,進程P2執(zhí)行完signal操作后立即退出管程,因此進程P1馬上被恢復(fù)執(zhí)行。

​但是hansen的這種折中的辦法,規(guī)定死了釋放的時機,增加了程序設(shè)計的難度。因此現(xiàn)在的管程普遍采用Hoare的方式,也被稱為霍爾管程?;魻柟艹滔啾扔诠艹?、漢森管程,他的一個更大的優(yōu)勢是可以基于PV操作原語來實現(xiàn),換一句話說就是,wait和signal可以是程序過程而不需要是原語實現(xiàn)(可以使用語言機制實現(xiàn)霍爾管程)。這個就很厲害了,霍爾管程可以基于操作系統(tǒng)的程序庫或者是高級程序設(shè)計語言,在基礎(chǔ)的P、V操作原語上實現(xiàn)wait和signal操作,而不用擴展操作系統(tǒng)的內(nèi)核。

​管程的示意圖如下,從圖中我們可以看到,每個條件變量都有一個對應(yīng)的等待隊列,除了等待調(diào)用管程的進程隊列外,還有一個緊急隊列(優(yōu)先級高,由進程調(diào)用signal操作后插入),對于具體的操作,我們可以結(jié)合霍爾管程中條件變量中的wait、signal操作來講。

​下面是霍爾管程的條件變量上的wait操作和signal操作的描述:

//霍爾管程使用的信號量定義
//semaphore為本文之前定義的記錄型信號量
typedef struct{
 semaphore mutex;  //用與管程調(diào)用的互斥信號量
 semaphore next;			//發(fā)出signal的進程掛起自己的信號量,信號量中記錄著等待調(diào)用管程的進程
 int next_count;			//在next上等待的進程數(shù)
}interf;

//霍爾管程中的條件變量
typedef struct{
 semaphore x_sem;  //與資源相關(guān)的信號量
 int x_count;    //在x_sem上等待的進程數(shù)
}cond;

//條件變量對應(yīng)的wait操作
wait(cond declar, interf IM){
 declar->x_count++;     //在條件變量declar上等待的進程數(shù)量加1
 if(IM->next_count > 0){  //判斷是否有進程在高優(yōu)先級隊列中
  V(IM->next);						//喚醒因調(diào)用signal操作的進程
 } else {
  V(IM->mutex);						//沒有的話,喚醒一個等待進入管程的進程
 }
 P(declar->x_sem);     //釋放資源后,立即把自己掛起
 declar->xcount--;     //恢復(fù)執(zhí)行后,重新開始執(zhí)行,退出管程,條件變量declar等待的進程數(shù)量減1
}

//條件變量對應(yīng)的signal操作
signal(cond declar, interf IM){
 if(declar->x_count > 0){ //判斷是否有等待條件變量的進程
  IM->next_count++;    //掛起自己后,因為調(diào)用signal掛起自己的進程數(shù)量加1
  V(declar->x_sem);    //喚醒一個等待條件變量的進程
  P(IM->next);      //釋放資源后,立即把自己掛起,進入高優(yōu)先級隊列
  IM->next_count--;    //恢復(fù)執(zhí)行后,等待調(diào)用的管程的進程數(shù)量減1
 }
}

6.總結(jié)

​本文主要講了OS中為了解決進程同步問題才采取的措施,進程同步機制也是經(jīng)過逐步的發(fā)展,慢慢的變得完善,可以滿足復(fù)雜的并發(fā)程序設(shè)計,本文的內(nèi)容主要是偏理論,也結(jié)合了代碼來講解各個操作,能幫助大家理解。通過本文的學(xué)習(xí),希望可以讓你在并發(fā)程序設(shè)計上能獲得理論的依據(jù),因為我們做的更多的應(yīng)該是多線程編程,如果你接觸過并發(fā)編程,我覺得本文里的許多內(nèi)容能引起你的共鳴。

​本文所涉及到的代碼都是使用C語言寫的,如果有任何錯誤,煩請批評指正。

​又到了分隔符以下,本文到此就結(jié)束了,本文內(nèi)容全部都是由博主自己進行整理并結(jié)合自身的理解進行總結(jié),如果有什么錯誤,還請批評指正,當(dāng)然,如果有什么疑惑可以評論留言。

​本篇博文全文多達9000余字(本科畢業(yè)論文都沒這么多字🤣),在寫這篇博客的時候,對于一些小的概念,折騰的都快抑郁了。原創(chuàng)不易,如果本文對你有所幫助,還請留下個贊,以表支持。

以上就是詳解C語言進程同步機制的詳細(xì)內(nèi)容,更多關(guān)于C 進程同步機制的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Easyx實現(xiàn)掃雷游戲

    Easyx實現(xiàn)掃雷游戲

    這篇文章主要為大家詳細(xì)介紹了Easyx實現(xiàn)掃雷游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • 手把手帶你搞懂C語言指針

    手把手帶你搞懂C語言指針

    這篇文章主要介紹了C語言的指針,本文給大家介紹的非常詳細(xì),具有參考借鑒價值,需要的朋友可以參考下,希望能給你帶來幫助
    2021-08-08
  • C++ 泛型編程詳解

    C++ 泛型編程詳解

    這一篇介紹一下 C++ 編程中與面向?qū)ο蟛⒘械牧硪淮蠓种А盒途幊蹋@一篇主要介紹函數(shù)模板、類模板和成員模板三大部分,需要的朋友可以參考下
    2020-02-02
  • C語言之sizeof與strlen的使用及區(qū)別

    C語言之sizeof與strlen的使用及區(qū)別

    這篇文章主要介紹了C語言之sizeof與strlen的使用及區(qū)別,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • C++實現(xiàn)LeetCode(30.串聯(lián)所有單詞的子串)

    C++實現(xiàn)LeetCode(30.串聯(lián)所有單詞的子串)

    這篇文章主要介紹了C++實現(xiàn)LeetCode(30.串聯(lián)所有單詞的子串),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • C++ 17轉(zhuǎn)發(fā)一個函數(shù)調(diào)用的完美實現(xiàn)

    C++ 17轉(zhuǎn)發(fā)一個函數(shù)調(diào)用的完美實現(xiàn)

    這篇文章主要給大家介紹了關(guān)于C++ 17如何轉(zhuǎn)發(fā)一個函數(shù)調(diào)用的完美實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用C++17具有一定的參考學(xué)習(xí)價值,需要的朋友們下面跟著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-08-08
  • OpenCV邊緣提取算法流程的實現(xiàn)(附DEMO)

    OpenCV邊緣提取算法流程的實現(xiàn)(附DEMO)

    本文主要介紹了OpenCV邊緣提取算法流程的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • C++ 自定義棧實現(xiàn)迷宮求解

    C++ 自定義棧實現(xiàn)迷宮求解

    這篇文章主要介紹了C++ 自定義棧實現(xiàn)迷宮求解的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • C++利用SQLite實現(xiàn)命令行工具

    C++利用SQLite實現(xiàn)命令行工具

    這篇文章主要為大家詳細(xì)介紹了一個基于 C++、SQLite 和 Boost 庫的簡單交互式數(shù)據(jù)庫操作 Shell,該 Shell 允許用戶通過命令行輸入執(zhí)行各種數(shù)據(jù)庫操作,感興趣的可以了解下
    2023-11-11
  • C++ Opengl圖形顏色功能附源碼下載

    C++ Opengl圖形顏色功能附源碼下載

    這篇文章主要介紹了C++ Opengl圖形顏色功能附源碼下載,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11

最新評論