国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 系統(tǒng) > Linux > 正文

Linux 核心--6.進(jìn)程間通訊機(jī)制

2024-07-26 00:31:28
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
第五章 進(jìn)程間通訊機(jī)制

進(jìn)程在核心的協(xié)調(diào)下進(jìn)行相互間的通訊。linux支持大量進(jìn)程間通訊(ipC)機(jī)制。除了信號(hào)和管道外,Linux 還支持Unix系統(tǒng)V中的IPC機(jī)制。 


5.1  信號(hào)
信號(hào)是Unix系統(tǒng)中的最古老的進(jìn)程間通訊方式。它們用來(lái)向一個(gè)或多個(gè)進(jìn)程發(fā)送異步事件信號(hào)。信號(hào)可以從鍵盤(pán)中斷中產(chǎn)生,另外進(jìn)程對(duì)虛擬內(nèi)存的非法存取等系統(tǒng)錯(cuò)誤環(huán)境下也會(huì)有信號(hào)產(chǎn)生。信號(hào)還被shell程序用來(lái)向其子進(jìn)程發(fā)送任務(wù)控制命令。 
系統(tǒng)中有一組被詳細(xì)定義的信號(hào)類(lèi)型,這些信號(hào)可以由核心或者系統(tǒng)中其它具有適當(dāng)權(quán)限的進(jìn)程產(chǎn)生。使用kill命令(kill -l)可以列出系統(tǒng)中所有已經(jīng)定義的信號(hào)。在我的系統(tǒng)(Intel系統(tǒng))上運(yùn)行結(jié)果如下: 

 1) SIGHUP  2) SIGINT  3) SIGQUIT  4) SIGILL
 5) SIGTRAP  6) SIGIOT  7) SIGBUS  8) SIGFPE
 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
13) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD
18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN
22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO
30) SIGPWR

當(dāng)我在Alpha AXP中運(yùn)行此命令時(shí),得到了不同的信號(hào)個(gè)數(shù)。除了兩個(gè)信號(hào)外,進(jìn)程可以忽略這些信號(hào)中的絕大部分。其一是引起進(jìn)程終止執(zhí)行的SIGSTOP信號(hào),另一個(gè)是引起進(jìn)程退出的SIGKILL信號(hào)。 至于其它信號(hào),進(jìn)程可以選擇處理它們的具體方式。進(jìn)程可以阻塞信號(hào),如若不阻塞,則可以在自行處理此信號(hào)和將其轉(zhuǎn)交核心處理之間作出選擇。如果由核心來(lái)處理此信號(hào),它將使用對(duì)應(yīng)此信號(hào)的缺省處理方法。 比如當(dāng)進(jìn)程接收到SIGFPE(浮點(diǎn)數(shù)異常)時(shí),核心的缺省操作是引起core dump和進(jìn)程的退出。信號(hào)沒(méi)有固有的相對(duì)優(yōu)先級(jí)。如果在同一時(shí)刻對(duì)于一個(gè)進(jìn)程產(chǎn)生了兩個(gè)信號(hào),則它們將可能以任意順序到達(dá)進(jìn)程并進(jìn)行處理。同時(shí)Linux并不提供處理多個(gè)相同類(lèi)型信號(hào)的方式。即進(jìn)程無(wú)法區(qū)分它是收到了1個(gè)還是42個(gè)SIGCONT信號(hào)。 

Linux通過(guò)存儲(chǔ)在進(jìn)程task_struct中的信息來(lái)實(shí)現(xiàn)信號(hào)。信號(hào)個(gè)數(shù)受到處理器字長(zhǎng)的限制。32位字長(zhǎng)的處理器最多可以有32個(gè)信號(hào)而64位處理器如Alpha AXP可以有最多64個(gè)信號(hào)。當(dāng)前未處理的信號(hào)保存在signal域中,并帶有保存在blocked中的被阻塞信號(hào)的屏蔽碼。除了SIGSTOP和SIGKILL外,所有的信號(hào)都能被阻塞。當(dāng)產(chǎn)生可阻塞信號(hào)時(shí),此信號(hào)可以保持一直處于待處理狀態(tài)直到阻塞釋放。Linux保存著每個(gè)進(jìn)程處理每個(gè)可能信號(hào)的信息,它們保存在每個(gè)進(jìn)程task_struct中的sigaction數(shù)組中。這些信息包括進(jìn)程希望處理的信號(hào)所對(duì)應(yīng)的過(guò)程地址,或者指示是忽略信號(hào)還是由核心來(lái)處理它的標(biāo)記。通過(guò)系統(tǒng)調(diào)用,進(jìn)程可以修改缺省的信號(hào)處理過(guò)程,這將改變某個(gè)信號(hào)的sigaction以及阻塞屏蔽碼。 

