###概念###
消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向其中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。
消息队列是一个存放在内核中的消息链表,每个消息队列由消息队列标识符标识。与管道不同的是消息队列存放在内核中,
只有在内核重启(即操作系统重启)或者显式地删除一个消息队列时,该消息队列才会被真正删除。
###头文件###
###数据结构###
1.消息缓冲结构 (发送、接收消息都要以这个 数据结构模板 进行)
struct
msgstru{
long
mtype;
//大于0 //消息类型
char
mtext[2048]; //消息文本
};
说明:消息文本可以为任意类型,由用户根据需要定义,如字符数组、结构体等;
消息队列中的消息的大小是受限制的,在内核中存在宏定义。
$ ipcs -l
2、msqid_ds -内核数据结构
linux内核中,每个消息队列都维护一个结构体msqid_ds ,此结构体保存着消息队列当前的状态信息。
struct msqid_ds {
struct ipc_perm msg_perm;
struct msg *msg_first; /* first message on queue,unused */
struct msg *msg_last; /* last message in queue,unused */
__kernel_time_t msg_stime; /* last msgsnd time */
__kernel_time_t msg_rtime; /* last msgrcv time */
__kernel_time_t msg_ctime; /* last change time */
unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */
unsigned long msg_lqbytes; /* ditto */
unsigned short msg_cbytes; /* current number of bytes on queue */
unsigned short msg_qnum; /* number of messages in queue */
unsigned short msg_qbytes; /* max number of bytes on queue */
__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */
};
各个字段的含义:
msg_perm:是一个ipc_perm(定义在头文件sys/ipc.h)的结构,保存了消息队列的存取权限,以及队列的用户ID、组ID等信息。
msg_first:指向队列中的第一条消息
msg_last:指向队列中的最后一条消息
msg_stime:向消息队列发送最后一条信息的时间
msg_rtime:从消息队列取最后一条消息的时间
msg_ctime:最后一次变更消息队列的时间
msg_lcbytes:消息队列中所有消息占的字节数
msg_qnum:消息队列中的消息数目
msg_qbytes:消息队列的最大字节数
msg_lspid:向消息队列发送最后一条消息的进程ID
msg_lrpid:从消息队列读取最后一条消息的进程ID
3、ipc_perm -内核数据结构
结构体ipc_perm保存着消息队列的一些重要的信息,比如消息队列关联的键值,消息队列的用户ID,组ID等。
定义在sys/ipc.h中。
struct ipc_perm
{
__kernel_key_t key;
__kernel_uid_t uid;
__kernel_gid_t gid;
__kernel_uid_t cuid;
__kernel_gid_t cgid;
__kernel_mode_t mode;
unsigned short seq;
};
几个主要字段的含义如下:
key:创建消息队列用到的键值key
uid:消息队列的用户ID
gid:消息队列的组ID
cuid:创建消息队列的进程用户ID
cgid:创建消息队列进程组ID
###常用接口函数###
1. 判断消息列队是否存在 存在返回消息列队msqid,否则返回负值
2.创建消息列队 成功返回消息列队msqid,否则返回负值
3.发送消息 发送失败返回负值
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//msqid:消息队列的标识码
//msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,结构由用户根据消息模板自行定义
//msgsz:消息的大小
//msgflg:用来指明核心程序在队列满了的情况下所应采取的行动。如果msgflg为常数IPC_NOWAIT,则在msgsnd()执行时若是消息队列已满,则msgsnd()将不会阻塞,而会立即返回-1,并设定错误码(errno)为ENOMSG;如果msgflg为0,则在msgsnd()执行时若是消息队列已满,采取阻塞等待的处理模式。
4.接收消息
size_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
//msqid:消息队列的标识码
//msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,结构由用户根据消息模板自行定义
//msgsz:消息的大小
//msgtyp: 0--接收所有消息类型的消息 正数--只接收该类型的消息
// msgflg:用来指明核心程序在队列为空的情况下所应采取的行动。如果msgflg为常数IPC_NOWAIT,则在执行msgrcv()时发现列队为空,不做等待马上返回-1,并设定错误码为ENOMSG;当msgflg为0时,采取阻塞等待的处理模式。5.删除消息列队
###测试例程###
/*MQ snd.c*/
#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 201844 //2018/04/04
struct msgstru{
long msgtype;
char msgtext[2048];
};
int main(){
struct msgstru msg;
int mtype;
char str[256];
int ret;
int mqid;
mqid = msgget(MSGKEY,IPC_EXCL); /*检查消息队列是否存在*/
if(mqid < 0){
mqid = msgget(MSGKEY,IPC_CREAT|0666); /*创建消息列队*/
if(mqid < 0){
printf("create mq failed: errno=%d,%s \n",errno,strerror(errno));
exit(-1);
}
}
while(1){
printf("input msgtype:[终止程序输入0]");
scanf("%d",&mtype);
if(mtype == 0){
break;
}
printf("input msgtext:");
scanf("%s",str);
msg.msgtype=mtype;
strcpy(msg.msgtext,str);
//发送消息 //列队满时不阻塞,返回-1
ret=msgsnd(mqid,&msg,sizeof(struct msgstru),IPC_NOWAIT);
if(ret < 0){
printf("msgsnd() write msg failed,errno=%d,%s \n",errno,strerror(errno));
exit(-1);
}
}
msgctl(mqid,IPC_RMID,0); //删除消息列队
return 0;
}
/*MQ mqrcv.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#define MSGKEY 201844
struct msgstru{
long msgtype;
char msgtext[2048];
};
void childproc(){
struct msgstru msg;
int mqid;
int ret;
char str[512];
while(1){
mqid = msgget(MSGKEY,IPC_EXCL);
if(mqid < 0){
printf("msq not existed! errno=%d [%s]\n",errno,strerror(errno));
sleep(3);
continue;
}
//ret=msgrcv(mqid,&msg,sizeof(struct msgstru),0,0); //列队为空 阻塞
//只接收消息类型为1的消息
ret=msgrcv(mqid,&msg,sizeof(struct msgstru),1,0); //列队为空 阻塞
printf("pid=[%d] mtype=[%ld] mtext=[%s] \n",getpid(),msg.msgtype,msg.msgtext);
}
exit(0);
}
int main(){
int i,cpid;
cpid=0; //局部变量初始化
//创建5个子进程 监听消息列队
for(i=0;i<5;i++){
cpid=fork();
if(cpid < 0){ //主进程
printf("fork failed! \n");
}else if(cpid == 0){ //子进程走这
childproc();
}
}
return 0;
}
####参考####
https://www.cnblogs.com/lpshou/archive/2013/06/20/3145651.html
https://www.cnblogs.com/zhangxuan/p/6724068.html