一、定义
消息队列提供了从一个进程向另一个进程发送一个数据块的方法。每个数据块都被人会含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞的问题。但是消息队列和命名管道一样,每个数据块都有一个最大长度的限制。
linux使用宏MSGMAX和MSGMNB来限制一条消息的最大长度和一个队列的最大长度。
1、每种通信方式实现方式和功能不一样,带来适用的场景也有所不同:
消息队列是链表队列,它通过内核提供个 struct msgid_ds *msgque[MSGMIN]向量维护内核的一个消息队列列表,因此linux系统支持的最大消息列数由msgque大小来决定,每一个msqid_ds表示一个消息队列,并通过msgid_ds.msg_first、msg_last维护一个先进先出的msg链表队列,当发送一个消息队列时,把发送的消息构造成一个msg结构对象,并添加到msqid_ds.msg_first、msg_last维护的链表队列,同样,接受消息的时候也是从msg链表队列尾部查找到一个msg_type匹配的msg节点,从链表中删除该msg节点,并修改msgqid_ds结构对象的数据。
2、消息队列key的获取
若程序要使用消息队列,必须要能知道消息队列key,因为应用进程无法直接访问内核消息队列中的数据结构,因此需要一个消息队列的标志,让应用进程知道当前操作的是哪个消息队列,同时也要保证每个消息队列key值的唯一性。
二、消息队列的使用
1、创建或者使用消息队列:msgget函数
该函数用来创建和访问一个消息队列原型为:
int msgget(key_t key, int msgflag);
与其他的ipc机制一样,程序必须提供一个键来命名某个特定的消息队列。msgflag是一个权限标志,表示消息队列的访问权限,它与文件的访问权限一样。msgflag可以与IPC_CREATE做或(|)操作,表示当key所命名的消息队列不存在的时候创建一个消息队列,如果消息队列存在,IPC_CREATE标志会被忽略,返回一个标识符。
在程序中若要使用消息队列,必须要能知道消息队列的key,因为应用程序无法直接访问内核消息队列中的数据结构,因此需要一个消息队列的标识,让应用程序知道当前操作是哪个消息队列,同时也要保证每个消息队列key值的唯一性。
申请一块内存,创建一个新的消息队列(数据结构msqid_ds),将其初始化后加入到msgque向量表中的某个空缺位置处,返回标识符。或者在msgque向量表中找键值为key的消息队列。
2、将消息添加到消息队列中
int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflag);
msgid是由msgget函数返回的消息队列标识符
msg_ptr是一个指向准备发送消息的指针,但是消息的数据结构却有一定的要求,指针msg_ptr所指向的消息结构一定要是以一个长整型成员变量开始的结构体,接受函数将用这个成员来确定消息的类型。所以消息结构要定义成这样:
struct my_message{
long int message_type;
};
msg_sz是msg_ptr指向的消息的长度,注意是消息的长度,而不是结构体的长度。
msgflag用于控制当前消息队列满或队列消息到达系统范围的限制时将要发生的事情。如果调用成功,消息数据的一分副本将被放到消息队列中,并返回0,失败时返回1;
3、从一个消息队列中获取消息
int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);
msgid,msg_ptr,msg_st的作用和函数msgsnd函数一样。
msgtype可以实现一种简单的接收优先级。如果msgtype为0,就获取队列中的第一个消息。如果它的值大于零,将获取具有相同消息类型的第一个信息。如果它小于零,就获取类型等于或者小于msgtype的绝对值的第一个消息。
msgflg用于控制当队列中没有相应类型的消息可以接收时发生的事情
4、消息队列控制函数
int msgrcv(int msgid, int command, struct msgid_ds *buf);
command是将要采取的动作,它可以取三个值,
IPC_STAT:把msg_ds结构中的数据设置为消息队列的当前关联值,即用消息队列的当前关联值设置为msgid_ds结构中给出的值
IPC_SET:如果进程有足够的权限,就把消息队列的当前关联值设置为msgid_ds结构中给出的值
IPC_RMID:删除消息队列
buf是指向msgid_ds结构的指针,它指向消息队列模式和访问权限的结构。msgid_ds至少包括以下成员
send.c
struct msgid_ds{
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
};
成功时候返回0,失败时候返回-1;
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<errno.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#define MSGKEY 1024
struct msgstru
{
long msgtype;
char msgtext[2048];
};
int main()
{
struct msgstru msgs;
int msg_type;
char str[256];
int ret_value;
int msqid;
msqid = msgget(MSGKEY, IPC_EXCL);//检查消息队列是否存在
if (msqid < 0)
{
msqid = msgget(MSGKEY, IPC_CREAT | 0666);//创建消息队列
if (msqid < 0)
{
printf("fail to create msq | errno = %d[%s]\n", errno, strerror(errno));
exit(-1);
}
}
while (1)
{
printf("input message type(end:0): ");;
scanf("%d", &msg_type);
if (msg_type == 0)
{
break;
}
printf("input message to be sent: ");
scanf("%s", str);
msgs.msgtype = msg_type;
strcpy(msgs.msgtext, str);
ret_value = msgsnd(msqid, &msgs, sizeof(struct msgstru), IPC_NOWAIT);
if (ret_value < 0)
{
printf("msgsnd() write msg failed, errno = %d[%s]\n", errno, strerror(errno));
exit(-1);
}
}
msgctl(msqid, IPC_RMID, 0);
}
receive.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<errno.h>
#include<string.h>
#include<unistd.h>
#define MSGKEY 1024
struct msgstru
{
long msgtype;
char msgtext[2048];
};
void childproc()
{
struct msgstru msgs;
int msgid, ret_value;
char str[512];
while (1)
{
msgid = msgget(MSGKEY, IPC_EXCL);//检查消息队列是否存在
if (msgid < 0)
{
printf("msq not existed!! errno = %d[%s]\n", errno, strerror(errno));
sleep(2);
continue;
}
ret_value = msgrcv(msgid, &msgs, sizeof(struct msgstru), 0, 0);
printf("text = [%s] pid = [%d]\n", msgs.msgtext, getpid());
}
return;
}
int main()
{
int i, cpid;
for (i=0; i<5; i++)
{
cpid = fork();
if (cpid < 0)
{
printf("fork failed\n");
}
else if (cpid == 0)
{
childproc();
}
}
}