并不是系統(tǒng)中每個(gè)進(jìn)程都可以向所有其它進(jìn)程發(fā)送信號(hào):只有核心和超級(jí)用戶(hù)具有此權(quán)限。普通進(jìn)程只能向具有相同uid和gid的進(jìn)程或者在同一進(jìn)程組中的進(jìn)程發(fā)送信號(hào)。信號(hào)是通過(guò)設(shè)置task_struct結(jié)構(gòu)中signal域里的某一位來(lái)產(chǎn)生的。如果進(jìn)程沒(méi)有阻塞信號(hào)并且處于可中斷的等待狀態(tài),則可以將其狀態(tài)改成Running,同時(shí)如確認(rèn)進(jìn)程還處在運(yùn)行隊(duì)列中,就可以通過(guò)信號(hào)喚醒它。這樣系統(tǒng)下次發(fā)生調(diào)度時(shí),調(diào)度管理器將選擇它運(yùn)行。如果進(jìn)程需要缺省的信號(hào)處理過(guò)程,則Linux可以?xún)?yōu)化對(duì)此信號(hào)的處理。例如SIGWINCH(X窗口的焦點(diǎn)改變)信號(hào),其缺省處理過(guò)程是什么也不做。 

信號(hào)并非一產(chǎn)生就立刻交給進(jìn)程,而是必須等待到進(jìn)程再次運(yùn)行時(shí)才交給進(jìn)程。每次進(jìn)程從系統(tǒng)調(diào)用中退出前,它都會(huì)檢查signal和blocked域,看是否有可以立刻發(fā)送的非阻塞信號(hào)。這看起來(lái)非常不可靠,但是系統(tǒng)中每個(gè)進(jìn)程都在不停地進(jìn)行系統(tǒng)調(diào)用,如向終端輸出字符。當(dāng)然進(jìn)程可以選擇去等待信號(hào),此時(shí)進(jìn)程將一直處于可中斷狀態(tài)直到信號(hào)出現(xiàn)。對(duì)當(dāng)前不可阻塞信號(hào)的處理代碼放置在sigaction結(jié)構(gòu)中。 

如果信號(hào)的處理過(guò)程被設(shè)置成缺省則由核心來(lái)應(yīng)付它。SIGSTOP信號(hào)的缺省處理過(guò)程是將當(dāng)前進(jìn)程的狀態(tài)改變成為Stopped并運(yùn)行調(diào)度管理器以選擇一個(gè)新進(jìn)程繼續(xù)運(yùn)行。SIGFPE的缺省處理過(guò)程則是引起core dump并使進(jìn)程退出。當(dāng)然,進(jìn)程可以定義其自身的信號(hào)處理過(guò)程。一旦信號(hào)產(chǎn)生,這個(gè)過(guò)程就將被調(diào)用。它的地址存儲(chǔ)在sigaction結(jié)構(gòu)中。核心必須調(diào)用進(jìn)程的信號(hào)處理例程,具體如何去做依賴(lài)于處理器類(lèi)型,但是所有的CPU 必須處理這個(gè)問(wèn)題:如果信號(hào)產(chǎn)生時(shí),當(dāng)前進(jìn)程正在核心模式下運(yùn)行并且馬上要返回調(diào)用核心或者系統(tǒng)例程的進(jìn)程,而該進(jìn)程處在用戶(hù)模式下。解決這個(gè)問(wèn)題需要操縱進(jìn)程的堆棧及寄存器。進(jìn)程的程序計(jì)數(shù)器被設(shè)置成其信號(hào)處理過(guò)程的地址,而參數(shù)通過(guò)調(diào)用框架或者寄存器傳遞到處理例程中。當(dāng)進(jìn)程繼續(xù)執(zhí)行時(shí),信號(hào)處理例程好象普通的函數(shù)調(diào)用一樣。 

