System V三種IPC編程巧學巧記

概述

        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機制相關的,經過頭文件的名稱咱們能發現它們一樣知足前面所說的縮寫。學習

 

一脈相承 同宗同源

ftok()

        三個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結構)。

  • key的31~24位爲ftok函數第二個參數proj_id的低8位。
  • key的23~16位爲該文件stat結構中st_dev屬性的低8位。
  • key的15~0位爲該文件stat結構中st_ino屬性的低16位。

 

ipc_perm

        這是一個結構體。他的英文含義是: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機制特性相關的函數。

  • 建立函數(get函數)建立的是IPC對象的標識符(id),它們以ftok生成的鍵(key)爲參數(以及其餘參數)生成。
  • 控制函數(ctl函數)控制的是對應IPC數據結構的成員屬性,從而改變IPC對象的狀態。

get函數 類比印證

        實際上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    若是key不存在,則建立(相似open函數的O_CREAT)
  • IPC_EXCL       若是key存在,則返回失敗(相似open函數的O_EXCL)
  • IPC_NOWAIT  若是須要等待,則直接返回錯誤

一般的用法是 IPC_CREAT|IPC_EXCL ,若是不存在key則建立它,若是已存在則返回失敗(EEXIST)。       

        上面講得是一個類比的記憶與學習方法。另外我還提到了一個印證,指的是和shell的命令相印證,Linux中有三個命令是和System V的三個IPC相關的:

  • ipcmk
  • ipcrm
  • ipcs

其中ipcmk命令用於建立IPC對象,來看一下它的三個主要選項:

選項 描述
-Q 建立一個消息隊列
-S 建立信號量,後跟一參數指明數量
-M 建立共享內存,後跟一參數指明大小

 

        可知建立消息隊列的時候選項後面是沒有參數的,而建立信號量和共享內存的時候選項後面還有一參數(用於指明數量或大小)。正好信號量和共享內存的get函數也比消息隊列多一個。

 

ctl函數 抽絲剝繭

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機制對應的三種數據結構:

  • msqid_ds
  • semid_ds
  • shmid_ds

        它們有共同的後綴——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機制還支持各自的控制參數,也不贅述了。

相關文章
相關標籤/搜索