一、前言
消息队列是一种以链表形式组织的数据结构,存放于内核中。消息队列通过唯一的IPC标识符表示。
相较于管道,消息队列更加灵活,它可以做到消息的类型的多样化。
它由如下特点:
消息队列发送的是一个自定义的数据块。
消息队列建立后可以独立于进程而存在。
可以根据消息类型有选择的接收信息。
二、常用函数
1、msgbuf
首先需要了解消息队列的模板
struct msgbuf
{
long mtype; /*type of message, must>0*/
char mtext[1]; /*message text*/
};
数据结构msgbuf中有两个成员
mtype是指消息的类型,必须为长整型,必须是大于0。
mtext是消息数据,类型不仅仅限于字符型,也可以是其他任意类型,用户可以自己定义。
2、msgset
系统调用msgget()用于创建一个新的消息队列,或者存取一个已经存在的消息队列,其函数原型是:
int msgget(key_t key, int msgflg);
参数key是唯一标识消息队列的关键字,参数msgflg是一些标志,包括:
PC_CREAT:如果内核中没有此队列,则创建它。
IPC_EXCL:当和IPC_CREAT一起使用时,如果队列已经存在,则返回错误。
3、msgsnd
当由上面函数获得消息队列标识符,就可以发送消息了,msgsnd()用来向消息队列添加消息,原型为:int msgsnd(int msqid, cosnt void *msgp, size_t msg_sz, int msgflg);
msgid是由msgget函数返回的消息队列标识符。
msgp是指向准备发送消息的指针,即以msgbuf为原型的数据结构
msg_sz是消息的长度,但不是整个结构体的长度,它不包括消息类型。
一般可以表示为length = sizeof(struct mymsgbuf) - sizeof(long);
msgflg可以设为:
0:此时为忽略此参数,如果消息队列已满,调用进程将会挂起,直到消息可以写入到队列中。
用来从一个消息队列中获取消息,原型为:
IPC_NOWAIT:如果消息队列己满,那么此消息则不会写入到消息队列中,控制将返回到调用进程中。消息队列写入成功时,函数返回0,否则返回-1。
4、msgrcv
int msgrcv(int msgid, void *msgp, size_t msg_sz, long int msgtype, int msgflg);
前三个参数msgid,msggp,msg_sz,和msgsnd中的含义相同。
msgtype是要从消息队列读取消息的类型
若msgtype=0,接收消息队列的第一个消息,大于0则接收队列中消息类型等于这个值的第一个消息。小于0接收消息队列中小于或者等于msgtype绝对值的所有消息中的最小一个消息。一般为0。
通过msgctl()可以对消息队列进行控制或者一些属性的修改,其函数原型为:
msgflg用于控制当队列中没有相应类型的消息可以接收时将发生的事情。
调用成功时,该函数返回放到接收缓存区中的字节数,消息被复制到由msg_ptr指向的用户分配的缓存区中,然后删除消息队列中的对应消息。失败时返回-1.
5、msgctl
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
第一个参数是消息队列的标识符,第二个参数cmd指定了操作,下面是几个常用的操作:
IPC_STAT:读取消息队列的数据结构msqid_ds,并将其存储在buf指定的地址中。
IPC_SET:设置消息队列的数据结构msqid_ds中的ipc_perm、msg_qbytes、msg_ctime元素的值。这个值取自buf参数。
IPC_RMID:从系统内核中移走消息队列。
消息队列标识符的属性被记录在一个msgid_ds结构体:
struct msqid_ds{
struct ipc_perm msg_perm; /*所有者和权限*/
time_t msg_stime; /*最后一次向队列发送消息的时间*/
time_t msg_rtime; /*最后一次从队列接收消息的时间*/
time_t msg_ctime; /*队列最后一次改动的时问*/
unsigned long __msg_cbytes; /*当前队列所有消息的总长度*/
msgqnum_t msg_qnum; /*当前队列中的消息数量*/
msglen_t msg_qbytes; /*消息队列的最大消息总长度*/
pid_t msg_lspid; /*最一次给队列发送消息的进程PID*/
pid_t msg_lrpid; /*最后一次从队列接收消息的进程PID*/
}
三、消息队列实现进程间通信
发送端源程序
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <errno.h>
#include <iostream>
using namespace std;
const int MAX_SEND_SIZE = 1024;
struct msgbuf_m
{
long int mtype;
char mtext[MAX_SEND_SIZE];
msgbuf_m()
{
memset(mtext,0,sizeof(mtext));
}
int size()
{
return strlen(mtext)+1;
}
};
int main()
{
int msg_id = msgget((key_t)1111,0666 | IPC_CREAT);
if(msg_id == -1)
{
cerr << "msgget failed with error: " << errno << endl;
exit(1);
}
msgbuf_m data;
char buf[MAX_SEND_SIZE];
while(1)
{
cout << ">";
memset(buf,0,sizeof(buf));
cin.getline(buf,MAX_SEND_SIZE);
data.mtype = 1;
strcpy(data.mtext,buf);
if(msgsnd(msg_id,(void *)&data,data.size(),0) == -1)
{
cerr << "msgsnd error" << endl;
exit(1);
}
if(strncmp(buf,"quit",4)==0)
break;
}
return 0;
}
接收端源程序
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <errno.h>
#include <iostream>
using namespace std;
const int MAX_RECV_SIZE = 1024;
struct msgbuf_m
{
long int mtype;
char mtext[MAX_RECV_SIZE];
};
int main()
{
int msg_id = msgget((key_t)1111,0666 | IPC_CREAT);
if(msg_id == -1)
{
cerr << "msgget failed with error: " << errno << endl;
exit(1);
}
msgbuf_m data;
while(1)
{
if(msgrcv(msg_id,(void *)&data,MAX_RECV_SIZE,0,0) == -1)
{
cerr << "msgrcv error" << endl;
exit(1);
}
cout << "receive data: " << data.mtext << endl;
if(strncmp(data.mtext,"quit",4) == 0)
break;
}
if(msgctl(msg_id,IPC_RMID,0) == -1)
{
cerr << "msgctl remove failed" << endl;
exit(1);
}
return 0;
}