Linux是POSIX兼容的,所以當(dāng)某個(gè)特定信號(hào)處理例程被調(diào)用時(shí),進(jìn)程可以設(shè)定哪個(gè)信號(hào)可以阻塞。這意味著可以在進(jìn)程信號(hào)處理過(guò)程中改變blocked屏蔽碼。當(dāng)信號(hào)處理例程結(jié)束時(shí),此blocked屏蔽碼必須設(shè)置成原有值。 因此,Linux添加了一個(gè)過(guò)程調(diào)用來(lái)進(jìn)行整理工作,通過(guò)它來(lái)重新設(shè)置被發(fā)送信號(hào)進(jìn)程調(diào)用棧中的原有blocked屏蔽碼。 對(duì)于同一時(shí)刻幾個(gè)信號(hào)處理過(guò)程,Linux通過(guò)堆棧方式來(lái)優(yōu)化其使用,每當(dāng)一個(gè)處理過(guò)程退出時(shí),下一個(gè)處理過(guò)程必須等到整理例程結(jié)束后才執(zhí)行。 


5.2  管道
一般的Linux shell程序都允許重定向。如 

$ ls | pr | lpr


在這個(gè)管道應(yīng)用中,ls列當(dāng)前目錄的輸出被作為標(biāo)準(zhǔn)輸入送到pr程序中,而pr的輸出又被作為標(biāo)準(zhǔn)輸入送到lpr程序中。管道是單向的字節(jié)流,它將某個(gè)進(jìn)程的標(biāo)準(zhǔn)輸出連接到另外進(jìn)程的標(biāo)準(zhǔn)輸入。但是使用管道的進(jìn)程都不會(huì)意識(shí)到重定向的存在,并且其執(zhí)行結(jié)果也不會(huì)有什么不同。shell程序負(fù)責(zé)在進(jìn)程間建立臨時(shí)的管道。 






圖5.1 管道 

在Linux中,管道是通過(guò)指向同一個(gè)臨時(shí)VFS inode的兩個(gè)file數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)的,此VFS inode指向內(nèi)存中的一個(gè)物理頁(yè)面。圖5.1中每個(gè)file數(shù)據(jù)結(jié)構(gòu)指向不同的文件操作例程向量,一個(gè)是實(shí)現(xiàn)對(duì)管道的寫(xiě),另一個(gè)從管道中讀。 

這樣就隱藏了讀寫(xiě)管道和讀寫(xiě)普通的文件時(shí)系統(tǒng)調(diào)用的差別。當(dāng)寫(xiě)入進(jìn)程對(duì)管道寫(xiě)時(shí),字節(jié)被拷貝到共享數(shù)據(jù)頁(yè)面中,當(dāng)讀取進(jìn)程從管道中讀時(shí),字節(jié)從共享數(shù)據(jù)頁(yè)面中拷貝出來(lái)。Linux必須同步對(duì)管道的訪問(wèn)。它必須保證讀者和寫(xiě)者以確定的步驟執(zhí)行,為此需要使用鎖、等待隊(duì)列和信號(hào)等同步機(jī)制。 

當(dāng)寫(xiě)者想對(duì)管道寫(xiě)入時(shí),它使用標(biāo)準(zhǔn)的寫(xiě)庫(kù)函數(shù)。表示打開(kāi)文件和打開(kāi)管道的描敘符用來(lái)對(duì)進(jìn)程的file數(shù)據(jù) 結(jié)構(gòu)集合進(jìn)行索引。Linux系統(tǒng)調(diào)用使用由管道file數(shù)據(jù)結(jié)構(gòu)指向的write過(guò)程。這個(gè)write過(guò)程用保存在表示管道的VFS inode中的信息來(lái)管理寫(xiě)請(qǐng)求。 

如果沒(méi)有足夠的空間容納對(duì)所有寫(xiě)入管道的數(shù)據(jù),只要管道沒(méi)有被讀者加鎖。則Linux為寫(xiě)者加鎖,并把從寫(xiě)入進(jìn)程地址空間中寫(xiě)入的字節(jié)拷貝到共享數(shù)據(jù)頁(yè)面中去。如果管道被讀者加鎖或者沒(méi)有足夠空間存儲(chǔ)數(shù)據(jù),當(dāng)前進(jìn)程將在管道inode的等待隊(duì)列中睡眠,同時(shí)調(diào)度管理器開(kāi)始執(zhí)行以選擇其它進(jìn)程來(lái)執(zhí)行。如果寫(xiě)入進(jìn)程是可中斷的,則當(dāng)有足夠的空間或者管道被解鎖時(shí),它將被讀者喚醒。當(dāng)數(shù)據(jù)被寫(xiě)入時(shí),管道的VFS inode被解鎖,同時(shí)任何在此inode的等待隊(duì)列上睡眠的讀者進(jìn)程都將被喚醒。 

