消息队列,信号量和共享内存都是System V进程间通信(IPC)机制. 消息队列就是一个消息的链表,存放在内核中并由消息队列标识符标识.在System V IPC机制中通信各方通过key_t
IPC关键字获取到一个消息队列, 当key_t
相同时可以获取到同一个消息队列的标识符
通过msgsnd()
将新的消息放在队列的末尾,再通过msgrecv()
从队列首获取与指定消息类型的相同的消息。
图一. 位于内核空间的消息队列示意图[1]. (借用别人的图.不知道这样表示是否正确,但是非常有利于理解)
/*
* 获取一个与参数key关联的System V消息队列的标识符id
* key:IPC关键字,通过ftok()获取到值
*/
int msgget(key_t key, int msgflg);
/*
*查看、设置、删除消息队列
*msqid:消息队列的标识符id
*cmd:对消息队列执行的操作,IPC_STAT,IPC_SET,IPC_RMID
*buf:描述指定消息队列的结构体,
*/
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
/*
*描述消息队列的结构体
*/
struct msqid_ds {
struct ipc_perm msg_perm; /* Ownership and permissions */
time_t msg_stime; /* 最后发送消息时间 */
time_t msg_rtime; /* 最后接收消息时间 */
time_t msg_ctime; /* 最后修改时间 */
unsigned long __msg_cbytes; /* 当前使用消息队列空间大小 (nonstandard) */
msgqnum_t msg_qnum; /* 当前消息个数 */
msglen_t msg_qbytes; /* 消息队列最大长度 */
pid_t msg_lspid; /* 最后发送消息的PID */
pid_t msg_lrpid; /* 最后接收消息的PID */
};
cmd
可以多种值, 这里值介绍下面三种:
IPC_STAT
: 复制描述消息队列的信息到参数buf
所指向的结构体中IPC_SET
: 通过参数buf
修改内核中与消息队列关联的描述信息IPC_RMID
: 立即删除消息队列, 并唤醒通过消息队列获取和发送消息而阻塞的进程,/*
*向消息队列末端添加一条消息
*msqid:消息队列的id
*msgp:消息队列中发送的消息, 一个结构体指针
*msgsz:消息结构体中消息主体(内容)的大小
*msgflg:值为0时如果消息队列没有足够空间存入消息将会阻塞. 值为IPC_NOWAIT时队列没有足够空间存入消息时立即返回,并将错误码设为EAGAIN
*返回值:失败返回-1, 成功返回0
*/
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数msgp
是用户自定义的结构体指针. 结构格式必须如下
struct msgbuf {
long mtype; /* 消息类型,必须>0 */
char mtext[1]; /* 消息内容,长度自己定义, 也可以其他的数据结构,如int数组,结构体*/
}
需要注意 1. mtype
必须大于0, 从消息队列中取消息时取对应类型的消息; 2. msgsnd()
的参数msgsz
是用户自定义结构体中消息内容的大小, 对应上面的结构体中mtext
的大小.
/*
*从队列中获取消息
*msqid:消息队列的id
*msgp:消息队列中发送的消息, 一个结构体指针
*msgsz:消息结构体中消息主体的大小
*msgtyp:要获取的消息类型
*msgflg:见下方
*返回值:失败返回-1, 成功返回0
*/
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz,long msgtyp, int msgflg);
根据参数type
的不同获取消息时可能从队列头开始,也可能从队列的中间位置取. 参数type
使我们可以指定想要哪一种消息:
type == 0
返回队列中的第一个消息。type > 0
返回队列中消息类型为type
的第一个消息。type < 0
返回队列中消息类型值小于或等于type
绝对值,而且在这种消息中,其类型值参数msgflg
的取值:
IPC_NOWAIT
: 如果队列中没有请求类型的消息,则立即返回。系统调用失败,errno 设置为 ENOMSG
。MSG_EXCEPT
: 与大于 0 的 msgtyp 一起使用以读取消息类型与 msgtyp 不同的队列中的第一条消息。MSG_NOERROR
:如果长于msgsz
字节则该消息被截短(在这种情况下,不通知我们消息截短了)。E2BIG
(消息仍留在队列中)。head.h
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys types.h="">
#include<sys msg.h="">
#include<sys ipc.h="">
#include<stdlib.h>
#include<errno.h>
struct User{
char name[20];
int age;
};
struct Msg{
long type;
struct User user; //man手册中是字符数组,这里使用结构体
};
send.c负责发送消息. 根据输入的参数循环创建type分别为1,2,3的消息发送到消息队列中.
#include "head.h"
int main(int argc, char ** argv){
if(argc<2){
printf("Usage: ./a.out 10");
exit(1);
}
int cnt = atoi(argv[1]);
key_t key = ftok(".",123);
//获取一个消息队列
int mqid = msgget(key, IPC_CREAT | 0664);
struct msqid_ds mqds;
//获取与消息队列关联的数据结构
if(msgctl(mqid, IPC_STAT, &mqds)!=0){
perror("msgctl");
exit(1);
}
mqds.msg_qbytes = sizeof(struct User)*10;
//设置消息队列能够放入的消息数量和
if(msgctl(mqid,IPC_SET, &mqds)!=0){
perror("msgctl set");
exit(1);
}
for(int i = 0; i < cnt; i++){
char name[3] = {i+‘A‘,i+‘A‘,0};
struct Msg m;
m.type = i%3+1;
strcpy(m.user.name,name);
m.user.age = i+10;
//如果在 msgflg 中指定了 IPC_NOWAIT,则调用将失败并返回错误 EAGAIN。
if(msgsnd(mqid, &m, sizeof(struct User),IPC_NOWAIT)==-1){
if(EAGAIN==errno){
printf("queue full");
}else{
perror("msgsnd");
}
}
}
return 0;
}
recv.c负责获取消息, 每次调用获取一个type为输入参数的消息
int main(int argc, char ** argv){
if(argc<2){
printf("Usage: ./a.out 1\n");
exit(1);
}
//要获取消息的type
int type = atoi(argv[1]);
key_t key = ftok(".",123);
int mqid = msgget(key, IPC_CREAT | 0664);
if(mqid == -1){
perror("msgget");
}
struct Msg msg;
//每调用成功一次, 队列中对应type的消息减一
//IPC_NOWAIT:如果队列中没有请求类型的消息,则立即返回。系统调用失败,errno 设置为 ENOMSG。
if(msgrcv(mqid, &msg, sizeof(msg), type, IPC_NOWAIT)==-1){
if(errno==ENOMSG){
printf("no msg\n");
}else{
perror("msgrcv");
}
}else{
printf("msg name:%s age:%d\n",msg.user.name,msg.user.age);
}
return 0;
}
ipcs
可看都队列中有10个消息. type为1, 2 和 3的消息分别有4个, 3个, 3个zhao@VM-0-8-ubuntu ipc_msg % ./send 10 [0]
zhao@VM-0-8-ubuntu ipc_msg % ipcs -q [0]
------ Message Queues --------
key msqid owner perms used-bytes messages
0x7b010174 65536 zhao 664 240 10
这时的消息队列如下图
zhao@VM-0-8-ubuntu ipc_msg % ./recv 1 [0]
msg type:1 name:AA age:10
zhao@VM-0-8-ubuntu ipc_msg % ./recv 1 [0]
msg type:1 name:DD age:13
zhao@VM-0-8-ubuntu ipc_msg % ./recv 1 [0]
msg type:1 name:GG age:16
zhao@VM-0-8-ubuntu ipc_msg % ./recv 1 [0]
msg type:1 name:JJ age:19
zhao@VM-0-8-ubuntu ipc_msg % ./recv 1 [0]
no msg
zhao@VM-0-8-ubuntu ipc_msg % ipcs -q [0]
------ Message Queues --------
key msqid owner perms used-bytes messages
0x7b010174 65536 zhao 664 144 6
下图右侧为取除所有type为1的消息, 左图为取出消息前的队列
再取两个type为0的消息. 取出了队列前端的两个消息,type分别为2和3, 此时队列只剩4个消息
zhao@VM-0-8-ubuntu ipc_msg % ./recv 0 [0]
msg type:2 name:BB age:11
zhao@VM-0-8-ubuntu ipc_msg % ./recv 0 [0]
msg type:3 name:CC age:12
zhao@VM-0-8-ubuntu ipc_msg % ipcs -q [0]
------ Message Queues --------
key msqid owner perms used-bytes messages
0x7b010174 65536 zhao 664 96 4
这时的消息队列
参考:
[1]https://www.jianshu.com/p/7e3045cf1ab8
</errno.h></stdlib.h></string.h></unistd.h></stdio.h>
原文:https://www.cnblogs.com/zhao66/p/15150175.html