- 什么是消息队列?
消息队列是消息的链接表,储存在内核中,由消息队列标识符标识。每个数据块都被认为有一个类型。接受者进程接收的数据块可以有不同的类型值。 - 消息队列的特点
消息队列不同于管道,消息队列是基于数据块的,而管道是基于字节流的,并且消息队列读取不一定要先入先出,可以根据数据类型读取。还有就是因为管道是随进程的,进程结束管道生命周期也就结束,而对于消息队列来说,是随内核的,就算进程退出,不去手动的释放消息队列,消息队列依然是存在的。
可以用下面连个命令查看消息队列和释放消息队列:
ipcs -q //查看
ipcrm -q msgid//删除 - 消息的数据结构
每个队列都有一个msqid_ds结构与其相对应。
消息队列是链式队列,它通过内核提供一个struct msqid_ds *msque[MSGMNI]向量维护内核的洗个消息队列列表,每个msqid_ds表示一个消息队列,系统最多可以用MSGSNI个消息队列。
msg_ds用来设置或返回消息队列的消息,存在与用户空间。
另外还有msg_queue用来描述消息队列头,存在于系统空间。
另外我们可以看到msg_ds的第一个就是一个ipc_perm结构体
IPC对象数据结构:内核为每个IPC对象维护⼀个数据结构(/usr/include/linux/ipc.h)
消息队列,共享内存和信号量都有这样⼀个共同的数据结构。可以通过这个结构体来访问IPC对象的key值。
- 消息队列的创建或访问:msgget函数
该函数用来创建或访问消息队列。原型为:
#include<sys/msg.h>
int msgget(key_t key, int msgflg);
返回值:若成功,返回消息队列ID;若出错返回-1;
与其他IPC机制一样,需要提供一个键值key来命名某个特定的消息队列。可通过ftok()来生成。
msgflg表示消息队列访问权限。可与两个宏配合进行操作:
IPC_CREAT:如果不存在消息队列键值为key那么就创建一个键值为key的消息队列,如果存在,则进行打开此消息队列。
IPC_EXCL:一般与IPC_CREAT一起使用,msgget(key,IPC_CREAT|IPC_EXCL),如果该IPC已存在,则返回-1,一起使用可以保证IPC对象是新创建的不是打开已有对象。
- 将数据放在消息队列中:msgend
#include<sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
返回值:若成功,返回0;若出错,返回-1;
参数:
1)msqid:是msgget返回的消息队列ID。
2)msgp:是一个指向准备发送消息的指针,但是消息的数据结构却有一定的要求,指针msg_ptr所指向的消息结构一定要是以一个长整型成员变量开始的结构体,接收函数将用这个成员来确定消息的类型。例如:
struct mymsgp{
long mtype;
char mtext[512];
};
这向mymsgp结构的指针。
3)msgsz:是msgp指向的消息的长度,注意是消息的长度,而不是整个结构体的长度,也就是说msgsz是不包括长整型消息类型成员变量的长度
4)msgflg:用于控制当前消息队列满或队列消息到达系统范围的限制时将要发生的事情。
如果调用成功,消息数据的一分副本将被放到消息队列中,并返回0,失败时返回-1。
- 从消息队列中取用消息:msgrcv
原型:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
返回值:若成功,返回消息数据部分的长度;若出错,返回-1;
参数:
msqid,msgp,msgsz的作用和函数msgsnd函数的一样
1)msgtyp可以实现简单的接受优先级。
msgtyp==0 返回队列中的第一个消息。
msgtyp>0 返回队列中消息类型为msgtyp的第一个消息。
msgtyp<0 返回队列中消息类型值小于等于msgtyp绝对值的消息,如果有多个,则取类型值最小的消息。
2)msgflg用于控制当队列中没有相应类型的消息时将发生的事情。
- 消息队列控制函数:msgctl
原型:
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
返回值:若成功,返回0;若失败,返回-1
参数:
1)msqid是msgget的返回值。
2)cmd:cmd参数指定将要执行的动作、命令。它可以取三个值。
IPC_STAT:取此消息队列的msqid_ds结构,并将它存放在buf指向的结构里面。
IPC_SET:将字段msg_perm.uid、msg_perm.gid、msg_perm.mode和msg_qbytes从buf指向的结构复制到与这个队列相关的msqid_ds结构中。(执行只能是有效ID等于msg_perm.cuid或uid的进程,或者超级用户权限的进程)
IPC_RMID:从系统中删除该消息队列以及仍在该队列中的所以数据。
三个命令也可用于信号量和共享存储中。
3)buf:buf是指向msqid_ds的结构的指针。
- 代码实例:
msgsend.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/msg.h>
#include<unistd.h>
#include<error.h>
#include<string.h>
struct mymesg{
long mtype;
char mtext[512];
};
int main()
{
int msgid=-1;
msgid=msgget(1234,0666|IPC_CREAT);
struct mymesg data;
if(msgid==-1)
{
perror("msgget is faild!!!\n");
exit(1);
}
while(1)
{
printf("007say:");
fgets(data.mtext,512,stdin);
if(msgsnd(msgid,&data,sizeof(data.mtext),0)==-1)
{
perror("send is faild\n");
exit(1);
}
if(strncmp(data.mtext,"end",3)==0)
{
printf("007 bye");
break;
}
memset(data.mtext,0,sizeof(data.mtext));
if(msgrcv(msgid,&data,sizeof(data.mtext),0,0)==-1)
{
perror("msgrcv is faild\n");
exit(1);
}
printf("008say:%s",data.mtext);
if(strncmp(data.mtext,"end",3)==0)
break;
}
return 0;
}
receive.c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/msg.h>
#include<string.h>
struct mymesg{
long mtype;
char mtext[512];
};
int main()
{
int msgid=-1;
struct mymesg data;
memset(data.mtext,0,sizeof(data.mtext));
msgid=msgget(1234,IPC_EXCL|IPC_CREAT|0666);
if(msgid==-1)
{
perror("msgget is error!!\n");
exit(1);
}
while(1)
{
if(msgrcv(msgid,&data,sizeof(data.mtext),0,0)==-1)
{
perror("msgrcv is faild\n");
exit(1);
}
printf("007:%s",data.mtext);
printf("008:");
memset(data.mtext,0,sizeof(data.mtext));
fgets(data.mtext,512,stdin);
if(msgsnd(msgid,&data,sizeof(data.mtext),0)==-1)
{
perror("send is faild\n");
exit(1);
}
if(strncmp(data.mtext,"end",3)==0)
{
printf("007 bye!\n");
break;
}
}
if(msgctl(msgid,IPC_RMID,0)==-1)
{
perror("msgctl is faild\n");
exit(1);
}
return 0;
}