從管道中讀出數(shù)據(jù)的過(guò)程和寫(xiě)入類(lèi)似。 

進(jìn)程允許進(jìn)行非阻塞讀(這依賴(lài)于它們打開(kāi)文件或者管道的方式),此時(shí)如果沒(méi)有數(shù)據(jù)可讀或者管道被加鎖, 則返回錯(cuò)誤信息表明進(jìn)程可以繼續(xù)執(zhí)行。阻塞方式則使讀者進(jìn)程在管道inode的等待隊(duì)列上睡眠直到寫(xiě)者 進(jìn)程結(jié)束。當(dāng)兩個(gè)進(jìn)程對(duì)管道的使用結(jié)束時(shí),管道inode和共享數(shù)據(jù)頁(yè)面將同時(shí)被遺棄。 

Linux還支持命名管道(named pipe),也就是FIFO管道,因?yàn)樗偸前凑障冗M(jìn)先出的原則工作。第一個(gè)被寫(xiě)入 的數(shù)據(jù)將首先從管道中讀出來(lái)。和其它管道不一樣,F(xiàn)IFO管道不是臨時(shí)對(duì)象,它們是文件系統(tǒng)中的實(shí)體并且 可以通過(guò)mkfifo命令來(lái)創(chuàng)建。進(jìn)程只要擁有適當(dāng)?shù)臋?quán)限就可以自由使用FIFO管道。打開(kāi)FIFO管道的方式稍有不同。其它管道需要先創(chuàng)建(它的兩個(gè)file數(shù)據(jù)結(jié)構(gòu),VFS inode和共享數(shù)據(jù)頁(yè)面)而FIFO管道已經(jīng)存在,只需要由使用者打開(kāi)與關(guān)閉。在寫(xiě)者進(jìn)程打開(kāi)它之前,Linux必須讓讀者進(jìn)程先打開(kāi)此FIFO管道;任何讀者進(jìn)程從中讀取之前必須有寫(xiě)者進(jìn)程向其寫(xiě)入數(shù)據(jù)。FIFO管道的使用方法與普通管道基本相同,同時(shí)它們使用相同數(shù)據(jù)結(jié)構(gòu)和操作。 


5.3  套接口

5.3.1  系統(tǒng)V IPC機(jī)制
Linux支持Unix系統(tǒng)V(1983)版本中的三種進(jìn)程間通訊機(jī)制。它們是消息隊(duì)列、信號(hào)燈以及共享內(nèi)存。這些系統(tǒng)V IPC機(jī)制使用共同的授權(quán)方法。只有通過(guò)系統(tǒng)調(diào)用將標(biāo)志符傳遞給核心之后,進(jìn)程才能存取這些資源。這些系統(tǒng)V IPC對(duì)象使用與文件系統(tǒng)非常類(lèi)似的訪問(wèn)控制方式。對(duì)象的引用標(biāo)志符被用來(lái)作為資源表中的索引。這個(gè)索引值需要一些處理后才能得到。 
系統(tǒng)中所有系統(tǒng)V IPC對(duì)象的Linux數(shù)據(jù)結(jié)構(gòu)包含一個(gè)ipc_perm結(jié)構(gòu),它含有進(jìn)程擁有者和創(chuàng)建者及組標(biāo)志符。另外還有對(duì)此對(duì)象(擁有者,組及其它)的存取模式以及IPC對(duì)象鍵。此鍵值被用來(lái)定位系統(tǒng)V IPC對(duì)象的引用標(biāo)志符。這樣的鍵值一共有兩組:公有與私有。如果此鍵為公有,則系統(tǒng)中任何接受權(quán)限檢查的進(jìn)程都可以找到系統(tǒng)V IPC對(duì)象的引用標(biāo)志符。系統(tǒng)V IPC對(duì)象絕不能用一個(gè)鍵值來(lái)引用,而只能使用引用標(biāo)志符。 


