Linux系统中的POSIX消息队列

一、概述

消息队列是Linux IPC中很常见的一种通信方式,它通常用来在不同进程(线程)间发送特定格式的消息数据。
消息队列和管道、FIFO有很大的区别,主要有以下两点:

  • 一个进程向消息队列写入消息之前,并不需要某个进程在该队列上等待消息的到达,而管道和FIFO是相反的,进程向其中写消息时,管道和FIFO必须已经打开来读。
  • IPC的持续性不同。当管道和FIFO最后一次关闭发生时,仍在管道和FIFO中的数据会被丢弃。消息队列是随内核的持续性,即一个进程向消息队列写入消息后,然后终止,另外一个进程可以在以后的某个时刻打开该队列读取消息。只要内核没有重启,消息队列就不会被删除。

消息队列可以认为是一个链表。进程(线程)可以往里写消息,也可以从里面取出消息。一个进程可以往某个消息队列里写消息,然后终止,另一个进程随时可以从消息队列里取走这些消息。这里也说明了,消息队列具有随内核的持续性,也就是系统不重启,消息队列永久存在。

注意:
1、消息队列的名字只能以一个’/‘开头,名字中不能包含其他的’/';
2、mq_receive()的第三个参数表示读取消息的长度,不能小于能写入队列中消息长度的最大值,即一定要大于等于该队列的mq_attr结构中mq_msgsize的大小。
3、消息的优先级:它是一个小于MQ_PRIO_MAX的数,数值越大,优先级越高。POSIX消息队列在调用mq_receive()时总是返回队列中最高优先级的最早消息。如果消息不需要设定优先级,那么可以在mq_send()时置msg_prio为0,mq_receive()msg_prio置为NULL。
4、默认情况下mq_send()mq_receive()是阻塞进行调用,可以通过mq_setattr来设置为O_NONBLOCK,如:

struct mq_attr new_attr;
mq_getattr(mqID, &new_attr);  //获取当前属性
new_attr.mq_flags = O_NONBLOCK;   //设置为非阻塞
mq_setattr(mqID, &new_attr, NULL);   //设置属性

二、POSIX消息队列的创建和关闭

POSIX消息队列的创建,关闭和删除用到以下三个函数接口:

#include <mqueue.h>

mqd_t mq_open(const char *name, int oflag, /*  mode_t mode, struct mq_attr *attr */);   //成功返回消息队列描述符,失败返回-1
mqd_t mq_close(mqd_t mqdes);
mqd_t mq_unlink(const char *name);
//成功返回0,失败返回-1

mq_open()用于打开或创建一个消息队列。
(1)name:表示消息队列的名字,它符合POSIX IPC的名字规则。
(2)oflag:表示打开的方式,和open函数类似。有必须的选项:O_RDONLYO_WRONLYO_RDWR,还有可选的选项:O_NONBLOCKO_CREATO_EXCL
(3)mode:是一个可选参数,在oflag中含有O_CREAT标志且消息队列不存在时,才需要提供该参数,表示默认访问权限,可以参考open。
(4)attr:也是一个可选参数,在oflag中含有O_CREAT标志且消息队列不存在时才需要。该参数用于给新队列设定某些属性,如果是空指针,那么就采用默认属性。

mq_open()返回值是mqd_t类型的值,被称为消息队列的描述符。在Linux2.6.18中该类型的定义为整型:

#include <bits/mqueue.h>
typedef int mqd_t;

mq_close()用于关闭一个消息队列,和文件的close类似,关闭后,消息队列并不会从系统中删除。如果一个进程结束,会自动调用关闭打开着的消息队列。

mq_unlink()用于删除一个消息队列。消息队列创建后只有通过调用该函数或者是内核自举才能进行删除。每个消息队列都有一个保存当前打开着该描述符数的引用计数器,和文件一样,因此本函数能够实现类似于unlink函数删除一个文件的机制。
创建示例:

#define TEST_MQ_NAME ("/test_mq")

static struct mq_attr test_mq_attr;
static mqd_t test_mq;
test_mq_attr.mq_maxmsg = LOCK_ALARM_MAX_NUM;
test_mq_attr.mq_msgsize = LOCK_ALARM_MAX_SIZE;
mq_unlink(TEST_MQ_NAME);
test_mq = mq_open(TEST_MQ_NAME, O_RDWR|O_CREAT|O_EXCL|O_NONBLOCK, 0644, &lock_mq_attr);

三、POSIX消息队列的属性

POSIX标准规定消息队列属性mq_attr必须要含有以下四个内容:

long mq_flags     //消息队列的标志,0或O_NONBLOCK,用来表示是否阻塞
long mq_maxmsg    //消息队列的最大消息数
long mq_msgsize   //消息队列中每个消息的最大字节数
long mq_curmsgs   //消息队列中当前的消息数目

在Linux2.6.18中mq_attr结构的定义如下:

#include <bits/mqueue.h>

struct mq_attr
{
	long int mq_flags;
	long int mq_maxmsg;
	long int mq_msgsize;
	long int mq_curmsgs;
	long int __pad[4];
};

POSIX消息队列的属性设置和获取可以通过下面两个函数实现:

