概述
消息队列是消息的链接表 ,
存放在内核中并由消息队列标识符标识。我们将称消息队列为 “
队列
”
,其标识符为
“
队列
I D”
。
m s g g e t
用于创建一个新队列或打开一个现存的队列。 msgsnd
用于将新消息添加到队列尾端。每个消息包含一个正长整型类型字段,一个非负长度以及实际数据字节(对应于长度),所有这些都在将消息添加到队列 时,传送给 msgsnd
。
msgrcv
用于从队列中取消息。我们并不一定要以先进先出次序取消息,也可以按消息的类型字段取消息.
linux 消息队列内部实现为一个带头节点的链表
(
内核中的链表
),
提供了很多函数可以去操作这个链表,
链表结构如下
struct msg //消息结点
{
struct msg *next; //下一个消息
long type;//消息的类型
long length;//该消息的长度
char data[];//消息的内容
}
struct msqid_ds // "头结点" ---->表示一个消息队列
{
struct ipc_perm msg_perm; //该数据结构的读写权限
struct msg *msg_first; //指向消息队列上的第一个消息
struct msg *msg_last; //指向消息队列上的最后一个消息
__kernel_time_t msg_stime; //最后发送消息到消息队列的时间
__kernel_time_t msg_rtime; //最后从消息队列读取消息的时间
__kernel_time_t msg_ctime; //最后效果结构体的时候
unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */
unsigned long msg_lqbytes; /* ditto */
unsigned short msg_cbytes; //当前消息队列上面总字节数
unsigned short msg_qnum; //消息个数
unsigned short msg_qbytes; //消息队列的最大的总字节数
__kernel_ipc_pid_t msg_lspid; //最后发送消息的进程的 pid
__kernel_ipc_pid_t msg_lrpid; //最后接收消息的进程的 pid
}
msqid_ds 消息队列数据结构:描述整个消息队列的属性,主要包括整个消息队列
的权限,拥有者、两个重要的指针分别指向消息队列的第一个消息和最后一个消息。
消息队列使用步骤
1.使用 ftok 先向内核申请一个 IPC key(钥匙,"许可证")
2.使用 msgget 打开或者创建一个 IPC 的“对象(msg)”
3.使用 msgsnd 和 msgrcv 读写信息
打开或创建一个消息队列的 IPC 对象
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数原型:
int msgget(key_t key, int msgflg);
作用:
创建或打开一个用于消息队列的 IPC
对象
参数含义:
key: system V IPC 的 key,一般由 ftok 生成
msgflg: 标志位,分两种情况,创建的话为 IPC_CREAT|权限位,打开的话为 0
返回值:
成功返回一个 system V 消息队列的 id
失败返回-1,同时 errno 被设置
示例:
int mid = msgget(key,IPC_CREAT | 0664);
if(-1 == mid)
{
perror("msgget error");
exit(-1);
}
写入信息到消息队列上(msgsnd)
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数原型:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
作用:
发送 msgp
消息到
msqid
指定的消息队列上
参数含义:
msqid: 指定的消息队列的 id,表示你要把消息发送到哪一个消息队列,一般由msgget 函数生成
msgp: 指向要发送的消息的结构体指针,该结构体具有定义如下(一般需要自己定义)
struct msgbuf //定义一个发送消息的结构体
{
long mtype; //消息的类型,具体的含义是由程序设计者指定 >0
char mtext[0]; //消息的内容,可长可短//“柔性数组”“0 长数组”
};
msgsz:消息内容的长度,上面结构体中数组 mtext 的实际大小
msgflg:阻塞标志位,为 0 表示阻塞模式,为 IPC_NOWAIT 表示非阻塞模式
返回值:
成功返回 0,
失败返回-1,同时 errno 被设置
示例:
struct msgbuf {
long mtype; //消息的类型,具体的含义是由程序设计者指定 >0
char mtext[0]; //消息的内容,可长可短//“柔性数组”“0 长数组”
};
//把信息发送到消息队列
char buf[256] = {0}; //用于保存发送的信息
printf("请输入发送的消息\n");
fgets(buf,256,stdin); //从标准输入中输入字符串到 buf 中
//分配消息结构体的空间(结构体本身和消息大小)
struct msgbuf *p = (struct msgbuf *)malloc(sizeof(struct msgbuf)+strlen(buf)+1);
if(p == NULL)
{
perror("malloc error");
exit(-1);
}
printf("请输入发送消息的类型\n");
scanf("%ld",&(p->mtype));
//p->mtype = 200;
memcpy(p->mtext,buf,strlen(buf)+1); //把 buf 空间上
strlen(buf)+1 大小的内容 copy 到 p->mtext 上去
int r = msgsnd(mid,(void *)p, strlen(buf)+1,0); //发送结构体上的消息到 mid 的消息队列上,以阻塞模式
if(r == -1)
{
perror("msgsnd error");
exit(-1);
}
读取信息到消息队列上(msgrcv)
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数原型:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
作用:
读取 msqid
指定消息队列的信息保存到
msgp
内存上
参数含义:
msqid: 指定的消息队列的 id,表示你要把消息发送到哪一个消息队列,一般由msgget 函数生成
msgp: 指向消息结构体 msgbuf,用来保存读取到的消息
struct msgbuf //定义一个发送消息的结构体
{
long mtype; //消息的类型,具体的含义是由程序设计者指定 >0
char mtext[0]; //消息的内容,可长可短//“柔性数组”“0 长数组”
};
msgsz: 用来指定 msgp 指向的消息结构体,最多能够保存多少个字节的消息
msgtyp:指定要读取消息的类型,为 0 的话表示接收全部类型
msgflg:阻塞标志位,为 0 表示阻塞模式,为 IPC_NOWAIT 表示非阻塞模式
返回值:
成功返回 0,
失败返回-1,同时 errno 被设置
示例:
struct msgbuf {
long mtype; //消息的类型,具体的含义是由程序设计者指定 >0
char mtext[0]; //消息的内容,可长可短//“柔性数组”“0 长数组”
};
struct msgbuf *p = (struct msgbuf *)malloc(sizeof(struct msgbuf)+100);
if(p == NULL)
{
perror("malloc error");
exit(-1);
}
memset(p,0,sizeof(struct msgbuf)+100); //把 p 所指内存全部赋值为 0
int r = msgrcv(mid, (void *)p, 100,100,0); //以阻塞模式读取 100 字节 mid 消息队列上类型为 100 的消息到 p 所指内存上阻塞
if(r == -1)
{
perror("msgrcv error");
exit(-1);
}
消息队列的控制操作(msgctl)
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数原型:
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
作用:
对 msqid
指定的消息队列根据
cmd
命令号执行指定的操作
参数含义:
msqid:消息队列的 id
cmd:命令号,常用命令如下:
IPC_RMID 删除指定的消息队列
IPC_SET 设置消息队列的属性
IPC_STAT 获取消息队列的属性
buf:msqid_ds 结构体(7.1 节有定义),用来保存要设置或获取的消息队列属性,当 cmd 为 IPC_RMID 时则忽略改参数
返回值:
成功返回 0,
失败返回-1,同时 errno 被设置
消息队列使用实例
进程 1-发送消息
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>
#include<stdlib.h>
#define PATHNAME "/home/china" //一个存在的路径名,ftok 获取这个文件的 inode 编号
#define PROJ_ID 20207 //一个整数值(非 0)用于申请一个“钥匙”
//自定义的消息结构体
//sizeof(struct msgbuf) == sizeof(long) == 8
struct msgbuf {
long mtype; //消息的类型,具体的含义是由程序设计者指定 >0
char mtext[0]; //消息的内容,可长可短//“柔性数组”“0 长数组”
};
int main()
{
//产生一个 System V IPC key
key_t key = ftok(PATHNAME,PROJ_ID);
if(-1 == key)
{
perror("ftok error");
exit(-1);
}
//利用 key 创建或者打开一个 IPC 对象
int mid = msgget(key,IPC_CREAT | 0664);
if(-1 == mid)
{
perror("msgget error");
exit(-1);
}
//把信息发送到消息队列
char buf[256] = {0}; //用于保存发送的信息
printf("请输入发送的消息\n");
fgets(buf,256,stdin); //从标准输入中输入字符串到 buf 中
//分配消息结构体的空间(结构体本身和消息大小)
struct msgbuf *p = (struct msgbuf *)malloc(sizeof(struct msgbuf)+strlen(buf)+1);
if(p == NULL)
{
perror("malloc error");
exit(-1);
}
printf("请输入发送消息的类型\n");
scanf("%ld",&(p->mtype));
//p->mtype = 200;
memcpy(p->mtext,buf,strlen(buf)+1); //把 buf 空间上
strlen(buf)+1 大小的内容 copy 到 p->mtext 上去
int r = msgsnd(mid,(void *)p, strlen(buf)+1,0); //发送结构体上的消息到 mid 的消息队列上,以阻塞模式
if(r == -1)
{
perror("msgsnd error");
exit(-1);
}
free(p);
return 0;
}
进程 2-接收进程
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>
#include<stdlib.h>
#define PATHNAME "/home/china"
#define PROJ_ID 20207
//自定义的消息结构体
//sizeof(struct msgbuf) == sizeof(long) == 8
struct msgbuf {
long mtype; //消息的类型,具体的含义是由程序设计者指定 >0
char mtext[0]; //消息的内容,可长可短//“柔性数组”“0 长数组”
};
int main()
{
//产生一个 System V IPC key
key_t key = ftok(PATHNAME,PROJ_ID);
if(-1 == key)
{
perror("ftok error");
exit(-1);
}
//利用 key 创建或者打开一个 IPC 对象
int mid = msgget(key,IPC_CREAT | 0664);
if(-1 == mid)
{
perror("msgget error");
exit(-1);
}
//把从消息队列中读取消息
//分配消息结构体的空间(结构体本身和消息大小)
struct msgbuf *p = (struct msgbuf *)malloc(sizeof(struct msgbuf)+100);
if(p == NULL)
{
perror("malloc error");
exit(-1);
}
memset(p,0,sizeof(struct msgbuf)+100); //把 p 所指内存全部赋值为 0
int r = msgrcv(mid, (void *)p, 100,100,0); //以阻塞模式读取 100字节 mid 消息队列上类型为 100 的消息到 p 所指内存上阻塞
if(r == -1)
{
perror("msgrcv error");
exit(-1);
}else
{
printf("消息类型:%ld\n",p->mtype);
printf("接收的消息为:%s\n",p->mtext);
}
free(p);
//移除系统中的消息队列
msgctl(mid,IPC_RMID,NULL);
return 0;
}