5.3.2  消息隊(duì)列
消息隊(duì)列允許一個(gè)或者多個(gè)進(jìn)程向它寫(xiě)入與讀取消息。Linux維護(hù)著一個(gè)msgque消息隊(duì)列鏈表,其中每個(gè)元素 指向一個(gè)描敘消息隊(duì)列的msqid_ds結(jié)構(gòu)。當(dāng)創(chuàng)建新的消息隊(duì)列時(shí),系統(tǒng)將從系統(tǒng)內(nèi)存中分配一個(gè)msqid_ds結(jié)構(gòu),同時(shí)將其插入到數(shù)組中。 





圖5.2 系統(tǒng)V IPC消息隊(duì)列 

每個(gè)msqid_ds結(jié)構(gòu)包含一個(gè)ipc_perm結(jié)構(gòu)和指向已經(jīng)進(jìn)入此隊(duì)列消息的指針。另外,Linux保留有關(guān)隊(duì)列修改時(shí)間信息,如上次系統(tǒng)向隊(duì)列中寫(xiě)入的時(shí)間等。msqid_ds包含兩個(gè)等待隊(duì)列:一個(gè)為隊(duì)列寫(xiě)入進(jìn)程使用而另一個(gè)由隊(duì)列讀取進(jìn)程使用。 

每次進(jìn)程試圖向?qū)懭腙?duì)列寫(xiě)入消息時(shí),系統(tǒng)將把其有效用戶(hù)和組標(biāo)志符與此隊(duì)列的ipc_perm結(jié)構(gòu)中的模式進(jìn)行比較。如果允許寫(xiě)入操作,則把此消息從此進(jìn)程的地址空間拷貝到msg數(shù)據(jù)結(jié)構(gòu)中,并放置到此消息隊(duì)列尾部。由于 Linux嚴(yán)格限制可寫(xiě)入消息的個(gè)數(shù)和長(zhǎng)度,隊(duì)列中可能容納不下這個(gè)消息。此時(shí),此寫(xiě)入進(jìn)程將被添加到這個(gè)消息隊(duì)列的等待隊(duì)列中,同時(shí)調(diào)用調(diào)度管理器選擇新進(jìn)程運(yùn)行。當(dāng)由消息從此隊(duì)列中釋放時(shí),該進(jìn)程將被喚醒。 

從隊(duì)列中讀的過(guò)程與之類(lèi)似。進(jìn)程對(duì)這個(gè)寫(xiě)入隊(duì)列的訪問(wèn)權(quán)限將被再次檢驗(yàn)。讀取進(jìn)程將選擇隊(duì)列中第一個(gè)消息(不管是什么類(lèi)型)或者第一個(gè)某特定類(lèi)型的消息。如果沒(méi)有消息可以滿(mǎn)足此要求,讀取進(jìn)程將被添加 到消息隊(duì)列的讀取等待隊(duì)列中,然后系統(tǒng)運(yùn)行調(diào)度管理器。當(dāng)有新消息寫(xiě)入隊(duì)列時(shí),進(jìn)程將被喚醒繼續(xù)執(zhí)行。 


5.3.3  信號(hào)燈
信號(hào)燈最簡(jiǎn)單的形式是某個(gè)可以被多個(gè)進(jìn)程檢驗(yàn)和設(shè)置(test&set)的內(nèi)存單元。這個(gè)檢驗(yàn)與設(shè)置操作對(duì)每個(gè)進(jìn)程而言是不可中斷或者說(shuō)是一個(gè)原子性操作;一旦啟動(dòng)誰(shuí)也終止不了。檢驗(yàn)與設(shè)置操作的結(jié)果是信號(hào)燈當(dāng)前值加1, 這個(gè)值可以是正數(shù)也可以是負(fù)數(shù)。根據(jù)這個(gè)操作的結(jié)果,進(jìn)程可能可以一直睡眠到此信號(hào)燈的值被另一個(gè)進(jìn)程更改為止。信號(hào)燈可用來(lái)實(shí)現(xiàn)臨界區(qū)(critical region):某一時(shí)刻在此區(qū)域內(nèi)的代碼只能被一個(gè)進(jìn)程執(zhí)行。 