#include <mqueue.h>

mqd_t mq_getattr(mqd_t mqdes, struct mq_attr *attr);
mqd_t mq_setattr(mqd_t mqdes, struct mq_attr *newattr, struct mq_attr *oldattr);
//成功返回0,失败返回-1

mq_getattr()用于获取当前消息队列的属性,mq_setattr()用于设置当前消息队列的属性。其中mq_setattr()中的oldattr用于保存修改前的消息队列的属性,可以为空。
mq_setattr()可以设置的属性只有mq_flags,用来设置或清除消息队列的非阻塞标志。newattr结构的其他属性被忽略。mq_maxmsgmq_msgsize属性只能在创建消息队列时通过mq_open()来设置。mq_open()只会设置这两个属性,忽略另外两个属性。mq_curmsgs属性只能被获取而不能被设置。
测试代码如下:

#include <iostream>
#include <cstring>
 
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <mqueue.h>
 
int main()
{
    mqd_t mqID;
    mqID = mq_open("/testmQueue", O_RDWR | O_CREAT, 0666, NULL);
 
    if (mqID < 0)
    {
        printf("open message queue error %s\n", strerror(errno));
        return -1;
    }
 
    mq_attr mqAttr;
    if (mq_getattr(mqID, &mqAttr) < 0)
    {
        printf("get the message queue attribute error...\n");
        return -1;
    }
 
    printf("mq_flags:%d\n", mqAttr.mq_flags);
    printf("mq_maxmsg:%d\n", mqAttr.mq_maxmsg);
    printf("mq_msgsize:%d\n", mqAttr.mq_msgsize);
    printf("mq_curmsgs:%d\n", mqAttr.mq_curmsgs);
}

在Linux2.6.18中执行结果如下:

mq_flags:0
mq_maxmsg:10
mq_msgsize:8192
mq_curmsgs:0

三、POSIX消息队列的使用

POSIX消息队列可以通过以下两个函数来进行发送和接收消息:

#include <mqueue.h>
mqd_t mq_send(mqd_t mqdes, const char *msg_ptr,
                      size_t msg_len, unsigned msg_prio);
                     //成功返回0,出错返回-1
 
mqd_t mq_receive(mqd_t mqdes, char *msg_ptr,
                      size_t msg_len, unsigned *msg_prio);
                     //成功返回接收到消息的字节数,出错返回-1

msg_send()向消息队列中写入一条消息,mq_receive()从消息队列中读取一条消息。
(1)mqdes:消息队列描述符;
(2)msg_prt:指向消息体缓冲区的指针;
(3)msg_len:消息体的长度,其中mq_receive()的该参数不能小于能写入队列中消息的最大大小,即一定要大于等于该队列的mq_attr结构中mq_msgsize的大小。如果mq_receive()中的msg_len小于该值,就会返回EMSGSIZE错误。POXIS消息队列发送的消息长度可以为0;
(4)msg_prio:消息的优先级;它是一个小于MQ_PRIO_MAX的数,数值越大,优先级越高。POSIX消息队列在调用mq_receive()时总是返回队列中最高优先级的最早消息。如果消息不需要设定优先级,那么可以在mq_send()是置msg_prio为0,mq_receive的msg_prio置为NULL。
测试代码如下:

#include <iostream>
#include <cstring>
#include <errno.h>
 
#include <unistd.h>
#include <fcntl.h>
#include <mqueue.h>

int main()
{
    mqd_t mqID;
    mqID = mq_open("/testmQueue", O_RDWR | O_CREAT | O_EXCL, 0666, NULL);
 
    if (mqID < 0)
    {
        if (errno == EEXIST)
        {
            mq_unlink("/testmQueue");
            mqID = mq_open("/testmQueue", O_RDWR | O_CREAT, 0666, NULL);
        }
        else
        {
            printf("open message queue error %s\n", strerror(errno));
            return -1;
        }
    }
 
    if (fork() == 0)
    {
        mq_attr mqAttr;
        mq_getattr(mqID, &mqAttr);
 
        char *buf[mqAttr.mq_msgsize];
 
        for (int i = 1; i <= 5; ++i)
        {
            if (mq_receive(mqID, buf, mqAttr.mq_msgsize, NULL) < 0)
            {
                printf("receive message  failed.\n");
                printf("error info:%s\n", strerror(errno));
                continue;
            }
 
            printf("receive message %d : %s\n", i, buf);   
        }
        return 0;
    }
 
    char msg[] = "yuki";
    for (int i = 1; i <= 5; ++i)
    {
        if (mq_send(mqID, msg, sizeof(msg), i) < 0)
        {
            printf("send message %d failed.\n", i);
            printf("error info:%s\n", strerror(errno));
        }
        printf("send message %d success.\n", i);
        
        sleep(1);
    }
} 

在Linux2.6.18下执行结果如下:

send message 1 success. 
receive message 1: yuki
send message 2 success. 
receive message 2: yuki
send message 3 success. 
receive message 3: yuki
send message 4 success. 
receive message 4: yuki
send message 5 success. 
receive message 5: yuki
  • 16
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值