1、特点
消息的链表,存在内核,有特定的ID。
不随进程的消失而消失(独立性)
有特定的格式和优先级
随机性
无论发送进程还是接收进程,都需要在进程空间中用消息缓冲区来暂存消息。该消息缓冲区的结构定义如下
struct msgbuf
{
long mtype; /* 消息的类型 */可通过mtype区分数据类型,同过判断mtype,是否为需要接收的数据
char mtext[1]; /* 消息正文 */mtext[]为存放消息正文的数组,可以根据消息的大小定义该数组的长度
};
2创建
通过msgget创建消息队列
函数原型如下
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
其中 key: 某个消息队列的名字
- msgflg:由九个权限标志构成,用法和创建文件时使用的mode模式标志是一样的,这里举两个来说明
- IPC_CREAT
- 如果消息队列对象不存在,则创建之,否则则进行打开操作
IPC_EXCL
如果消息对象不存在则创建之,否则产生一个错误并返回
返回值:
- 成功msgget将返回一个非负整数,即该消息队列的标识码;
- 失败则返回“-1”
那么如何获取key值?
- 通过宏定义key值
- 通过ftok函数生成key值,这里就不具体介绍ftok函数用法
向消息队列中添加数据,使用到的是msgsnd()函数
函数原型如下
int msgsnd(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);
参数:
- msgid: 由msgget函数返回的消息队列标识码
- msg_ptr:是一个指针,指针指向准备发送的消息,
- msg_sz:是msg_ptr指向的消息长度,消息缓冲区结构体中mtext的大小,不包括数据的类型
- msgflg:控制着当前消息队列满或到达系统上限时将要发生的事情
如:
msgflg = IPC_NOWAIT 表示队列满不等待,返回EAGAIN错误
返回值:
- 成功返回0
- 失败则返回-1
从消息队列中读取消息
从消息队列中读取消息,我们使用msgrcv()函数,
函数原型如下
int msgrcv(int msgid, void *msg_ptr, size_t msgsz,
long int msgtype, int msgflg);
参数:
- msgid: 由msgget函数返回的消息队列标识码
- msg_ptr:是一个指针,指针指向准备接收的消息,
- msgsz:是msg_ptr指向的消息长度,消息缓冲区结构体中mtext的大小,不包括数据的类型
- msgtype:它可以实现接收优先级的简单形式
msgtype=0返回队列第一条信息
msgtype>0返回队列第一条类型等于msgtype的消息
msgtype<0返回队列第一条类型小于等于msgtype绝对值的消息 - msgflg:控制着队列中没有相应类型的消息可供接收时将要发生的事
msgflg=IPC_NOWAIT,队列没有可读消息不等待,返回ENOMSG错误。
msgflg=MSG_NOERROR,消息大小超过msgsz时被截断
注意
msgtype>0且msgflg=MSC_EXCEPT,接收类型不等于msgtype的第一条消息
返回值:
- 成功返回实际放到接收缓冲区里去的字符个数
- 失败,则返回-1
消息队列的控制函数
函数原型
int msgctl(int msqid, int command, strcut msqid_ds *buf);
参数:
- msqid: 由msgget函数返回的消息队列标识码
- command:是将要采取的动作,(有三个可取值)分别如下
注意:若选择删除队列,第三个参数传NULL
返回值:
如果操作成功,返回“0”;如果失败,则返回“-1”
查看消息队列
- 查看消息队列
ipcs -q 命令查看已经创建的消息队列,包括他的key值信息,id信息,拥有者信息,文件权限信息,已使用的字节数,和消息条数。 - ipcrm -Q加消息队列的key值,或来删除一个消息队列。
3例子
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#define MSGKEY 123
//消息的数据结构是以一个长整型成员变量开始的结构体
struct msgstru
{
long msgtype;
char msgtext[2048];
};
int main()
{
struct msgstru msgs;
char str[256];
int msg_type;
int ret_value;
int msqid;
int pid;
//检查消息队列是否存在
msqid = msgget(MSGKEY, IPC_EXCL);//(键名,权限)
if (msqid < 0)
{
//创建消息队列
msqid = msgget(MSGKEY, IPC_CREAT | 0666);
if (msqid <0)
{
printf("failed to create msq | errno=%d [%s]\n", errno, strerror(errno));
exit(-1);
}
}
pid = fork();//创建子进程
if (pid > 0)
{
//父进程
while (1)
{
printf("input message type:\n");//输入消息类型
scanf("%d", &msg_type);
if (msg_type == 0)
break;
printf("input message to be sent:\n");//输入消息信息
scanf("%s", str);
msgs.msgtype = msg_type;
strcpy(msgs.msgtext, str);
//发送消息队列(sizeof消息的长度,而不是整个结构体的长度)
ret_value = msgsnd(msqid, &msgs, sizeof(msgs.msgtext), IPC_NOWAIT);
if (ret_value < 0)
{
printf("msgsnd() write msg failed,errno=%d[%s]\n", errno, strerror(errno));
exit(-1);
}
}
}
else if (pid == 0)
{
//子进程
while (1)
{
msg_type = 1;//接收的消息类型为1
msgs.msgtype = msg_type;
//发送消息队列(sizeof消息的长度,而不是整个结构体的长度)
ret_value = msgrcv(msqid, &msgs, sizeof(msgs.msgtext), msgs.msgtype, IPC_NOWAIT);
if (ret_value > 0)
{
printf("read msg:%s\n", msgs.msgtext);
}
}
}
else
{
printf("fork error\n");
//删除消息队列
msgctl(msqid, IPC_RMID, 0);
exit(1);
}
return 0;
}