如果你有多個(gè)協(xié)作進(jìn)程從一個(gè)數(shù)據(jù)文件中讀取與寫(xiě)入記錄。有時(shí)你可能需要這些文件訪問(wèn)遵循嚴(yán)格的訪問(wèn)次序。 那么可在文件操作代碼上使用一個(gè)初始值為1的信號(hào)燈,它帶有兩個(gè)信號(hào)燈操作,一個(gè)檢驗(yàn)并對(duì)信號(hào)燈 值減1,而另一個(gè)檢驗(yàn)并加1。第一個(gè)訪問(wèn)文件的進(jìn)程將試圖將信號(hào)燈值減1,如果獲得成功則信號(hào)燈值變成了 0。此進(jìn)程于是開(kāi)始使用這個(gè)數(shù)據(jù)文件,但是此時(shí)如果另一進(jìn)程也想將信號(hào)燈值減1,則信號(hào)燈值將為-1,這次操作將會(huì)失敗。它將掛起執(zhí)行直到第一個(gè)進(jìn)程完成對(duì)此數(shù)據(jù)文件的使用。此時(shí)這個(gè)等待進(jìn)程將被喚醒,這次它對(duì)信號(hào)燈的操作將成功。 




圖5.3 系統(tǒng)V IPC信號(hào)燈 


每個(gè)系統(tǒng)V IPC信號(hào)燈對(duì)象對(duì)應(yīng)一個(gè)信號(hào)燈數(shù)組,Linux使用semid_ds結(jié)構(gòu)來(lái)表示。系統(tǒng)中所有semid_ds結(jié)構(gòu)由一組semary指針來(lái)指示。在每個(gè)信號(hào)燈數(shù)組中有一個(gè)sem_nsems,它表示一個(gè)由sem_base指向的sem結(jié)構(gòu)。授權(quán)的進(jìn)程可以使用系統(tǒng)調(diào)用來(lái)操縱這些包含系統(tǒng)V IPC信號(hào)燈對(duì)象的信號(hào)燈數(shù)組。這個(gè)系統(tǒng)調(diào)用可以定義許多種操作,每個(gè)操作用三個(gè)輸入來(lái)描敘:信號(hào)燈索引、操作值和一組標(biāo)志。信號(hào)燈索引是一個(gè)信號(hào)燈數(shù)組的索引,而操作值是將被加到信號(hào)燈上的數(shù)值。首先Linux將檢查是否所有操作已經(jīng)成功。如果操作值與信號(hào)燈當(dāng)前數(shù)值相加大于0,或者操作值與信號(hào)燈當(dāng)前值都是0,操作將會(huì)成功。如果所有信號(hào)燈操作失敗,Linux僅僅會(huì)把那些操作標(biāo)志沒(méi)有要求系統(tǒng)調(diào)用為非阻塞類(lèi)型的進(jìn)程掛起。進(jìn)程掛起后,Linux必須保存信號(hào)燈操作的執(zhí)行狀態(tài)并將當(dāng)前進(jìn)程放入等待隊(duì)列。系統(tǒng)還在堆棧上建立sem_queue結(jié)構(gòu)并填充各個(gè)域。這個(gè)sem_queue結(jié)構(gòu)將被放到此信號(hào)燈對(duì)象等待隊(duì)列的尾部(使用 sem_pending和sem_pending_last指針)。系統(tǒng)把當(dāng)前進(jìn)程置入sem_queue結(jié)構(gòu)中的等待隊(duì)列(sleeper)中,然后啟動(dòng)調(diào)度管理器選擇其它進(jìn)程運(yùn)行。 

如果所有這些信號(hào)燈操作都成功則無(wú)需掛起當(dāng)前進(jìn)程,Linux將對(duì)信號(hào)燈數(shù)組中的其他成員進(jìn)行相同操作,然后檢查那些處于等待或者掛起狀態(tài)的進(jìn)程。首先,Linux將依次檢查掛起隊(duì)列(sem_pending) 中的每個(gè)成員,看信號(hào)燈操作能否繼續(xù)。如果可以則將其sem_queue結(jié)構(gòu)從掛起鏈表中刪除并對(duì)信號(hào)燈數(shù)組發(fā)出信號(hào)燈操作。Linux還將喚醒處于睡眠狀態(tài)的進(jìn)程并使之成為下一個(gè)運(yùn)行的進(jìn)程。如果在對(duì)掛起隊(duì)列的遍歷過(guò)程中有的信號(hào)燈操作不能完成則Linux將一直重復(fù)此過(guò)程,直到所有信號(hào)燈操作完成且沒(méi)有進(jìn)程需要繼續(xù)睡眠。 

