System V(「系統五」)系統上發明了三種IPC機制(消息隊列、信號量和共享內存),一般稱爲System V IPC。又由於後來被收錄到Unix的XSI標準之中故又稱爲XSI IPC。因此當你看到System V IPC 和 XSI IPC的時候實際上指的是同一種東西。linux
C語言是一門面向過程的語言,與OO(面向對象)語言不一樣,它沒有作到數據和操做的封裝。所以在編寫c語言程序的時候暴露在你面前的是一大堆函數。因爲缺乏OO的那層抽象,增長了記憶時的複雜度,所以若是你對函數分不清的話,經常會使你在編程的時候焦頭爛額。git
本文不會詳實地介紹各個函數的參數、返回值等等這些細節,這些你在Unix或Linux編程的書中應該都能找到。本文的目的是幫助你打通三種IPC之間關係的任督二脈,從而強化理解,減輕記憶難度。github
C語言中全部標識符都是用英文字母組成的,這點毋庸置疑。若是咱們能善於找到各類縮寫的原型,去理解它的英文釋義,就能幫助咱們記憶。shell
好比三個IPC的縮寫:編程
縮寫 | 全寫 | 釋義 |
---|---|---|
msg | message (queue) | 消息隊列 |
sem | semaphore | 信號量 |
shm | shared memory | 共享內存 |
這三個縮寫經常使用在出如今操做函數的函數名之中。此外還出如今對應的頭文件的名稱之中。接下來我還會介紹更多函數名的英文釋義。數據結構
在使用三種IPC機制的時候,咱們確定是經過系統調用,而這些函數所須要的頭文件須要首先搞清楚。System V的IPC操做要用到的頭文件有:函數
#include <sys/types.h> //公共頭文件,聲明瞭key_t類型 #include <sys/ipc.h> //公共頭文件 #include <sys/msg.h> //消息隊列函數的頭文件 #include <sys/sem.h> //信號量函數的頭文件 #include <sys/shm.h> //共享內存函數的頭文件
這裏用到的頭文件都是在sys目錄下的。前面兩個是公共的頭文件,也就是說三種IPC機制都有用到,然後面三個是和具體的IPC機制相關的,經過頭文件的名稱咱們能發現它們一樣知足前面所說的縮寫。學習
三個IPC機制會用到大量的函數,不一樣IPC所用到的函數不一樣可是有一個是相同的——ftok()ui
Unix系統中有個重要的概念叫作:萬物皆文件。在不少IPC機制中的操做都是針對文件描述符(簡稱 fd)的,然而System V卻不一樣,它沒有對fd進行操做,而是針對 IPC對象的id來操做的,而這個id(標識符)又是經過key(鍵)來生成的。spa
三種IPC有各自的函數來生成id,可是它們所利用的key卻都由函數ftok()生成,看一下函數聲明:
#include <sys/types.h> #include <sys/ipc.h> key_t ftok(const char *pathname, int proj_id);
ftok的英文能夠理解爲 file to key 的縮寫。即將文件轉換成key。
參數pathname是文件路徑名(該文件必須存在,一般用當前路徑名 「.」);proj_id被稱做子id,本身指定一個整型。注意若是兩個進程要經過System V的IPC通訊,那麼它們的ftok函數的兩個參數必須相同,這樣才能生成一樣的key,從而產出一樣的id。
返回值類型key_t在<sys/types.h>中被定義,實際是一個整型(32位),該key是路徑名和子id共同做用的結果。這裏要用到該文件路徑的stat結構。(系統中每個文件都有其對應的stat結構)。
這是一個結構體。他的英文含義是:ipc permission(IPC權限)
struct ipc_perm { key_t __key; /* key */ uid_t uid; /* 全部者的有效用戶ID */ gid_t gid; /* 全部者的有效組ID */ uid_t cuid; /* 創造者的有效用戶ID */ gid_t cgid; /* 創造者的有效組ID */ unsigned short mode; /* 權限 */ unsigned short __seq; /* 可忽略 */ };
三種IPC機制都有對應的結構體,這些結構體中有一個共同的成員就是這個ipc_perm,用來標識IPC對象的權限。
不一樣IPC機制之中的不少函數之間有着殊途同歸之妙,學會分類,找到各自的相同點和不一樣點。
分類 | 建立函數 | 控制函數 | 獨立函數 |
---|---|---|---|
消息隊列 | msgget | msgctl | msgsnd,msgrcv |
信號量 | semget | semctl | semop |
共享內存 | shmget | shmctl | shmat,shmdt |
橫着看。能夠清楚的看到同一行的函數名都有同一個頭。這個頭就是IPC機制的縮寫:msg、sem和shm。
豎着看。我把每種IPC函數都分紅三類:建立函數、控制函數和獨立函數。建立函數和控制函數是三種IPC都有的,而獨立函數指的是與具體IPC機制特性相關的函數。
實際上key和id都能惟一地標識一個IPC對象,可是之因此沒有直接對key操做,而是拐彎對id進行操做,是由於id除了能惟一標識IPC對象以外,還包含其餘信息(好比權限)。所以經過get函數生成的id,能夠類比文件描述符(fd),而get函數在功能上來講能夠類比open函數。
只能說IPC的id能夠類比文件描述符fd,實際上它並非fd的一種。不信你能夠寫個程序建立一個消息隊列,而後進入死循環,去/proc/進程id/fd/目錄下面看看有沒有這個id值。fd是進程相關的,進程終止以後fd被釋放,而IPC對象在進程結束以前若是沒有顯示的刪除,那麼及時進程結束了,它還獨立存在。
int msgget(key_t key, int flag); int semget(key_t key, int nsems, int flag); int shmget(key_t key, size_t size, int flag);
這個函數都有一個flag參數(由邏輯或組成),該參數也可類比open函數的flag參數,雖然取值不盡相同。這三個函數的flag取值是同樣的。
一般的用法是 IPC_CREAT|IPC_EXCL ,若是不存在key則建立它,若是已存在則返回失敗(EEXIST)。
上面講得是一個類比的記憶與學習方法。另外我還提到了一個印證,指的是和shell的命令相印證,Linux中有三個命令是和System V的三個IPC相關的:
其中ipcmk命令用於建立IPC對象,來看一下它的三個主要選項:
選項 | 描述 |
---|---|
-Q | 建立一個消息隊列 |
-S | 建立信號量,後跟一參數指明數量 |
-M | 建立共享內存,後跟一參數指明大小 |
可知建立消息隊列的時候選項後面是沒有參數的,而建立信號量和共享內存的時候選項後面還有一參數(用於指明數量或大小)。正好信號量和共享內存的get函數也比消息隊列多一個。
int msgctl(int msqid, int cmd, struct msqid_ds *buf); int semctl(int semid, int semnum, int cmd, ...); //有三參數和四參數兩種,根據cmd的不一樣而不一樣 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
三個ctl控制函數實際上是在操做三種IPC機制對應的三種數據結構:
它們有共同的後綴——id_ds。ds就是data structure(數據結構)的意思。
此處要注意的是消息隊列的對應的結構體名稱,其前綴爲msq而非msg(這個縮寫有點違和,取了隊列的首字母q)
這些結構體中有一個共同的成員就是前面提到的ipc_perm。具體每一個結構體的成員有誰,這裏篇幅有限,不贅述,你們自行百度谷歌,或者去man一下其對應的的控制函數。你們在學習過程當中就要一層一層的抽絲剝繭,看到函數的參數是結構體,就要去探究結構體的成員,看到它的成員也是結構體,那麼就要繼續探究。
這三個函數都有一個cmd參數(控制參數),不一樣的IPC機制它們的控制參數是不同的。可是由幾個控制參數是公共的(定義在ipc.h中)。下面以消息隊列爲例(也適用於信號量和共享內存)
IPC_RMID | 刪除消息隊列。只能由其建立者或超級用戶(root)來刪除 |
IPC_SET | 設置消息隊列的屬性。按照buf指向的結構中的值,來設置此IPC對象 |
IPC_STAT | 讀取消息隊列的屬性。取得此隊列的msqid_ds結構,並存放在buf中 |
IPC_INFO | (只有Linux有)返回系統級的限制,結果放在buf中 |
除此以外,不一樣的IPC機制還支持各自的控制參數,也不贅述了。