说明:只供学习交流,转载请注明出处
一,前言
使用信号和管道进行进程通信存在一定的限制。例如,使用信号传递的信息有限,使用管道虽然能够传输一定量的信息,但是只能传输无格式的字节流。为了解决这些问题,20世纪70年代,AT&T发行的system V版本的Unix中引入了3中新的进程间通信(IPC)机制,分别是:消息队列、共享内存和信号量。在POSIX标准中,这些进程间通信机制被编入POSIX:XSI中。Linux系统支持POSIX标准,自然就支持这几种(IPC)机制。
二,基本概念
1、消息队列简介
顾名思义,消息队列指的是存放消息的队列。消息是指有消息类型和数据的信息,这些信息被存放在预先定义的消息结构中。消息类型可以是私有的(只用于创建消息队列的进程及其子进程访问),也可以是共有的(能够被系统中所有的进程访问)。要让系统中其他进程访问,必须通过一个唯一的标识,这个标识在消息队列中称为消息的Key。不同进程通过向消息队列中写入消息或读取消息来实现进程间的数据交换。
2、信号量简介
信号量用于多进程情况下的进程同步问题。信号量是一个含有整数值的资源,进程通过检测该整数值,来保证其他进程在某个时间不会进行类似的操作。
3、共享内存简介
在Linux系统中,每个进程都运行在自己的虚拟地址空间中。进程之间是不能够访问其他进程的地址空间的。共享内存通过建立一段充许其他进程访问的内存空间,实现资源和数据的共享。进程可以对共享内存空间进行读写操作。当多个进程同时进行写操作时,需要使用信号量控制对资源的访问。
POSIX IPC提供的这3种IPC通信机制都只能用于本机之间的进程通信。要实现不同主机之间的进程通信,可以使用socket套接字或RPC等方式。
三,IPC资源
消息队列、信号量和共享内存都是IPC资源,而在使用IPC资源前,需要创建该资源。与文件属性中有文件所有者、访问权限等信息类似,IPC资源也具体类似的属性。这些属性被保存在结构体ipc_perm中,包含IPC关键字、IPC资源的拥有者和同组用户等信息。
struct ipc_perm
{
__kernel_key_t key; //关键字
__kernel_uid_t uid; //所有者ID
__kernel_gid_t gid; //所有者所属组ID
__kernel_uid_t cuid; //创建者ID
__kernel_gid_t cgid; //创建者所属组ID
__kernel_mode_t mode; //访问权限
unsignedshort seq;
};
四,IPC标识符与关键字
在Linux内核中,为了标识IPC资源,使用了一个非负整数的标识符。只要通过该标识符就可以访问与标识符相关的IPC资源,如消息队列、信号量或共享内存。这一标识被称为IPC标识符。
要创建IPC标识符,需要指定一个关键字。IPC关键字(ipc_perm结构体中的__key)可以通过调用ftok函数获得。ftok函数的具体信息如下表所示:
ftok函数
头文件 | #include <sys/types.h> #include <sys/ipc.h> | ||
函数原型 | key_t ftok(const char *pathname, int proj_id); | ||
返回值 | 成功 | 失败 | 是否设置errno |
产生的IPC关键字 | -1 | 是 |
说明:ftok函数将给定的pathname和proj_id转换成IPC关键字。参数pathname必须指向文件系统中存在的文件或目录。
错误信息:
EACCES:无权限进入pathname中的目录。
ELOOP:解析pathname时存在循环。
ENAMETOOLONG:pathname长度超过PATH_MAX限定,或路径中某个目录长度超过了NAME_MAX限定。
ENOENT:pathname中的某个目录为空。
ENOTDIR:pathname中含有非目录部分。
实例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
int main(void)
{
key_t key;
int proj_id;
char *pathname = "./program";
proj_id = 1;
key = ftok(pathname, proj_id);
if ( key == -1 )
{
perror("Cannot generate the IPC key");
return (1);
}
printf("proj_id = %d pathname = %s IPC key = %d\n", proj_id, pathname, key);
proj_id = 2;
key = ftok(pathname, proj_id);
if (key == -1)
{
perror("Cannot generate the IPC key");
return (1);
}
printf("proj_id = %d pathname = %s IPC key = %d\n", proj_id, pathname, key);
return (0);
}
运行结果:
[root@localhost test]# ./ftok
proj_id = 1 pathname = ./program IPC key = 16912503
proj_id = 2 pathname = ./program IPC key = 33689719
五,基本IPC命令
为了方便地使用IPC通信机制实现进程间的通信,在POSIX:XSI中还定义了查看和删除IPC资源的命令。ipcs命令提供了查看内核中存储的IPC资源的方法,而ipcrm用于从内核中删除指定的IPC资源。
1、ipcs命令
ipcs命令用于显示系统中与POSIX:XSI进程间通信相关的资源信息。ipcs命令常用的参数有q(显示消息队列信息)、s(显示信号量信息)和m(显示共享内存信息)。如果不带任何参数执行ipcs命令,将会显示消息队列、信号量和共享内存三者的信息。
2、ipcrm命令
POSIX标准还提供了从内核中删除IPC资源的系统调用,使用ipcrm可以手工移除IPC资源。
从帮助信息中可以了解到ipcrm的各种使用参数,如下表所示:
参数 | 说明 | 参数 | 说明 |
-q msqid | 移除msqid为指定值的消息队列资源 | -Q mskey | 移除key为mskey的消息队列资源 |
-m shmid | 移除shmid为指定值的共享内存资源 | -M shmkey | 移除key为shmkey的共享内存资源 |
-s semid | 移除semid为指定值的信号量资源 | -S semkey | 移除key为semkey的信号量资源 |
在实际使用中,往往需要将ipcs和ipcrm配合起来使用。删除系统中的消息队列资源的具体步骤如下:
(1):使用ipcs查看系统中IPC资源的情况
(2):根据显示的msqid或key信息,使用ipcrm删除指定的资源。