Linux POSIX 消息队列
文章目录
进程间通信的各种方式
- 管道;
- FIFO;
- socket;
- 消息队列;
- 共享内存;
其中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");
注意:
- 消息队列的名称参数name,必须以斜线开头,后面跟着一个或多个非斜线字符的名字,如 “/myqueue”, 名称的最长字符数为 NAME_MAX(255) - 4;
- 当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错误
- 如果mq_open()用于打开一个既有消息队列,则仅适用前两个参数即可。如果是创建新消息队列,则可以通过mode_t mode设置新创建的消息队列的文件权限(Owner Group Other, Read Write Execute等),通过 struct mq_attr * attr可以设置消息队列的属性,下节详细讲解,如果设为NULL则为默认属性。
- 当打开或者创建消息队列时,会自动建立进程与消息队列间的关联关系,可以通过mq_close()断开该关联关系;
消息队列的特性 struct mq_attr
创建消息队列时,需要设置消息队列的属性,其属性结构如下。
struct mq_attr{
long mq_flags; //0 或者 O_NONBLOCK 创建时,创建后均可修改
long mq_maxmsg; //创建的消息队列所能添加消息的数量上限,其取值必须大于零,仅创建的时候可以设置。
long mq_msgsize; //消息队列中每条消息的大小上限,其取值必须大于零,仅创建的时候可以设置。
long mq_curmsgs; //消息队列中,消息的数量,只读
};
注意:
- 消息队列的属性参数仅有 mq_flags 可以在创建后设置。
- 设置为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);
- 可以通过返回的结中的 mq_flags 查看该消息队列的mq_send() mq_receive()是否可阻塞;
- 可以通过返回的结中的 mq_curmsgs 查看消息队列中现有的消息队列数量,该值具有短时效性,可能读取该值后,马上又进程发送或读取消息队列;
- Linux上 mq_maxmsg 值为10, mq_maxmsg 值为8192。
- 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);
注意:
- 在mq_send()中,msg_ptr指向的消息长度,msg_len的值,必须小于或等于队列的属性mq_msgsize,否则mq_send()会返回EMSGSIZE错误。
- 每条消息都拥有一个用非负整数表示的优先级,消息队列中的消息是按照优先级倒序排列,高优先级在前,0表示最低优先级。同等优先级的消息按照入队的先后次序排列。消息队列的最高优先级在各个不同系统中定义不同,Linux中最高优先级为32768。
- 不论消息的实际大小是多少,msg_ptr指向的存储区长度,与msg_len必须要大于或等于队列的mq_msgsize属性,否则mq_receive()就会失败返回EMSGSIZE错误。
- 如果消息队列已满,则mq_send()会阻塞进程,直到消息队列中消息被取走。如果消息队列为空,则mq_receive()会阻塞进程,直到消息队列有消息被发入。
- 超时时间参数 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);
注意:
- 进程在使用完消息队列后,应该使用mq_close()断开关联关系,释放描述符,防止出现消息队列耗尽描述符的情况。
- mq_unlink()仅做标记,是否马上删除消息队列决定于是否仍有进程关联消息队列。