Linux进程间通信-System V IPC
概述
Linux/Unix中有三种System V IPC可以用以进程间通信,分别是消息队列、信号量以及共享内存。IPC资源表示单独的消息队列、共享内存或信号量集合。这三种资源均对应有XXXget()及XXXctl()函数(XXX代表msg、sem、shm三者之一)。下面我们就一一介绍。
IPC对象的共有特性
首先明确一个概念,所谓IPC对象是表示一个单独的消息队列、一个分配好的共享内存或者是一个包含一个或多个信号量的信号量集合。这些对象的共有特性如下:
均有XXXget()函数(XXX代表msg、sem、shm三者之一),且有两个共同参数key和flag。详见后面对具体IPC对象的介绍;
均有XXXctl()函数,且均提供IPC_SET、IPC_STAT、IPC_RMID命令,前两者用来设置或得到IPC对象的状态信息,IPC_RMID用来释放IPC对象资源;
共同的操作模式,都是先通过XXXget()创建一个IPC资源,返回值是该IPC资源的ID。在以后的操作中,均以IPC资源的ID为参数,以对相应的IPC资源进行操作。其他进程可以通过XXXget()取得已有的IPC资源的ID(前提是权限允许)并对其进行操作,从而使进程间通信成为可能;
共同的数据结构,每一类IPC资源都有一个ipc_ids结构的全局变量用来描述同一类资源的公有数据,详见下一小节;
IPC数据结构
在1.1节我们谈到每一类IPC资源都有一个ipc_ids结构的全局变量用来描述同一类资源的公用数据,三种IPC对象对应的三个全局变量分别是semid_ds,msgid_ds和shmid_ds。ipc_ids结构定义如下:
struct ipc_ids{
intsize;/* entries数组的大小 */
intin_use;/* entries数组已使用的元素个数
intmax_id;
unsigned shortseq;
unsigned shortseq_max;
struct semaphoresem; /* 控制对ipc_ids结构的访问 */
spinlock_tary; /* 自旋锁控制对数组entries的访问 */
struct ipc_id*entries;
};
struct ipc_id{
struct kern_ipc_perm *p;
};
数组entries的每一项指向一个kern_ipc_perm结构,kern_ipc_perm结构表示每一个IPC资源的属性,用来控制操作权限。
struct kern_ipc_perm{
key_tkey; /* 用户提供的键,由XXXget函数使用 */
uid_tuid; /* 创建者用户ID */
gid_tgid; /* 创建者组ID */
uid_tcuid; /* 所有者用户ID */
gid_tcgid; /* 所有者组ID */
mode_tmode; /* 操作权限,包括读、写等 */
unsigned long seq;
};
因为每个IPC资源描述符的第一个成员就是kern_ipc_perm结构。因此,我们可以认为数组entries的每一非空项均指向一个IPC资源。
IPC资源ID与entries数组下标的联系
当创建一个IPC资源时
调用函数ipc_addid从相应ipc_ids结构的entries数组中找出第一个未使用的项,然后返回其下标index
返回IPC资源。IPC资源ID=SEQ_MULTIPLIER*seq+indexSEQ_MULTIPLIER是可用资源的最大数目,seq是ipc_ids结构中的seq。每当分配一个IPC资源时,ipc_ids结构中的seq就增1。
当知道IPC资源ID时,可通过IPC资源ID%SEQ_MULTIPLIER得到其在entries数组中的index,从而找到相应的IPC资源。
why保证在一段时间内IPC资源ID的惟一性。
IPC标识符和IPC键值
每个IPC结构在内核中都是通过一个非负整数的标识符来惟一标识,这个标识符是IPC对象对内核可见(即内核维护IPC对象时使用)的内部名。但对于IPC对象而言,其最大的作用是用以进程间通信,那么多个进程如何能够实现对同一IPC对象的操作呢?为了达到这个目的,便需要一个标识符用来让用户态的应用进程可以访问同一个IPC对象,这个标识符叫做IPC对象的外部名,也即我们这里所说的IPC对象的键值key,每个IPC对象都与一个key相关联,于是不同进程便可以通过引用相同的key来操作同一个IPC对象。只要创建一个IPC对象,就必须指定一个key,key的数据类型是基本系统数