更多配套资料CSDN地址:点赞+关注,功德无量。更多配套资料,欢迎私信。
物联技术666_嵌入式C语言开发,嵌入式硬件,嵌入式培训笔记-CSDN博客物联技术666擅长嵌入式C语言开发,嵌入式硬件,嵌入式培训笔记,等方面的知识,物联技术666关注机器学习,arm开发,物联网,嵌入式硬件,单片机领域.https://blog.csdn.net/weixin_39804904?type=blog
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{
int size; /* entries数组的大小 */
int in_use; /* entries数组已使用的元素个数
int max_id;
unsigned short seq;
unsigned short seq_max;
struct semaphore sem; /* 控制对ipc_ids结构的访问 */
spinlock_t ary; /* 自旋锁控制对数组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_t key; /* 用户提供的键,由XXXget函数使用 */
uid_t uid; /* 创建者用户ID */
gid_t gid; /* 创建者组ID */
uid_t cuid; /* 所有者用户ID */
gid_t cgid; /* 所有者组ID */
mode_t mode; /* 操作权限,包括读、写等 */
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+index
SEQ_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的数据类型是基本系统数据类型key_t,在Linux2.6.9内核版本中被定义为int类型。一个作为IPC对象外部标识的key被内核转换成内部标识。
ftok()函数
一个IPC对象的key值,可以明确指定为IPC_PRIVATE,但更通常的做法是根据一个路径名和一个项目ID作为传入参数,调用ftok()函数由系统转换为一个key,然后在创建IPC对象时使用此key。
ftok()函数原型如下:
key_t ftok(const char *pathname, int proj_id);
使用这个函数需要包含<sys/types.h>和<sys/ipc.h>两个头文件。这个函数接受两个输入参数pathname和proj_id,分别解释如下。
一个路径名,此路径名必须引用一个实际存在的文件或目录;
项目id,一个整数值,函数只使用此参数的低8位
ftok()函数通过以下方式创建一个key:按指定的路径名取得其stat结构,从该结构中取出部分st_dev和st_ino字段,然后再于proj_id的低8位结合起来。如果两个路径名引用两个不同的文件,那么,对这两个路径名ftok通常返回不同的key。但是,因为i节点号和key通常都存放在长整型中,于是,ftok创建key时可能会丢失信息,这意味着,如果使用同一proj_id,那么对于不同的两个pathname,有可能产生相同的key(这种情况我还没有遇到过,呵呵……哪位朋友遇到了可以告知一声)。绝大多数情况下,我们要指定不同的路径名,至于proj_id,我一直都是设置为0,不知道这样有什么不好,但确实尚未发现问题。
semget/shmget/msgget函数
由ftok函数得到一个key后,便可以调用msgget