Linux进程间通信-消息队列
消息队列是消息的链表,存放在内核中并由消息队列标识符标识,消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺陷。消息队列包括POSIX消息队列和System V消息队列。
消息队列是UNIX下不同进程之间实现共享资源的一种机制,UNIX允许不同进程将格式化的数据流以消息队列形式发送给任意进程,有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。
相关函数
- msgget函数原型
**函数原型:**int msgget(key_t key, int msgflg)
**函数说明:**得到消息队列标识符或创建一个消息队列对象并返回消息队列标识符
参数说明:
key:
0(IPC_PRIVATE):
会建立新的消息队列;
大于0的32位整数:
视参数msgflg来确定操作。通常要求此值来源于ftok返回的IPC键值 msgflg:
0:
取消息队列标识符,若不存在则函数会报错;
IPC_CREAT:
当msgflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列,返回此消息队列的标识符
IPC_CREAT|IPC_EXCL:
如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列则报错函数返回值:
成功:
返回消息队列的标识符;出错:
-1,错误原因存于error中
- msgctl函数原型
**函数原型:**int msgctl(int msqid, int cmd, struct msqid_ds *buf)
**函数说明:**获取和设置消息队列的属性
参数说明:
msqid:消息队列标识符
cmd:
IPC_STAT:
获得msgid的消息队列头数据到buf中
IPC_SET:
设置消息队列的属性,要设置的属性需先存储在buf中,可设置的属性包括:sg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes buf:消息队列管理结构体,请参见消息队列内核结构说明部分
函数返回值:
成功:
返回0;出错:
-1,错误原因存于error中
- msgsnd函数原型
**函数原型:**int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
**函数说明:**将msgp消息写入到标识符为msqid的消息队列
参数说明:
msqid:消息队列标识符
msgp:发送给队列的消息。msgp可以是任何类型的结构体,但第一个字段必须为long类型,即表明此发送消息的类型,msgrcv根据此接收消息。
msgsz:要发送消息的大小,不含消息类型占用的4个字节,即mtext的长度
msgflg:
0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列
IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回
IPC_NOERROR:若发送的消息大于size字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程。
函数返回值:
成功:
返回0;出错:
-1,错误原因存于error中
- msgrcv函数原型
**函数原型:**ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
**函数说明:**从标识符为msqid的消息队列读取消息并存于msgp中,读取后把此消息从消息队列中删除
参数说明:
msqid:消息队列标识符
msgp:发送给队列的消息。msgp可以是任何类型的结构体,但第一个字段必须为long类型,即表明此发送消息的类型,msgrcv根据此接收消息。
msgsz:要发送消息的大小,不含消息类型占用的4个字节
msgtyp:
0:接收第一个消息
>0:接收类型等于msgtyp的第一个消息
<0:接收类型等于或者小于msgtyp绝对值的第一个消息
msgflg:
0: 阻塞式接收消息,没有该类型的消息msgrcv函数一直阻塞等待
IPC_NOWAIT:如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG
IPC_EXCEPT:与msgtype配合使用返回队列中第一个类型不为msgtype的消息
IPC_NOERROR:如果队列中满足条件的消息内容大于所请求的size字节,则把该消息截断,截断部分将被丢弃
函数返回值:
成功:
实际读取到的消息数据长度;出错:
-1,错误原因存于error中
代码演示
发送消息队列代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
struct msg{
long type;
long msgtype;
unsigned char text[4];
};
void menu(void);
int test_send1(struct msg msgbuf, int msgid);
int test_send2(struct msg msgbuf, int msgid);
int test_send3(struct msg msgbuf, int msgid);
void menu(void){
printf("========================\n");
printf("|| 1.send1 test ||\n");
printf("|| 2.send2 test ||\n");
printf("|| 2.send3 test ||\n");
printf("|| 0.exit ||\n");
printf("========================\n");
}
int main(int argc, char * argv[])
{
int choose;
int msgid;
key_t key;
struct msg msgbuf;
if((key = ftok("/tmp", 'g')) < 0){
perror("ftok");
exit(1);
}
printf("msg_key:%d\n", key);
if((msgid = msgget(key, 0666)) < 0){
perror("msgget");
exit(1);
}
printf("send msgid:%d\n", msgid);
bzero(msgbuf.text, sizeof(msgbuf.text));
menu();
while(1){
printf("please input:");
scanf("%d", &choose);
switch(choose){
case 1:
test_send1(msgbuf, msgid);
break;
case 2:
test_send2(msgbuf, msgid);
break;
case 3:
test_send3(msgbuf, msgid);
break;
case 0:
return 0;
default:
printf("please input valid value.\n");
return -1;
}
}
return 0;
}
int test_send1(struct msg msgbuf, int msgid){
memset(&msgbuf, 0, sizeof(msgbuf));
msgbuf.type = 1L;
msgbuf.msgtype = 1L;
msgbuf.text[0] = 0x40;
msgsnd(msgid, &msgbuf, sizeof(msgbuf)-sizeof(long), 0);
return 0;
}
int test_send2(struct msg msgbuf, int msgid){
memset(&msgbuf, 0, sizeof(msgbuf));
msgbuf.type = 1L;
msgbuf.msgtype = 2L;
msgbuf.text[0] = 0x61;
msgsnd(msgid, &msgbuf, sizeof(msgbuf)-sizeof(long), 0);
return 0;
}
int test_send3(struct msg msgbuf, int msgid){
memset(&msgbuf, 0, sizeof(msgbuf));
msgbuf.type = 2L;
msgbuf.msgtype = 2L;
msgbuf.text[0] = 0x81;
msgsnd(msgid, &msgbuf, sizeof(msgbuf)-sizeof(long), 0);
return 0;
}
接收消息队列代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "data_global.h"
// extern int msgid;
// extern key_t msg_key;
struct msg{
long type;
long msgtype;
unsigned char text[4];
};
int main(int argc, char * argv[]){
key_t msg_key;
int msgid;
struct msg msgbuf;
if((msg_key = ftok("/tmp", 'g')) < 0){
perror("receive-ftok");
exit(-1);
}
printf("msg_key:%d\n", msg_key);
msgid = msgget(msg_key, IPC_CREAT|0666);
if(msgid == -1){
perror("Error:");
if(errno == EEXIST){
msgid = msgget(msg_key,0777);
}else{
perror("fail to msgget");
exit(1);
}
}
printf("receive msgid:%d.\n", msgid);
sleep(1);
while(1){
bzero(&msgbuf, sizeof(msgbuf));
printf(">>>>>>>>>>wait from testqt\n");
msgrcv(msgid, &msgbuf, sizeof(msgbuf)-sizeof(long), 1L, 0);
printf("Get %ldL msg.\n", msgbuf.msgtype);
printf("test[0] = %#x\n", msgbuf.text[0]);
}
}
现象说明
代码运行的现象如图所示,需要说明的是:
- 我们对消息类型进行了定义,那么由于接收端msgrcv函数的msgtyp为1L,而发送端test_send3封装的msgbuf.type=2L,所以该信息是不会进行读取的
代码运行以后,执行ipcs
以下命令,可以查看创建的消息队列的msgid:
通过ipcrm -q msgid
命令,可以删除对应的msgid:
消息队列相关操作更加详细的内容,可以参考以下博客: