IPC通信_消息队列
(共享内存,信号量,消息队列等方式时,有System v以及POXIS两种接口类型,这里介绍常用的systemV接口)
内核中的IPC结构(共享内存,信号量,消息队列)都用一个非负整数的标识符(identifier)加以引用。这里的标识符(identifier)是IPC对象的内部名。
为了使多个合作进程能够在同一IPC对象上汇聚,提供一个该IPC对象的外部名键(key),每一个IPC对象都与一个键(key)相关联。
消息队列是消息的链接表,存储在内核中,有消息队列标识符标识。下面描述几个涉及到的API。
函数1:
#include <sys/ipc.h> key_t ftok(const char* path, int id);
该函数的作用就是由一个路径名和项目ID产生一个键。
返回值 : 成功返回键值(key),失败返回-1
参数path:必须是一个存在的文件或者目录,ftok会根据文件inode在系统内的唯一性取一 个值,再结合第二个参数的低8位合成key
参数id :项目ID,是一个0~255之间的字符值,自己约定即可
函数2:
#include <sys/msg.h> int msgget(key_t key, int flag);
该函数的作用是打开一个现有的队列或创建一个新队列,返回队列ID(就是标识符)。
返回值 :成功返回队列ID,失败返回-1
参数key: 键值
参数flag: 设置的权限,设置下表权限(消息队列和共享内存术语“读”和“写”,信号量术语“读”和“更改”),且必须指定IPC_CREAT和IPC_EXCL位(例如:IPC_CREAT |0666)
权限 |
位 |
用户读 用户写(更改) |
0400 0200 |
组读 组写(更改) |
0040 0020 |
其他读 其他写(更改) |
0004 0002 |
IPC_CREAT只置此位 |
不管是否已存在该消息队列,则都返回该消息队列ID,若不存在则创建消息队列 |
IPC_EXCL 只置此位 |
不管有没有该消息队列,msgget()都返回-1 |
IPC_CREAT|IPC_EXCL 都置位 |
如果没有该块消息队列,则创建,并返回消息队列ID。若已有该消息队列,则返回-1 |
函数3:
#include <sys/msg.h> int msgctl(int msqid, int cmd, struct msqid_ds *buf);
该函数对队列执行多种操作。
返回值 :成功返回0,失败返回-1
参数msqid: 队列ID
参数cmd : 指定对msqid队列要执行的命令,见下表
IPC_STAT |
取此队列的msqid_ds结构,并将它存放在buf指向的结构中 |
IPC_SET |
将字段msg_perm.uid、msg_perm.gid、msg_perm.mode和msg_qbytes从buf指向的结构复制到与这个队列相关的msqid_ds结构中。此命令只能有下列两种进程执行: 1、该进程有效用户ID等于msg_perm.cuid或msg_perm.uid 2、该进程具有超级用户特权,只有超级用户才能增加msg_gbytes值 |
IPC_RMID |
从系统中删除该消息队列以及仍在该队列中的所有数据。这种删除立即生效。仍在使用这一消息队列的其他进程在它们下一次试图对此队列进行操作时,将得到EIDRM错误。此命令只能有下列两种进程执行: 1、该进程有效用户ID等于msg_perm.cuid或msg_perm.uid 2、该进程具有超级用户特权 |
参数buf : 每个队列都有与此队列相关的msqid_ds结构体,结构体如下
struct msqid_ds
{
struct msqid_ds {
struct ipc_perm msg_perm;
struct msg *msg_first; /* first message on queue,unused */
struct msg *msg_last; /* last message in queue,unused */
__kernel_time_t msg_stime; /* last msgsnd time */
__kernel_time_t msg_rtime; /* last msgrcv time */
__kernel_time_t msg_ctime; /* last change time */
unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */
unsigned long msg_lqbytes; /* ditto */
unsigned short msg_cbytes; /* current number of bytes on queue */
unsigned short msg_qnum; /* number of messages in queue */
unsigned short msg_qbytes; /* max number of bytes on queue */
__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */
};
函数4:
#include <sys/msg.h> int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag);
发送数据的函数
返回值:成功返回0,失败返回-1
参数msqid: 队列ID
参数 ptr: 指针ptr指向一个长整型数的消息类型,其后紧跟消息数据。可定义下列结构体
struct mymesg
{
long mtype; //消息类型
char mtext[512]; //长度为512的数据
}
参数nbytes: 上面结构体中mtext的长度
参数flag:模式设置,可以设置为IPC_NOWAIT, 非阻塞标志。
当msgsnd返回成功时,消息队列相关的msgid_ds结构会随之更新,表明调用的进程ID(msg_lspid)、调用的时间(msg_stime)以及队列中新增的消息(msg_qnum)。
函数5:
#include <sys/msg.h> int msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag);
接受数据的函数
返回值 :成功返回消息数据的长度,失败返回-1
参数msqid :队列ID
参数ptr : 同发送函数一样
参数nbytes:同接受函数一样,指定缓冲区长度,如果消息长度超过nbytes,且flag中设置了 MSG_NOERROR位,则 该消息会被截断。
参数type : 指定想要哪一种消息
type == 0 |
返回队列中的第一个消息 |
type > 0 |
返回队列中消息类型为type的第一个消息 |
type < 0 |
返回队列中消息类型值小于等于type绝对值的消息,如果有若干个此类消息,则取类型值最小的消息 |
参数flag : 同接受函数
msgrcv成功执行后,内核会更新与该消息队列相关联的msgid_ds结构,以指示调用者的进程ID(msg_lrpid)和调用时间(msg_rtime),并指示队列中的消息数减少了1个(msg_qnum)。
举例:一个进程发送两种消息,另外两个进程分别接受这两个消息。
1、msgSend.cpp
#include <sys/types.h> #include <sys/msg.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <string.h> #include <iostream> #define MSG1 1 #define MSG2 2 #define BUFSIZE 48 struct mybuf { long mytype; char msgtext[BUFSIZE]; }msg; int main() { key_t key = ftok("/tmp/",1);//获取键值 if(key == -1) { printf("ftok failed\n"); return -1; } //获取ID int msgid = msgget(key,IPC_CREAT |0666); if(msgid == -1) { printf("msgget failed\n"); return -1; } int msgType =0; std::string str; while(std::cin>>msgType>>str) { if(msgType == MSG1) { msg.mytype = MSG1; memset(msg.msgtext,0,BUFSIZE); strncpy(msg.msgtext, str.c_str(),BUFSIZE); if(msgsnd(msgid, &msg, str.size(), 0) == -1) {//发送失败 printf("msgsnd failed\n"); return -1; } } else if(msgType == MSG2) { msg.mytype = MSG2; memset(msg.msgtext,0,BUFSIZE); strncpy(msg.msgtext, str.c_str(),BUFSIZE); if(msgsnd(msgid, &msg, str.size(), 0) == -1) {//发送失败 printf("msgsnd failed\n"); return -1; } } sleep(2); } return 0; }
2、msgRcv1.cpp
1 #include <sys/types.h> 2 #include <sys/msg.h> 3 #include <unistd.h> 4 #include <stdio.h> 5 #include <fcntl.h> 6 #include <string.h> 7 #include <iostream> 8 #define MSG1 1 9 #define MSG2 2 10 #define BUFSIZE 48 11 struct mybuf 12 { 13 long mytype; 14 char msgtext[BUFSIZE]; 15 }msg; 16 int main() 17 { 18 key_t key = ftok("/tmp/",1); 19 if(key == -1) 20 { 21 printf("ftok failed\n"); 22 return -1; 23 } 24 int msgid = msgget(key,IPC_CREAT |0666); 25 if(msgid == -1) 26 { 27 printf("msgget failed"); 28 return -1; 29 } 30 while(1) 31 { 32 if(msgrcv(msgid, &msg, BUFSIZE, MSG1, 0) == -1) //接受失败 33 { 34 printf("recive is failure\n"); 35 return -1; 36 } 37 printf(" rcv1:%s\n", msg.msgtext); 38 } 39 printf("rcv end\n"); 40 return 0; 41 } 42
3、msgRcv2.cpp
1 #include <sys/types.h> 2 #include <sys/msg.h> 3 #include <unistd.h> 4 #include <stdio.h> 5 #include <fcntl.h> 6 #include <string.h> 7 #include <iostream> 8 #define MSG1 1 9 #define MSG2 2 10 #define BUFSIZE 48 11 struct mybuf 12 { 13 long mytype; 14 char msgtext[BUFSIZE]; 15 }msg; 16 int main() 17 { 18 key_t key = ftok("/tmp/",1); 19 if(key == -1) 20 { 21 printf("ftok failed\n"); 22 return -1; 23 } 24 int msgid = msgget(key,IPC_CREAT |0666); 25 if(msgid == -1) 26 { 27 printf("msgget failed"); 28 return -1; 29 } 30 while(1) 31 { 32 if(msgrcv(msgid, &msg, BUFSIZE, MSG2, 0) == -1) //接受失败 33 { 34 printf("recive is failure\n"); 35 return -1; 36 } 37 printf(" rcv2:%s\n", msg.msgtext); 38 } 39 printf("rcv end\n"); 40 return 0; 41 } 42