概念
消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。
特点
1.消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。(消息队列是结构体)
2.消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
3.消息队列可以实现消息的随机查询,消息不一定要以先进先出的次席读取,也可以按消息的类型读取。
两者的队列ID需相同才能成功实现存放数据和取数据,如图都指向队列1的最后一个。
消息队列与管道的不同点:写入读取后内容还存在于Linux内核中,不会跟管道一样读取完就消失。
创建
从消息队列特点可知,两个进程分别需要同队列ID相同的队列进行写入数据并读取数据,此时要想成功创建一个消息队列,需关心两个问题:
问题一:进程B如何添加消息到队列
问题二:进程A如何读取队列的消息
头文件
#include <sys/msg.h>
常用API
msgget()
创建或打开消息队列:成功返回队列ID,失败返回-1
int msgget(key_t key, int flag);
key | 是一个索引值,为非负数,将通过索引值在Linux内核找到队列 |
flag | 打开队列的方式 |
在以下两种情况下,msgget将创建一个新的消息队列:
1、如果没有与键值key相对应的消息队列,并且flag中包含了IPC_CREAT标志位。
msgget(key,IPC_CREAT);
2、key参数为IPC_PRIVATE。
msgget(key,IPC_PRIVATE);
msgsnd()
添加消息:成功返回0,失败返回-1
int msgsnd(int msqid, const void *ptr, size_t size, int flag);
msqid:消息队列的ID
ptr:写入的数据,指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下:
struct msgbuf
{
long mtype; //消息类型,必须大于0
char mtext[1];//消息文本
};
size:数据的长度
flag:0,表示忽略,表示进程将被阻塞直到函数可以从队列中得到符合条件的消息为止;(还有许多,此处省略)
msgrcv()
读取消息:成功返回消息数据的长度,失败返回-1
int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
msqid:消息队列的ID
ptr:写入的数据,指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下:
struct msgbuf
{
long mtype; //消息类型,必须大于0
char mtext[1];//消息文本
};
type:消息类型
type = 0 | 返回队列中的第一个消息 |
type >0 | 返回队列中消息类型为 type 的第一个消息 |
type< 0 | 返回队列中消息类型值小于或等于 type 绝对值的消息,如果有多个,则取类型值最小的消息 |
可以看出,type值非 0 时用于以非先进先出次序读消息。也可以把 type 看做优先级的权值。
size:数据的长度
flag:0,表示忽略,表示进程将被阻塞直到函数可以从队列中得到符合条件的消息为止;(还有许多,此处省略)
代码展示
get.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct msgbuf
{
long mtype;
char mtext[128];
};
int main()
{
int msgId;//创建消息队列ID
struct msgbuf readBuf;//定义一个读取数据的结构体
msgId = msgget(1234,IPC_CREAT|0777);//在内核中打开或建立键值为1234的,权限为0777的消息队列
if(msgId == -1)//如果创建失败则执行下面代码
{
printf("create queue failed\n");
}
msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),888,0);//从队列中获取888类型的数据并存放到结构体的mtext中,如果队列中未出现888类型的数据,则程序阻塞在这里,这里的888需要与写入队列类型数据一致
printf("read from queue:%s\n",readBuf.mtext);
struct msgbuf sendBuf = {999,"thank you for reach\n"};//读取完毕后将字符串内容写入到999类型的数据中,这里的999类型需要与读取的类型数据一致
msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);//将上一行的结构体数据写入1234消息队列中
return 0;
}
send.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
struct msgbuf
{
long mtype;
char mtext[128];
};
int main()
{
int msgId;
struct msgbuf sendBuf = {888,"this is message from queue\n"};//将字符串内容写入到888类型的数据中,这里的888类型需要与读取的类型数据一致
struct msgbuf readBuf;
msgId = msgget(1234,IPC_CREAT|0777);
if(msgId == -1)
{
printf("create queue failed\n");
}
msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);//将结构体内容写入到1234消息队列中
msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),999,0);//写入之后从队列中获取999类型的数据并存放到结构体的mtext中,如果队列中未出现999类型的数据,则程序阻塞在这里,这里的999需要与写入队列类型数据一致
printf("return form queue:%s\n",readBuf.mtext);
return 0;
}
运行get.c,创建并打开键值为1234的消息队列,但此时表现为堵塞状态,因为队列里没有888类型的数据
运行send.c,创建并打开键值为1234的消息队列,往队列里写入888类型的数据,此时接收端会接受到写入端写入消息队列的数据并将其读取,同时让接收端往队列里写入999类型的数据,让写入段接受999类型的数据并读取