但是信號(hào)燈的使用可能產(chǎn)生一個(gè)嚴(yán)重的問(wèn)題:死鎖。當(dāng)一個(gè)進(jìn)程進(jìn)入臨界區(qū)時(shí)它改變了信號(hào)燈的值而離開(kāi)臨界區(qū)時(shí)由于運(yùn)行失敗或者被kill而沒(méi)有改回信號(hào)燈時(shí),死鎖將會(huì)發(fā)生。Linux通過(guò)維護(hù)一組描敘信號(hào)燈數(shù)組變化的鏈表來(lái)防止該現(xiàn)象的發(fā)生。它的具體做法是讓Linux將把此信號(hào)燈設(shè)置為進(jìn)程對(duì)其進(jìn)行操作前的狀態(tài)。這些狀態(tài)值被保存在使用該信號(hào)燈數(shù)組進(jìn)程的semid_ds和task_struct結(jié)構(gòu)的sem_undo結(jié)構(gòu)中。 

信號(hào)燈操作將迫使系統(tǒng)對(duì)它引起的狀態(tài)變化進(jìn)行維護(hù)。Linux為每個(gè)進(jìn)程維護(hù)至少一個(gè)對(duì)應(yīng)于信號(hào)燈數(shù)組的sem_undo結(jié)構(gòu)。如果請(qǐng)求進(jìn)行信號(hào)燈操作的進(jìn)程沒(méi)有該結(jié)構(gòu),則必要時(shí)Linux會(huì)為其創(chuàng)建一個(gè)。這個(gè)sem_undo 結(jié)構(gòu)將同時(shí)放入此進(jìn)程的task_struct結(jié)構(gòu)和此信號(hào)燈數(shù)組的semid_ds結(jié)構(gòu)中。當(dāng)對(duì)信號(hào)燈進(jìn)行操作時(shí),信號(hào)燈變化值的負(fù)數(shù)被置入進(jìn)程的sem_undo結(jié)構(gòu)中該信號(hào)的入口中。所以當(dāng)操作值為2時(shí),則此信號(hào)燈的調(diào)整入口中將加入一個(gè)-2。 

象正常退出一樣,當(dāng)進(jìn)程被刪除時(shí),Linux將遍歷該進(jìn)程的sem_undo集合對(duì)信號(hào)燈數(shù)組使用調(diào)整值。如果信號(hào)燈集合被刪除而sem_undo數(shù)據(jù)結(jié)構(gòu)還在進(jìn)程的task_struct結(jié)構(gòu)中則此信號(hào)燈數(shù)組標(biāo)志符將被置為無(wú)效。此時(shí) 信號(hào)燈清除代碼只需丟棄sem_undo結(jié)構(gòu)即可。 


5.3.4  共享內(nèi)存
共享內(nèi)存允許一個(gè)或多個(gè)進(jìn)程通過(guò)同時(shí)出現(xiàn)在它們虛擬地址空間中的內(nèi)存來(lái)通訊。此虛擬內(nèi)存的頁(yè)面出現(xiàn)在每個(gè)共享進(jìn)程頁(yè)表中。但此頁(yè)面并不一定位于所有共享進(jìn)程虛擬內(nèi)存的相同位置。和其它系統(tǒng)V IPC對(duì)象的使用方法一樣,對(duì)共享內(nèi)存區(qū)域的訪問(wèn)是通過(guò)鍵和訪問(wèn)權(quán)限檢驗(yàn)來(lái)控制的。一旦內(nèi)存被共享,則再不會(huì)檢驗(yàn)進(jìn)程對(duì)對(duì)象的使用方式。它依賴(lài)于其它機(jī)制,如系統(tǒng)V信號(hào)燈,來(lái)同步對(duì)共享內(nèi)存的訪問(wèn)。 






圖5.4 系統(tǒng)V IPC共享內(nèi)存 

每個(gè)新創(chuàng)建的共享內(nèi)存區(qū)域由一個(gè)shmid_ds數(shù)據(jù)結(jié)構(gòu)來(lái)表示。它們被保存再shm_segs數(shù)組中。 shmid_ds數(shù)據(jù)結(jié)構(gòu)描敘共享內(nèi)存的大小,進(jìn)程如何使用以及共享內(nèi)存映射到其各自地址空間的方式。由共享內(nèi)存創(chuàng)建者控制對(duì)此內(nèi)存的存取權(quán)限以及其鍵是公有還是私有。如果它由足夠權(quán)限,它還可以將此共享內(nèi)存加載到物理內(nèi)存中。 

