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机制还支持各自的控制参数,也不赘述了。