一、消息队列的概述
消息队列,列就是一些消息的列表。用户可以从消息队列中添加消息和读取消息等。从这点上看,消息队列具有一定的FIFO 特性,但是它可以实现消息的随机查询,比FIFO 具有更大的优势。同时,这些消息又是存在于内核中的,由“队列ID”来标识。
二、消息队列的一般使用原理和函数说明
1、一般使用原理
消息队列的实现包括创建或打开消息队列、添加消息、读取消息和控制消息队列这4 种操作。其中创建或打开消息队列使用的函数是msgget(),这里创建的消息队列的数量会受到系统消息队列数量的限制;添加消息使用的函数是msgsnd()函数,它把消息添加到已打开的消息队列末尾;读取消息使用的函数是msgrcv(),它把消息从消息队列中取走,与FIFO 不同的是,这里可以指定取走某一条消息;最后控制消息队列使用的函数是msgctl(),它可以完成多项功能。
2、常用函数的说明
a、获得或者创建消息队列的函数
int msgget(key_t key, int msgflg)
key : 消息队列的键值,多个进程可以通过它访问同一个消息队列,其中有个特殊值IPC_PRIVATE。它用于创建当前进程的私有消息队列
msgflg : 权限标志位
返回值:成功返回消息队列ID,失败返回-1
b、添加一个消息到消息队列
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
msgid : 消息队列的队列ID
msgp : 指向消息结构的指针。该消息结构msgbuf 通常为:
struct msgbuf
{
long mtype; /* 消息类型,该结构必须从这个域开始 */
char mtext[1]; /* 消息正文 */
}
msgsz : 消息正文的字节数(不包括消息类型指针变量)
msgflg :IPC_NOWAIT 若消息无法立即发送(比如:当前消息队列已满),函数会立即返回;0:msgsnd 调阻塞直到发送成功为止
返回值:成功返回0,失败返回-1
c、从消息队列中取出一个消息
int msgrcv(int msgid, void *msgp, size_t msgsz, long int msgtyp, int msgflg)
msgid : 消息队列的队列ID
msgp : 消息缓冲区, 同于msgsnd()函数的msgp
msgsz : 消息正文的字节数(不包括消息类型指针变量)
msgtype : 0:接收消息队列中第一个消息;大于0:接收消息队列中第一个类型为msgtyp 的消息;小于0:接收消息队列中第一个类型值不小于msgtyp 绝对值且类型值又最小的消息
msgflg : MSG_NOERROR:若返回的消息比msgsz 字节多,则消息就会截短到msgsz 字节,且不通知消息发送进程;IPC_NOWAIT 若在消息队列中并没有相应类型的消息可以接收,则函数立即返回;0:msgsnd()调用阻塞直到接收一条相应类型的消息为止;
返回值:成功返回0,失败返回-1
d、消息队列的控制函数
int msgctl (int msgqid, int cmd, struct msqid_ds *buf )
msqid : 消息队列的队列ID
cmd : IPC_STAT:读取消息队列的数据结构msqid_ds,并将其存储在buf 指定的地址中;IPC_SET:设置消息队列的数据结构msqid_ds 中的ipc_perm 域(IPC 操作权限描述结构)值。这个值取自buf 参数;IPC_RMID:从系统内核中删除消息队列
buf : 描述消息队列的msgqid_ds 结构类型变量
返回值:成功返回0,失败返回-1
三、测试
编写一个消息队列的测试例程:
msg_rcv.c :创建一个消息队列,并不断的将消息队列中的消息读取出来,没有消息就阻塞等待,接收到quit消息就退出
msg_snd.c : 创建一个消息,将消息添加到消息队列,消息的信息从用户空间传入
msg_rcv.c的具体实现如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <unistd.h>
/* 定义消息的大小 */
#define MESSAGE_SIZE 256
/* 定义一个结构体作为消息的基本单元 */
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[MESSAGE_SIZE]; /* message data */
};
/* 消息队列的接收端,接收从发送端发送过来的消息
*
*/
int main(void)
{
key_t key;
int msgid;
int ret;
struct msgbuf msg;
/* 构造一个消息队列的键值 */
key = ftok(".", 0x03);
if(-1 == key)
{
printf("ftok error!\n");
return -1;
}
/* 创建一个消息队列 */
msgid = msgget(key, 0666 | IPC_CREAT);
if(-1 == msgid)
{
printf("msgget error!\n");
return -1;
}
/* 不停的接收从发送端发来的消息,收到: quit 退出 */
while(1)
{
/* 初始化消息单元 */
memset(msg.mtext, 0, MESSAGE_SIZE);
/* 从消息队列中取出第一个消息 */
ret = msgrcv(msgid, &msg, MESSAGE_SIZE, 0, 0);
if(-1 == ret)
{
printf("msgrcv error!\n");
return -1;
}
/* 打印接收来的消息 */
printf("message form : %ld, message : %s\n", msg.mtype, msg.mtext);
/* 判断接收到的消息是否是 : quit */
if(strncmp(msg.mtext, "quit", 4) == 0)
{
printf("quit!!!\n");
goto out;
}
}
out:
/* 将消息队列从系统中移除 */
ret = msgctl(msgid, IPC_RMID, NULL);
if(-1 == ret)
{
printf("msgctl error!\n");
return -1;
}
return 0;
}
msg_snd.c的具体实现如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <unistd.h>
/* 定义消息的大小 */
#define MESSAGE_SIZE 256
/* 定义一个结构体作为消息的基本单元 */
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[MESSAGE_SIZE]; /* message data */
};
/* 消息队列的发送端,发送一条消息给接收端
* usage : msg_snd <msg>
*/
int main(int argc, char *argv[])
{
key_t key;
int msgid;
int ret;
struct msgbuf msg;
if(2 != argc)
{
printf("usage : %s <msg>\n", argv[0]);
return -1;
}
/* 构造一个消息队列的键值 */
key = ftok(".", 0x03);
if(-1 == key)
{
printf("ftok error!\n");
return -1;
}
/* 获得或者创建一个消息队列 */
msgid = msgget(key, 0666 | IPC_CREAT);
if(-1 == msgid)
{
printf("msgget error!\n");
return -1;
}
/* 初始化消息缓冲区 */
memset(msg.mtext, 0, MESSAGE_SIZE);
/* 构造一个消息单元 */
msg.mtype = getpid(); // 设置消息类型为当前进程的pid
strcpy(msg.mtext, argv[1]); // 将用户输入的消息写入消息队列中
/* 将消息添加到消息队列 */
ret = msgsnd(msgid, &msg, strlen(msg.mtext), 0);
if(-1 == ret)
{
printf("msgsnd error!\n");
return -1;
}
/* 打印发送消息的信息 */
printf("pid : %ld, message : %s\n", msg.mtype, msg.mtext);
return 0;
}
编译并运行结果如下所示: