Linux POSIX 消息队列

Linux POSIX 消息队列

进程间通信的各种方式

  1. 管道;
  2. FIFO;
  3. socket;
  4. 消息队列;
  5. 共享内存;
    其中1~4的方式均为发送端发送数据,接收端接收数据的模式。管道、FIFO、消息队列的发送端和接收端必须是处于同一台设备的进程,而socket通信可在同一设备,也可在不同设备间通信。
    共享内存则是进程间共享同一段内存,不同进程对共同区域进行读写,实现数据交互,也要求处于同一台设备中。

POSIX消息队列的使用流程

//创建或打开消息队列 ---> 向队列写入消息 ---> 从队列读取一条消息 ---> 断开进程与队列间的关联关系 ---> 删除队列
        mq_open() --->   mq_send()    ---> mq_receive()     ---> mq_close()               ---> mq_unlink()

打开或创建消息队列

#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>

/*
 *  @Description: 创建一个新的消息队列 或 打开一个既有消息队列
 *  @Para       : const char * name         消息队列的名称
 *                int oflag                 位掩码  为O_CREAT则创建一个队列 非O_CREAT为打开一个既有队列
 *                mode_t mode               (创建队列时)位掩码 可以设置队列的文件权限
 *                struct mq_attr * attr     (创建队列时)设置消息队列的特性
 *  @return     : 成功返回消息队列描述符,失败返回-1
**/
mqd_t mq_open(const char * name,  int oflag /*, mode_t mode, struct mq_attr * attr */);

//例:创建一个消息队列
if(mq_open("/myqueue", O_CREAT | O_EXCL | O_RDWR, 0644, NULL) == -1)
    printf("queue creat failed!\r\n");

注意:

  1. 消息队列的名称参数name,必须以斜线开头,后面跟着一个或多个非斜线字符的名字,如 “/myqueue”, 名称的最长字符数为 NAME_MAX(255) - 4;
  2. 当int oflag = O_CREAT | O_EXCL 时,如果消息队列不存在则创建,如果存在,则函数失败返回。可以设置如下特性:
    • O_CREAT 队列不存在时创建队列
    • O_EXCL 与O_CREAT一起排它地创建队列
    • O_RDONLY 只读打开消息队列
    • O_WRONLY 只写打开消息队列
    • O_RDWR 读写打开消息队列
    • O_NONBLOCK 以非阻塞模式打开,此时如果mq_send(),mq_receive()不能在不阻塞的情况下执行,则会立即返回EAGAIN错误
  3. 如果mq_open()用于打开一个既有消息队列,则仅适用前两个参数即可。如果是创建新消息队列,则可以通过mode_t mode设置新创建的消息队列的文件权限(Owner Group Other, Read Write Execute等),通过 struct mq_attr * attr可以设置消息队列的属性,下节详细讲解,如果设为NULL则为默认属性。
  4. 当打开或者创建消息队列时,会自动建立进程与消息队列间的关联关系,可以通过mq_close()断开该关联关系;

消息队列的特性 struct mq_attr

创建消息队列时,需要设置消息队列的属性,其属性结构如下。

struct mq_attr{
    long mq_flags;      //0 或者 O_NONBLOCK  创建时,创建后均可修改
    long mq_maxmsg;     //创建的消息队列所能添加消息的数量上限,其取值必须大于零,仅创建的时候可以设置。
    long mq_msgsize;    //消息队列中每条消息的大小上限,其取值必须大于零,仅创建的时候可以设置。
    long mq_curmsgs;    //消息队列中,消息的数量,只读
};

注意:

  1. 消息队列的属性参数仅有 mq_flags 可以在创建后设置。
  2. 设置为O_NONBLOCK则该队列为非阻塞队列,如果mq_send(),mq_receive()不能在不阻塞的情况下执行,则会立即返回EAGAIN错误。

获取与设置消息队列属性

#include <mqueue.h>

/*
 *  @Description: 获取指定消息队列的属性
 *  @Para       : mqd_t mqdes         消息队列描述符
 *                struct mq_attr * attr   返回属性结构的存储地址
 *  @return     : 成功返回0,失败返回-1
**/
int mq_getattr(mqd_t mqdes, struct mq_attr * attr);

/*
 *  @Description: 设置指定消息队列属性
 *  @Para       : mqd_t mqdes         消息队列描述符
 *                struct mq_attr * newattr   设置消息队列的结构
 *                struct mq_attr * oldattr   如果不为NULL,则此结构将会返回设置前的消息队列属性,与mq_getattr()执行的任务相同。
 *  @return     : 成功返回0,失败返回-1
**/
int mq_setattr(mqd_t mqdes, const struct mq_attr * newattr, struct mq_attr * oldattr);
  1. 可以通过返回的结中的 mq_flags 查看该消息队列的mq_send() mq_receive()是否可阻塞;
  2. 可以通过返回的结中的 mq_curmsgs 查看消息队列中现有的消息队列数量,该值具有短时效性,可能读取该值后,马上又进程发送或读取消息队列;
  3. Linux上 mq_maxmsg 值为10, mq_maxmsg 值为8192。
  4. mq_setattr()仅能修改mq_flags。其它参数修改都是无效的。

