消息队列理解
消息队列提供了一种将一条消息从一个进程发送到另一个进程的方法。每条消息都被认为有一个类型,它可以实现消息的随机查询,消息不必按照先入先出的顺序读取,用户可以根据消息的类型读取它们。
当从消息队列中读取一条消息的时候,消息队列中相应的数据会被删除。此外,它允许一个或多个进程向其写入或读取消息。每个消息队列都有一个消息队列标识,在整个系统中是唯一的;而且消息队列只有在内核重启或是手动删除(命令:ipcrm)消息队列时才会被删除。如果不手动删除消息队列,则消息队列将会一直存在于系统内核中。
消息队列的限制
每条消息的最大长度是上限,系统上所有队列的最大长度也是上限。
每条消息的最大长度为最大值(MSGMAX),每个消息队列的总字节数为最大值(MSGMNB),系统上的消息队列总数也是最大值(MSGMNI)。
宏MSGMNI、MSGMAX和MSGMNB声明于/usr/inclue/linux/msg.h头文件中。其默认值如下所示:
#define MSGMAX 8192 /* <= INT_MAX */ /* max size of message (bytes) */
#define MSGMNI 32000 /* <= IPCMNI */ /* max # of msg queue identifiers */
#define MSGMNB 16384 /* <= INT_MAX */ /* default max size of a message queue */
在/proc/sys/kernel/目录下,有相应的msgmni、msgmax和msgmnb这3个文件:
/proc/sys/kernel/msgmni 规定了消息队列能创建标识符的数量
/proc/sys/kernel/msgmax 单条消息最多写入字节数
/proc/sys/kernel/msgmnb 一条消息队列中最多保存的字节数
原型
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
key_t ftok( const char * fname, int id );//系统IPC键值的格式转换函数,获取key
//key的值,也可约定一个固定值
int msgget(key_t key, int msgflag);// 创建或打开消息队列:成功返回队列ID,失败返回-1
//key: 唯一消息队列的ID,可以使用ftok()获得,也可以人为指定
//msgflg:标识函数的行为,施加权限或者检查既有队列权限的掩码
//IPC_CREAT:如果没有与指定key对应的队列,则创建一个新队列
//IPC_EXCL:同时指定IPC_CREAT时,如果存在key对应的队列则失败
int msgsnd(int msqid, const void *ptr, size_t size, int msgflg);// 添加消息:成功返回0,失败返回-1
//msqid:消息队列的队列ID,msgget()函数的返回值
//ptr:待发送消息结构体的地址。
//size:消息正文的字节数。
//msgflg:函数的控制属性.
//0:msgsnd调用阻塞直到条件满足为止。
//IPC_NOWAIT:若消息没有立即发送则调用该函数的进程会立即返回。
int msgrcv(int msqid, void *ptr, size_t size, long type,int msgflg);// 读取消息:成功返回消息数据的长度,失败返回-1
//msqid:消息队列的队列ID,msgget()函数的返回值
//ptr: 存放消息结构体的地址。
//size: 消息正文的字节数。
//type: 消息的类型、可以有以下几种类型:
//= 0:返回队列中的第一个消息
//> 0:返回队列中消息类型为msgtyp的消息
//< 0:返回队列中消息类型值小于或等于msgtyp绝对值的消息,如果这种消息有若干个,则取类型值最小的消息
//msgflg:函数的控制属性
//0:msgrcv调用阻塞直到接收消息成功为止。
//MSG_NOERROR:若返回的消息字节数比nbytes字节数多,则消息就会截短到nbytes字节,且不通知消息发送进程。
//IPC_NOWAIT:调用进程会立即返回。若没有收到消息则立即返回-1
int msgctl(int msqid, int cmd, struct msqid_ds *buf);// 控制消息队列:成功返回0,失败返回-1
//cmd:函数功能的控制
//IPC_RMID:删除由msqid指示的消息队列,将它从系统中删除并破坏相关数据结构
//IPC_STAT:将msqid相关的数据结构中各个元素的当前值存入到由buf指向的结构中
//IPC_SET:将msqid相关的数据结构中的元素设置为由buf指向的结构中的对应值。
//buf:msqid_ds数据类型的地址,用来存放或更改消息队列的属性。
通信流程
示例
发送方:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <cstring>
typedef struct _iMSG
{
int mtype; //消息类型
char msgInfo[1024];
}MSG;
int main()
{
int msgid;
key_t key = 0xEE;
int msgflg = 0; //调用阻塞直到条件满足为止
MSG bufMsg;
char buf[1024];
//建立消息队列
msgid = msgget(key,IPC_CREAT|0666);
if( msgid < 0)
{
printf("msgget error:");
return -1;
}
//向消息队列中写入数据,输入end结束
while(1){
printf("\n请输入数据:");
scanf("%s",buf);
bufMsg.mtype = 1; //指定消息类型
sprintf(bufMsg.msgInfo,"%s",buf); //指定消息
//写入消息队列
if( msgsnd(msgid,(void *)&bufMsg,strlen(buf)+1,msgflg) < 0){
printf("发送消息失败\n");
}
printf("发送完成\n");
if( strcmp(buf,"end") == 0){
printf("终止发送\n");
break;
}
}
printf("程序结束\n");
return 0;
}
接收方:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <cstring>
typedef struct _iMSG
{
int mtype; //消息类型
char msgInfo[1024];
}MSG;
int main()
{
printf("程序开始\n");
key_t key; //消息队列键值
int msgid; //消息队列标识符
int msgtyp = 1; //读取的消息类型位msgtyp=1的消息
int msgflg = 0; //调用阻塞直到接收消息成功为止
MSG bufMsg;
//获取消息队列
msgid = msgget(key,IPC_CREAT|0666);
if( msgid < 0){
perror("msgget error:");
return -1;
}
//从消息队列中读取消息,遇到end结束
while(1)
{
printf("\n读取消息:\n");
if(msgrcv(msgid,(MSG*)&bufMsg,1024,1,msgflg) < 0){
printf("读取失败\n");
}else{
printf("%s\n",bufMsg.msgInfo);
printf("读取完成\n");
}
if( strcmp(bufMsg.msgInfo,"end") == 0){
printf("终止发接收\n");
break;
}
}
//删除消息队列
if(msgctl(msgid,IPC_RMID,0) < 0){
perror("删除消息队列失败\n");
return -1;
}
printf("删除消息队列\n");
printf("程序结束\n");
return 0;
}