XSI IPC源自于系統V的IPC功能。
有三種IPC我們稱作XSI IPC,即消息隊列、信號量以及共享存儲器,它們之間有很多相似之處。
1、標識符和鍵
每個內核中的IPC結構(消息隊列、信號量或共享存儲段)都用一個非負整數的標識符(identifier)加以引用。例如,為了對一個消息隊列發送或取消息,只需要知道其隊列標識符。與文件描述符不同,IPC標識符不是小的整數。當一個IPC結構被創建,以后又被刪除時,與這種結構相關的標識符連續加1,直到達到一個整型數的最大正值,然后又回轉到0.
標識符是IPC對象的內部名。為使多個合作進程能夠在同一IPC對象上會合,需要提供一個外部名方案。為此使用了鍵(key),每個IPC對象都與一個鍵相關聯,于是鍵就用作為該對象的外部名。
無論何時創建IPC結構(調用msgget、semget或shmget),都應指定一個鍵,鍵的數據類型是基本系統數據類型key_t,通常在頭文件<sys/types.h>中被定義為長整型。鍵由內核變換成標識符。
有多種方法使客戶進程和服務器進程在同一IPC結構上會合:
(1)服務器進程可以指定鍵IPC_PRIVATE創建一個新IPC結構,將返回的標識符存放在某處(例如一個文件)以便客戶進程取用。鍵IPC_PRIVATE保證服務器進程創建一個新IPC結構。這種技術的缺點是:服務器進程要將整型標識符寫到文件中,此后客戶進程又要讀文件取得此標識符。
IPC_PRIVATE鍵也可以用于父、子進程關系。父進程指定IPC_PRIVATE創建一個新IPC結構,所返回的標識符在調用fork后可由子進程使用。接著,子進程又可將此標識符作為exec函數的一個參數傳給一個新程序。
(2)在一個公用頭文件中定義一個客戶進程和服務器進程都認可的鍵。然后服務器進程指定此鍵創建一個新的IPC結構。這種方法的問題是該鍵可能已與一個IPC結構相結合,在此情況下,get函數(msgget、semget或shmget)出錯返回。服務器進程必須處理這一錯誤,刪除已存在的IPC結構,然后試著再創建它。
(3)客戶進程和服務器進程認同一個路徑名和項目ID(項目ID是0-255之間的字符值),接著調用函數ftok將這兩個值變換為一個鍵。然后在方法(2)中使用此鍵。ftok提供的唯一服務就是由一個路徑名和項目ID產生一個鍵。
#include <sys/ipc.h>key_t ftok(const char *path, int id);返回值:若成功則返回鍵,若出錯則返回(key_t)-1
path參數必須引用一個現存文件。當產生鍵時,只是用id參數的低8位。
ftok創建的鍵通常是下列方式構成的:按給定的路徑名取得其stat結構(http://www.CUOXin.com/nufangrensheng/p/3501385.html),從該結構中取出部分st_dev和st_ino字段,然后再與項目ID組合起來。如果兩個路徑名引用兩個不同的文件,那么,對這兩個路徑名調用ftok通常返回不同的鍵。但是,因為i節點號和鍵通常都存放在長整型中,于是創建鍵時可能會丟失信息(?)。這意味著,如果使用同一項目ID,那么對于不同文件的兩個路徑名可能產生相同的鍵。
三個get函數(msgget、semget和shmget)都有兩個類似的參數:一個key和一個整型flag。如若滿足下列兩個條件之一,則創建一個新的IPC結構(通常由服務器進程創建):
為訪問現存的隊列(通常由客戶進程進行),key必須等于創建該隊列時所指定的鍵,并且不應指定IPC_CREAT。
注意,為了訪問一個現存隊列,決不能指定IPC_PRIVATE作為鍵。因為這是一個特殊的鍵值,它總是用于創建一個新隊列。為了訪問一個用IPC_PRIVATE鍵創建的現存隊列,一定要知道與該隊列相結合的標識符,然后在其他IPC調用中(例如msgsnd和msgrvc)使用該標識符。
如果希望創建一個新的IPC結構,而且要確保不是引用具有同一標識符的一個現行IPC結構,那么必須在flag中同時指定IPC_CREAT和IPC_EXCL位。這樣做了以后,如果IPC結構已經存在就會造成出錯,返回EEXIST(這與指定了O_CREAT和O_EXCL標志的open相類似)。
2、權限結構
XSI IPC為每一個IPC結構設置了一個ipc_perm結構。該結構規定了權限和所有者。它至少包括下列成員:
struct ipc_perm { uid_t uid; /* owner's effective user id */ gid_t gid; /* owner's effective group id */ uid_t cuid; /* creator's effective user id */ gid_t cgid; /* creator's effective group id */ mode_t mode; /* access modes */ ...};
每種實現在其ipc_perm結構中會包括另外一些成員。如欲了解你所用系統中它的完整定義,請參見<sys/ipc.h>。
在創建IPC結構時,對所有字段都賦初值。以后,可以調用msgctl、semctl或shmctl修改uid、gid和mode字段。為了改變這些值,調用進程必須是IPC結構的創建者或超級用戶。更改這些字段類似于對文件調用chown和chmod。
mode字段的值類似于http://www.CUOXin.com/nufangrensheng/p/3502097.html中表4-5所示的值,但是對于任何IPC結構都不存在執行權限。另外,消息隊列和共享存儲使用術語讀(read)和寫(write),而信號量則使用術語讀(read)和更改(alter)。表15-2中對每種IPC說明了6種權限。
某些實現定義了表示每種權限的符號常量,但是這些常量并不包括在Single UNIX Specification中。
3、結構限制
三種形式的XSI IPC都有內置限制(built-in limit)。這些限制的大多數可以通過重新配置內核而加以更改。
在報告和修改限制方面,每種平臺都提供它自己的方法。FreeBSD 5.2.1、linux 2.4.22和Mac OS X 10.3提供了sysctl命令,用該命令觀察和修改內核配置參數。Solaris9修改內核配置參數的方法是,修改文件/etc/system,然后重新啟動。
在Linux中,你可以運行 ipcs -l以顯示IPC相關的限制。
4、優點和缺點
XSI IPC的主要問題是:IPC結構是在系統范圍內起作用的,沒有訪問計數。例如,如果進程創建了一個消息隊列,在該隊列中放入了幾條消息,然后終止,但是消息隊列及其內容并不會被刪除。它們余留在系統中直至出現下述情況:由某個進程調用msgrcv或msgctl讀消息或刪除消息隊列;或某個進程執行ipcrm(1)命令刪除消息隊列;或由正在再啟動的系統刪除消息隊列。將此與管道相比,當最后一個訪問管道的進程終止時,管道就被完全地刪除了。對于FIFO而言,雖然當最后一個引用FIFO的進程終止時其名字仍保留在系統中,直至顯示地刪除它,但是留在FIFO中的數據卻在此時全部被刪除,于是也就徒有其名了。
XSI IPC的另一個問題是:這些IPC結構在文件系統中沒有名字。我們不能用文件I/O和文件和目錄章節中所述的函數來訪問它們或修改它們的特性。為了支持它們不得不增加了十幾條全新的系統調用(msgget、semop、shmat等)。我們不能用ls命令見到IPC對象,不能用rm命令刪除它們,也不能用chmod命令更改它們的訪問權限。于是,就不得不增加新的命令ipcs(1)和ipcrm(1)。
因為這些IPC不使用文件描述符,所以不能對它們使用多路轉接I/O函數:select和poll。這就使得難于一次使用多個IPC結構,以及在文件或設備I/O中使用IPC結構。
表15-3對不同形式IPC的某些特征進行了比較。
表15-3 不同形式IPC之間的特征比較
表15-3中的“無連接”指的是無需先調用某種形式的打開函數就能發送消息的能力。正如前述,因為需要有某種技術以獲得隊列標識符,所以我們并不認為消息隊列具有無連接特性。因為所有這些形式的IPC都限制在單主機上,所以它們都是可靠的。當消息通過網絡傳送時,丟失消息的可能性就要加以考慮。“流控制”指的是:如果系統資源(緩沖區)短缺或者如果接收進程不能再接收更多消息,則發送進程就要休眠。當流控制條件消失時,發送進程應自動被喚醒。
表15-3中沒有表示的一個特征是:IPC設施能否自動地為每個客戶進程創建一個到服務器進程的唯一連接。STREAMS以及UNIX流套接字可以提供這種能力。
本篇博文內容摘自《UNIX環境高級編程》(第二版),僅作個人學習記錄所用。關于本書可參考:http://www.apuebook.com/。
新聞熱點
疑難解答