前言:进程间的通信方式包括
IPC:
1、管道
pipe 无名管道
fifo 有名管道
2、信号 signal
3、消息队列 System V消息队列 / POSIX消息队列 <-----------
4、共享内存 System V共享内存 / POSIX共享内存
5、信号量 System V信号量 / POSIX信号量
6、socket套接字
0、System V IPC的一些相关的命令
查看:
ipcs -q 查看消息队列
ipcs -m 查看共享内存
ipcs -s 查看信号量
ipcs -a 查看所有的IPC
删除:
ipcrm -q 消息队列id / ipcrm -Q 键值 删除消息队列
ipcrm -m 共享内存id / ipcrm -Q 键值 删除共享内存
ipcrm -s 信号量id / ipcrm -Q 键值 删除信号量
1、什么是消息队列
System V 消息队列 : 实现为一个带头结点的链表 对于系统中的每一个消息队列,内核维护在一个消息结构体中 struct msqid_ds {} (man msgctl )
struct msqid_ds {struct ipc_perm msg_perm; /* 该结构体的读写权限 Ownership and permissions */time_t msg_stime; /* 最后发送消息的时间 Time of last msgsnd(2) */time_t msg_rtime; /* 最后接收消息的时间 Time of last msgrcv(2) */time_t msg_ctime; /* 最后修改消息结构体的时间 Time of last change */unsigned long __msg_cbytes; /* 当前消息队列中消息的总字节数 Current number of bytes in queue (nonstandard) */msgqnum_t msg_qnum; /* 当前消息队列中 消息的数量 Current number of messages in queue */msglen_t msg_qbytes; /* 消息队列中 允许的最大字的节数 Maximum number of bytes allowed in queue */pid_t msg_lspid; /* 最后发送消息的进程id PID of last msgsnd(2) */pid_t msg_lrpid; /* 最后接收消息的进程id PID of last msgrcv(2) */};
2、 System V 消息队列 相关的接口函数
1)申请 System V IPC 的键值 key
键值的作用 就是用来唯一标识一个 System V IPC 的对象 ,用类型 key_t 来表示 。
ftok()
NAME
ftok - convert a pathname and a project identifier to a System V IPC key
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
功能:用一个路径名 和 一个整数 生成一个键值key
参数:
pathname: 指定一个已经存在的路径名 (例如: /home/china/ )
proj_id: 一个整数
返回值:
成功,返回一个IPC 键值key
失败,返回-1,同时errno被设置
注意:
调用ftok()函数多次,如果两个参数与之前一致,那么生成的键值key也相同
2)创建 或者 打开 消息队列 msgget
NAME
msgget - get a System V message queue identifier
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
功能:用来创建或者打开一个消息队列
参数:
key: 一个IPC 键值key
msgflg: 标志位
(1)创建标志
IPC_CREAT | 权限
例子:
IPC_CREAT | 0666
注意:
如果创建失败的原因 是因为已经存在了
且 创建的标志为 IPC_CREAT | IPC_EXCL 一起使用
此时 errno == EEXIST
(2)打开标志
0
返回值:
成功,返回一个已经打开的消息队列id
失败,返回-1,同时errno被设置
创建一个 System V 消息队列, 打印其 键值 和 id 然后 ipcs -q 查看消息队列 #define PATHNAME "/home/china/"int main() {//1.获取键值 key_t key = ftok( PATHNAME, 2 );if( key == -1 ){perror("ftok error ");return -1;}printf("key = %d\n", key );//2.创建消息队列 int msg_id = msgget( key, IPC_CREAT | IPC_EXCL | 0666 ); //不存在,则创建打开if( msg_id == -1 ){if( errno == EEXIST ) //如果存在,就直接打开{msg_id = msgget( key, 0 );}else {perror("msgget error ");return -1;}}printf("msg_id = %d\n", msg_id );}
3)发送/接收消息
NAME
msgrcv, msgsnd - System V message queue operations
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:用来发送一个消息到指定的消息队列上
参数:
msqid: 一个已经打开的消息队列id
msgp: 指向要发送的消息结构体的指针
struct msgbuf //在自己代码中 根据实际情况重新定义
{
long mtype; /* 消息类型(可以看做成编号) message type, must be > 0 */
char mtext[1]; /* 消息的内容,可长可短 message data */
};
msgsz:消息内容的实际的长度 (上述结构体中 metxt数据的实际的大小)
msgflg:发送标志
0 阻塞模式(默认)
IPC_NOWAIT 非阻塞模式 立即返回,且 errno == EAGAIN
返回值:
成功,返回0
失败,返回-1,同时errno被设置
发送一个消息到指定的消息队列上 struct msgbuf {long mtype; //消息类型(可以看做成编号) char mtext[64]; //消息内容 };int main(){//1.获取键值 //2.创建消息队列 //3.发送消息 struct msgbuf buf;buf.mtype = 1001;fgets( buf.mtext, 64, stdin ); int re = msgsnd( msg_id, &buf, sizeof(buf.mtext), 0 );if( re == -1 ){perror("msgsnd error ");return -1;}}
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
功能:用来从一个消息队列上去接收一个消息
参数:
msqid: 一个已经打开的消息队列id
msgp: 指针,指向的空间用来保存接收到的消息
msgsz: msgp指针 指向的空间的大小,最多能够存放多少字节的消息
msgtyp: 接收消息的类型
如果为0,表示接收任意类型消息
msgflg:接收标志
0 阻塞模式(默认)
IPC_NOWAIT 非阻塞模式 立即返回,且 errno == EAGAIN
返回值:
成功,返回实际接收到的字节数
失败,返回-1,同时errno被设置
利用消息队列,实现进程间通信 msgsnd.c 发送消息 msgrcv.c 接收消息 #define PATHNAME "/home/china/"struct msgbuf {long mtype; //消息类型(可以看做成编号) char mtext[64]; //消息内容 };int main(){//1.获取键值 key_t key = ftok( PATHNAME, 2 );if( key == -1 ){perror("ftok error ");return -1;}printf("key = 0x%x\n", key );//2.创建或打开一个消息队列 int msg_id = msgget( key, IPC_CREAT | IPC_EXCL | 0666 ); //不存在,则创建打开if( msg_id == -1 ){if( errno == EEXIST ) //如果存在,就直接打开{msg_id = msgget( key, 0 );}else {perror("msgget error ");return -1;}}printf("msg_id = %d\n", msg_id );//3.发送消息 / 接收消息 struct msgbuf buf;buf.mtype = 1002;fgets( buf.mtext, 64, stdin ); int re = msgsnd( msg_id, &buf, sizeof(buf.mtext), 0 );if( re == -1 ){perror("msgsnd error ");return -1;}}
4)消息队列的控制操作 msgctl
NAME
msgctl - System V message control operations
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:用来对消息队列进行控制操作
参数:
msqid: 一个已经打开的消息队列id
cmd: 控制命令
IPC_RMID 删除消息队列
IPC_STAT 获取消息队列的属性
IPC_SET 设置消息队列的属性
...
buf: 指向一个msqid_ds结构体的指针,用来保存或修改消息队列的属性
根据cmd命令的不同,第三个参数有不同的情况
如果 cmd == IPC_RMID , 那么就填 NULL
返回值:
成功,返回0
失败,返回-1,同时errno被设置
获取消息队列的属性,打印 该消息队列中的消息的总字节数 和 消息的数量 #define PATHNAME "/home/china/"int main(){//1.获取键值 key_t key = ftok( PATHNAME, 2 );if( key == -1 ){perror("ftok error ");return -1;}printf("key = 0x%x\n", key );//2.创建 或 打开一个消息队列 int msg_id = msgget( key, IPC_CREAT | IPC_EXCL | 0666 ); //不存在,则创建打开if( msg_id == -1 ){if( errno == EEXIST ) //如果存在,就直接打开{msg_id = msgget( key, 0 );}else {perror("msgget error ");return -1;}}printf("msg_id = %d\n", msg_id );//3.获取消息队列的属性 struct msqid_ds stat;int re = msgctl( msg_id, IPC_STAT, &stat );if( re == -1 ){perror("msgctl error ");return -1;}//打印 该消息队列中的消息的总字节数 和 消息的数量 printf("msg_cbytes = %ld\n", stat.__msg_cbytes );printf("msg_qnum = %ld\n", stat.msg_qnum );}