每個(gè)使用此共享內(nèi)存的進(jìn)程必須通過(guò)系統(tǒng)調(diào)用將其連接到虛擬內(nèi)存上。這時(shí)進(jìn)程創(chuàng)建新的vm_area_struct來(lái)描敘此共享內(nèi)存。進(jìn)程可以決定此共享內(nèi)存在其虛擬地址空間的位置,或者讓Linux選擇一塊足夠大的區(qū)域。 新的vm_area_struct結(jié)構(gòu)將被放到由shmid_ds指向的vm_area_struct鏈表中。通過(guò)vm_next_shared和vm_prev_shared 指針將它們連接起來(lái)。虛擬內(nèi)存在連接時(shí)并沒(méi)有創(chuàng)建;進(jìn)程訪問(wèn)它時(shí)才創(chuàng)建。 

當(dāng)進(jìn)程首次訪問(wèn)共享虛擬內(nèi)存中的頁(yè)面時(shí)將產(chǎn)生頁(yè)面錯(cuò)。當(dāng)取回此頁(yè)面后,Linux找到了描敘此頁(yè)面的vm_area_struct數(shù)據(jù)結(jié)構(gòu)。它包含指向使用此種類(lèi)型虛擬內(nèi)存的處理函數(shù)地址指針。共享內(nèi)存頁(yè)面錯(cuò)誤處理 代碼將在此shmid_ds對(duì)應(yīng)的頁(yè)表入口鏈表中尋找是否存在此共享虛擬內(nèi)存頁(yè)面。如果不存在,則它將分配物理頁(yè)面并為其創(chuàng)建頁(yè)表入口。同時(shí)還將它放入當(dāng)前進(jìn)程的頁(yè)表中,此入口被保存在shmid_ds結(jié)構(gòu)中。這意味著下個(gè)試圖訪問(wèn)此內(nèi)存的進(jìn)程還會(huì)產(chǎn)生頁(yè)面錯(cuò)誤,共享內(nèi)存錯(cuò)誤處理函數(shù)將為此進(jìn)程使用其新創(chuàng)建的物理頁(yè)面。這樣,第一個(gè)訪問(wèn)虛擬內(nèi)存頁(yè)面的進(jìn)程創(chuàng)建這塊內(nèi)存,隨后的進(jìn)程把此頁(yè)面加入到各自的虛擬地址空間中。 

當(dāng)進(jìn)程不再共享此虛擬內(nèi)存時(shí),進(jìn)程和共享內(nèi)存的連接將被斷開(kāi)。如果其它進(jìn)程還在使用這個(gè)內(nèi)存,則此操作只影響當(dāng)前進(jìn)程。其對(duì)應(yīng)的vm_area_struct結(jié)構(gòu)將從shmid_ds結(jié)構(gòu)中刪除并回收。當(dāng)前進(jìn)程對(duì)應(yīng)此共享內(nèi)存地址的頁(yè)表入口也將被更新并置為無(wú)效。當(dāng)最后一個(gè)進(jìn)程斷開(kāi)與共享內(nèi)存的連接時(shí),當(dāng)前位于物理內(nèi)存中的共享內(nèi)存頁(yè)面將被釋放,同時(shí)還有此共享內(nèi)存的shmid_ds結(jié)構(gòu)。 

當(dāng)共享內(nèi)存沒(méi)有被鎖入物理內(nèi)存時(shí),情況將更加復(fù)雜。此時(shí)共享內(nèi)存頁(yè)面可能會(huì)在內(nèi)存使用高峰期,被交換到系統(tǒng)的交換磁盤(pán)上。共享內(nèi)存如何被交換與調(diào)入物理內(nèi)存將在mm一章中詳細(xì)描敘。
發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 乐至县| 蒙阴县| 汽车| 临夏县| 凉城县| 临洮县| 清河县| 东台市| 桂平市| 杭州市| 资溪县| 天等县| 屏山县| 文山县| 临澧县| 安阳市| 桂林市| 兴国县| 晋州市| 涟源市| 锦州市| 县级市| 康定县| 观塘区| 新乡县| 乌兰察布市| 固镇县| 茂名市| 巍山| 海安县| 上杭县| 新邵县| 固始县| 砚山县| 寿光市| 德江县| 交城县| 筠连县| 遵义市| 绥滨县| 宾阳县|