首页 > 系统服务 > 详细

Linux进程间通信-MessageQueue消息队列

时间:2021-08-16 22:33:56      阅读:33      评论:0      收藏:0      [点我收藏+]

简介

消息队列,信号量和共享内存都是System V进程间通信(IPC)机制. 消息队列就是一个消息的链表,存放在内核中并由消息队列标识符标识.在System V IPC机制中通信各方通过key_tIPC关键字获取到一个消息队列, 当key_t相同时可以获取到同一个消息队列的标识符

通过msgsnd()将新的消息放在队列的末尾,再通过msgrecv()从队列首获取与指定消息类型的相同的消息。

技术分享图片

图一. 位于内核空间的消息队列示意图[1]. (借用别人的图.不知道这样表示是否正确,但是非常有利于理解)

相关函数

msgget

/*
 * 获取一个与参数key关联的System V消息队列的标识符id
 * key:IPC关键字,通过ftok()获取到值
 */
int msgget(key_t key, int msgflg);

msgctl

/*
 *查看、设置、删除消息队列
 *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可以多种值, 这里值介绍下面三种:

  1. IPC_STAT: 复制描述消息队列的信息到参数buf所指向的结构体中
  2. IPC_SET: 通过参数buf修改内核中与消息队列关联的描述信息
  3. IPC_RMID: 立即删除消息队列, 并唤醒通过消息队列获取和发送消息而阻塞的进程,

msgsnd

/*
 *向消息队列末端添加一条消息
 *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的大小.

msgrecv

/*
 *从队列中获取消息
 *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;
}

  1. 发送10个消息到队列中, 通过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    

这时的消息队列如下图
技术分享图片

  1. 逐个取除type为1的消息. 从取除接口可以看出取除消息并连续, 第五次取type为1的消息时队列中已经没有消息
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>

Linux进程间通信-MessageQueue消息队列

原文:https://www.cnblogs.com/zhao66/p/15150175.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!