原理说明:
消息队列是内核地址空间中的内部链表,通过linux内核在各个进程之间传递内容,消息顺序地发送到消息队列中,并且以几种不同的方式
从队列中获取,每个消息队列可以用IPC标识符唯一的进行标识,内核中的消息队列是通过IPC的标识符来区别的,不同的消息队列之间是
相互独立的,每个消息队列中的消息又构成一个独立的链表.
消息队列中的数据结构
1、消息缓冲结构
向消息队列发送消息时,必须组成合理的数据结构。Linux系统定义了一个模版数据结构msgbuf:
#include<linux/msg.h>
struct msgbuf{
long type;
char mtext[1];
}
其中type表示消息的类型,以正数表示。mtext是该消息的数据,并不一定就是char 类型,任意类型都可以的。
2、msqid_ds内核数据结构。
struct msqid_ds{
struct ipc_perm msg_perm;
time_t msg_stime;
time_t msg_rtime;
time_t msg_ctime;
unsigned long _msg_cbuyes;
..........
};
Linux内核中,每个消息队列都维护一个结构体,此结构体保存着消息队列当前状态信息,该结构体在头文件linux/msg.h中定义。
3、ipc_perm内核数据结构
struct ipc_perm{
key_t key;
uid_t uid;
gid_t gid;
.......
};
结构体ipc_perm保存着消息队列的一些重要的信息,比如说消息队列关联的键值,消息队列的用户id组id等。它定义在头文件linux/ipc.h中
Linux的消息队列(queue)实质上是一个链表, 它有消息队列标识符(queue ID). msgget创建一个新队列或打开一个存在的队列; msgsnd向队列末端
添加一条新消息; msgrcv从队列中取消息, 取消息是不一定遵循先进先出的, 也可以按消息的类型字段取消息.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
参数key是一个键值,由ftok获得;msgflg参数是一些标志位。该调用返回与健值key相对应的消息队列描述字,如果没有消息队列与健值key相对应,
并且msgflg中包含了IPC_CREAT标志位或者key参数为IPC_PRIVATE,那么该调用将创建一个新的消息队列。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv(int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg);
该系统调用从msgid代表的消息队列中读取一个消息,并把消息存储在msgp指向的msgbuf结构中。
msqid为消息队列描述字;消息返回后存储在msgp指向的地址,msgsz指定msgbuf的mtext成员的长度(即消息内容的长度),msgtyp为请求读
取的消息类型,成功返回读出消息的实际字节数,否则返回-1。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg);
向msgid代表的消息队列发送一个消息,即将发送的消息存储在msgp指向的msgbuf结构中,消息的大小由msgze指定。
对发送消息来说,有意义的msgflg标志为IPC_NOWAIT,指明在消息队列没有足够空间容纳要发送的消息时,msgsnd是否等待。成功返回0,否则
返回-1。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
该系统调用对由msqid标识的消息队列执行cmd操作,共有三种cmd操作:IPC_STAT、IPC_SET 、IPC_RMID。
IPC_STAT:该命令用来获取消息队列信息,返回的信息存贮在buf指向的msqid结构中;
IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf指向的msqid结构中;可设置属性包括:msg_perm.uid、msg_perm.gid、
msg_perm.mode以及msg_qbytes,同时,也影响msg_ctime成员。
IPC_RMID:删除msqid标识的消息队列;
read.c
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h>
#include <stdio.h>
typedef struct msg_st
{
long int msg_type;
char text[BUFSIZ];
} MSG_ST ;
int main()
{
int running = 1;
int msgid = -1;
MSG_ST data;
long int msgtype = 2;
msgid = msgget((key_t)1234,0666 | IPC_CREAT);
if(msgid == -1)
{
puts("msgget fail \n");
exit(EXIT_FAILURE);
}
while(running)
{
if(msgrcv(msgid,(void *)&data,BUFSIZ,msgtype,0) == -1)
{
puts("msgrcv fail \n");
exit(EXIT_FAILURE);
}
printf("rcv data type %d\n",data.msg_type);
printf("rcv data text %s\n",data.text);
if(strncmp(data.text,"end",3)==0)
running = 0;
}
if(msgctl(msgid,IPC_RMID,0)==-1)
{
printf(stderr,"msgctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_FAILURE);
}
write.c
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/msg.h>
#include <errno.h>
#define MAX_TEXT 512
typedef struct msg_st
{
long int msg_type;
char text[MAX_TEXT];
}MSG_ST;
int main()
{
int running = 1 ;
struct msg_st data;
char buffer[BUFSIZ];
int msgid = -1;
printf("bufsize size is %d\n",BUFSIZ);
printf("MAX_TEXT is %d\n",MAX_TEXT);
msgid = msgget((key_t)1234,0666 | IPC_CREAT);
if(msgid == -1)
{
puts("msgget fail !\n");
exit(EXIT_FAILURE);
}
while(running)
{
puts("input data");
fgets(buffer,BUFSIZ,stdin);
data.msg_type = 2;
strcpy(data.text,buffer);
if(msgsnd(msgid,(void *)&data,MAX_TEXT,0) == -1)
{
puts("msgsnd fail !\n");
exit(EXIT_FAILURE);
}
if(strncmp(buffer,"end",3)==0)
running = 0;
sleep(1);
}
puts("exit success\n");
exit(EXIT_FAILURE);
}