发送接收消息

#include <mqueue.h>
#include <time.h>

/*
 *  @Description: 发送消息到指定消息队列中
 *  @Para       : mqd_t mqdes            消息队列描述符
 *                const char * msg_ptr   发送的消息存储区首地址
 *                size_t msg_len         发送消息的长度
 *                unsigned int msg_prio  发送消息的优先级,0为最低优先级
 *  @return     : 成功返回0,失败返回-1
**/
int mq_send(mqd_t mqdes, const char * msg_ptr, size_t msg_len, unsigned int msg_prio);

/*
 *  @Description: 发送消息到指定消息队列中,如果阻塞发送,到达超时时间则返回ETIMEDOUT错误
 *  @Para       : mqd_t mqdes            消息队列描述符
 *                const char * msg_ptr   发送的消息存储区首地址
 *                size_t msg_len         发送消息的长度
 *                unsigned int msg_prio  发送消息的优先级,0为最低优先级
 *                const struct timespec * abs_timeout   到达该绝对时间还未完成发送,则返回错误
 *  @return     : 成功返回0,失败返回-1
**/
int mq_timedsend(mqd_t mqdes, const char * msg_ptr, size_t msg_len, unsigned int msg_prio, const struct timespec * abs_timeout);

/*
 *  @Description: 从指定消息队列接收消息
 *  @Para       : mqd_t mqdes            消息队列描述符
 *                const char * msg_ptr   接收消息存储区首地址
 *                size_t msg_len         接收消息的长度
 *                unsigned int * msg_prio  返回接收到的消息的优先级
 *  @return     : 成功返回接收到的字节数,失败返回-1
**/
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int * msg_prio);

/*
 *  @Description: 从指定消息队列接收消息,如果阻塞接收,到达超时时间则返回ETIMEDOUT错误
 *  @Para       : mqd_t mqdes            消息队列描述符
 *                const char * msg_ptr   接收消息存储区首地址
 *                size_t msg_len         接收消息的长度
 *                unsigned int * msg_prio  返回接收到的消息的优先级
 *                const struct timespec * abs_timeout   到达该绝对时间还未完成接收,则返回错误
 *  @return     : 成功返回接收到的字节数,失败返回-1
**/
ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int * msg_prio, const struct timespec * abs_timeout);

注意:

  1. 在mq_send()中,msg_ptr指向的消息长度,msg_len的值,必须小于或等于队列的属性mq_msgsize,否则mq_send()会返回EMSGSIZE错误。
  2. 每条消息都拥有一个用非负整数表示的优先级,消息队列中的消息是按照优先级倒序排列,高优先级在前,0表示最低优先级。同等优先级的消息按照入队的先后次序排列。消息队列的最高优先级在各个不同系统中定义不同,Linux中最高优先级为32768。
  3. 不论消息的实际大小是多少,msg_ptr指向的存储区长度,与msg_len必须要大于或等于队列的mq_msgsize属性,否则mq_receive()就会失败返回EMSGSIZE错误。
  4. 如果消息队列已满,则mq_send()会阻塞进程,直到消息队列中消息被取走。如果消息队列为空,则mq_receive()会阻塞进程,直到消息队列有消息被发入。
  5. 超时时间参数 abs_timeout 为绝对时间,指定相对超时时间可使用clock_gettime()来获取CLOCK_REALTIME时钟的当前值,再加上相对时间,得到未来的时间绝对值,设置为abs_timeout即可。

关闭和删除消息队列

#include <mqueue.h>

/*
 *  @Description: 断开当前进程与消息队列的关联关系
 *  @Para       : mqd_t mqdes            消息队列描述符
 *  @return     : 成功返回0,失败返回-1
**/
int mq_close(mqd_t mqdes);

/*
 *  @Description: 将消息队列name标记为:当所有进程与该消息队列断开关联关系后销毁该队列。
 *  @Para       : const char * name       要删除的消息队列名称
 *  @return     : 成功返回0,失败返回-1
**/
int mq_unlink(const char * name);

注意:

  1. 进程在使用完消息队列后,应该使用mq_close()断开关联关系,释放描述符,防止出现消息队列耗尽描述符的情况。
  2. mq_unlink()仅做标记,是否马上删除消息队列决定于是否仍有进程关联消息队列。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值