O_CREAT
这个标志是在创建消息队列时需要传入的打开标志。它的含义是,如果该消息队列不存在,那么就创建它,它的隐含含义就是如果该消息队列是存在的,那么O_CREAT不起作用,而仅仅执行打开操作,不会报错。
编译测试代码后重复运行该执行文件,都次都会成功打开,打印如下:
$ ./posix_msq
[6590] INFO: mq_open success
O_EXCL
一般这个标志是和O_CREAT一起使用来创建一个消息队列,此时当该消息队列已经存在时,返回错误EEXIST。
测试打印如下:
[6495] ERROR: mq_open error, File exists
这两个场景的测试实例代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "include/debug.h"
#define MSG_QUEUE_NAME "/test_msg_queue"
#define MSG_QUIT "quit"
#define MAX_SIZE 512
int main(int argc, char **argv)
{
mqd_t mq;
struct mq_attr attr;
char buffer[MAX_SIZE + 1];
int must_stop = 0;
/* initialize the queue attributes */
attr.mq_flags = 0;
attr.mq_maxmsg = 10;
attr.mq_msgsize = MAX_SIZE;
attr.mq_curmsgs = 0;
/* create the message queue */
#if 1
mq = mq_open(MSG_QUEUE_NAME, O_CREAT | O_RDONLY, 0644, &attr);
#else
mq = mq_open(MSG_QUEUE_NAME, O_CREAT | O_EXCL | O_RDONLY, 0644, &attr);
#endif
if ((mqd_t)-1 == mq) {
err_exit("mq_open error, %s\n", strerror(errno));
}
pr_info("mq_open success\n");
do {
ssize_t bytes_read;
/* receive the message */
bytes_read = mq_receive(mq, buffer, MAX_SIZE, NULL);
if(bytes_read < 0) {
err_exit("mq_receive error, %s\n", strerror(errno));
}
buffer[bytes_read] = '\0';
if (!strncmp(buffer, MSG_QUIT, strlen(MSG_QUIT))) {
must_stop = 1;
} else {
pr_info("Received: %s\n", buffer);
}
} while (!must_stop);
/*
if ((mqd_t)-1 == mq_close(mq)) {
err_exit("mq_close error\n");
}
if ((mqd_t)-1 == mq_unlink(MSG_QUEUE_NAME)) {
err_exit("mq_unlink error\n");
}
*/
return 0;
}
对于systemv消息队列使用msgrcv来接收数据,如果消息实际长度大于传入要接收的数据时,根据消息标志MSG_NOERROR是否设置,其结果会表现不同。如果接收的消息指定了MSG_NOERROR则说明,不会把这种情况当作错误,而是会主动截断消息长度到接收长度并返回正确。否则该消息不会被接收,而msgrcv直接返回-1,errno设置为E2BIG。
而本文介绍的posix消息队列则大有不同:
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr,
size_t msg_len, unsigned int *msg_prio);
对于posix消息队列,使用说明上明确说明了,如果msg_len小于该队列的属性中mq_msgsize的大小值,那么会直接返回-1出错,并且设置errno为EMSGSIZE。同样使用上面的实例代码稍作改动:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "include/debug.h"
#define MSG_QUEUE_NAME "/test_msg_queue"
#define MSG_QUIT "quit"
#define MAX_SIZE 512
int main(int argc, char **argv)
{
mqd_t mq;
struct mq_attr attr;
char buffer[MAX_SIZE + 1];
int must_stop = 0;
/* initialize the queue attributes */
attr.mq_flags = 0;
attr.mq_maxmsg = 10;
attr.mq_msgsize = MAX_SIZE;
attr.mq_curmsgs = 0;
/* create the message queue */
mq = mq_open(MSG_QUEUE_NAME, O_CREAT | O_RDONLY, 0644, &attr);
if ((mqd_t)-1 == mq) {
err_exit("mq_open error, %s\n", strerror(errno));
}
pr_info("mq_open success\n");
do {
ssize_t bytes_read;
/* receive the message */
bytes_read = mq_receive(mq, buffer, MAX_SIZE - 1, NULL);
if(bytes_read < 0) {
err_exit("mq_receive error, %s\n", strerror(errno));
}
buffer[bytes_read] = '\0';
if (!strncmp(buffer, MSG_QUIT, strlen(MSG_QUIT))) {
must_stop = 1;
} else {
pr_info("Received: %s\n", buffer);
}
} while (!must_stop);
/* cleanup */
if ((mqd_t)-1 == mq_close(mq)) {
err_exit("mq_close error\n");
}
if ((mqd_t)-1 == mq_unlink(MSG_QUEUE_NAME)) {
err_exit("mq_unlink error\n");
}
return 0;
}
这里把接收时传入的参数为MAX_SIZE - 1, 小于消息队列属性值大小,运行打印如下:
[6661] INFO: mq_open success
[6661] ERROR: mq_receive error, Message too long
mq_close只是用来关闭一个进程中使用的消息队列描述符,消息队列本身依然存在,其中的消息也都会保存。
mq_unlink用于删除消息队列的name,如果存在打开的描述符,那么该消息队列依然可用,直到所有打开的描述符都关闭后才会destory,这一点需要特别注意。
测试实例:
server:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "include/debug.h"
#define MSG_QUEUE_NAME "/test_msg_queue"
#define MSG_QUIT "quit"
#define MAX_SIZE 512
int main(int argc, char **argv)
{
mqd_t mq;
struct mq_attr attr;
char buffer[MAX_SIZE + 1];
int must_stop = 0;
/* initialize the queue attributes */
attr.mq_flags = 0;
attr.mq_maxmsg = 10;
attr.mq_msgsize = MAX_SIZE;
attr.mq_curmsgs = 0;
/* create the message queue */
mq = mq_open(MSG_QUEUE_NAME, O_CREAT | O_RDONLY, 0644, &attr);
if ((mqd_t)-1 == mq) {
err_exit("mq_open error, %s\n", strerror(errno));
}
pr_info("mq_open success\n");
do {
ssize_t bytes_read;
pr_info("start received...\n");
/* receive the message */
bytes_read = mq_receive(mq, buffer, MAX_SIZE, NULL);
if(bytes_read < 0) {
err_exit("mq_receive error, %s\n", strerror(errno));
}
buffer[bytes_read] = '\0';
if (!strncmp(buffer, MSG_QUIT, strlen(MSG_QUIT))) {
must_stop = 1;
} else {
pr_info("Received: %s\n", buffer);
}
} while (!must_stop);
/* cleanup */
if ((mqd_t)-1 == mq_close(mq)) {
err_exit("mq_close error\n");
}
if ((mqd_t)-1 == mq_unlink(MSG_QUEUE_NAME)) {
err_exit("mq_unlink error\n");
}
return 0;
}
client:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "include/debug.h"
#define MSG_QUEUE_NAME "/test_msg_queue"
#define MSG_QUIT "quit"
#define MAX_SIZE 512
int main(int argc, char **argv)
{
mqd_t mq;
if (argc < 2)
err_exit("need an argument as message\n");
/* open the mail queue */
mq = mq_open(MSG_QUEUE_NAME, O_WRONLY);
if ((mqd_t)-1 == mq) {
err_exit("mq_open error, %s\n", strerror(errno));
}
pr_info("mq_open success\n");
/* send the message */
if (0 < mq_send(mq, argv[1], strlen(argv[1]), 0)) {
err_exit("mq_send error,%s\n", strerror(errno));
}
/* cleanup */
#if 0
if ((mqd_t)-1 == mq_close(mq)){
err_exit("mq_close error, %s\n", strerror(errno));
}
#else
if (-1 == mq_unlink(MSG_QUEUE_NAME)){
err_exit("mq_unlink error, %s\n", strerror(errno));
}
if (0 < mq_send(mq, argv[1], strlen(argv[1]), 0)) {
err_exit("mq_send error,%s\n", strerror(errno));
}
#endif
return 0;
}
最后server在unlink后依然可以接收到数据:
[8222] INFO: mq_open success
[8222] INFO: start received...
[8222] INFO: Received: 123
[8222] INFO: start received...
[8222] INFO: Received: 123
[8222] INFO: start received...
情形1:多线程环境中,一个线程1正在阻塞读,另一个线程2使用mq_close关闭了描述符,那么会对其有什么影响呢?
实测发现,虽然该消息队列实际已经关闭,但这种情况不会打断线程1的阻塞状态。
[8502] INFO: mq_open success
[8502] INFO: start received...
[8502] INFO: close mq descriptor now!
情形2:线程1使用timeout接口或者nonblock方式重复读消息队列,另一个线程2使用mq_close关闭了描述符,那么会对其有什么影响呢?
这种情况下,线程1在关闭描述符后再次读取时会报错返回。
[8514] INFO: mq_open success
[8514] INFO: start received...
[8514] INFO: tv_sec:1555164263 tv_nsec:724058183
[8514] INFO: mq_timedreceive timeout
[8514] INFO: tv_sec:1555164266 tv_nsec:724172113
[8514] INFO: mq_timedreceive timeout
[8514] INFO: tv_sec:1555164269 tv_nsec:724316674
[8514] INFO: mq_timedreceive timeout
[8514] INFO: tv_sec:1555164272 tv_nsec:724467855
[8514] INFO: close mq descriptor now!
[8514] INFO: mq_timedreceive timeout
[8514] INFO: tv_sec:1555164275 tv_nsec:724606740
[8514] ERROR: mq_receive error, Bad file descriptor
测试代码如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "include/debug.h"
#define MSG_QUEUE_NAME "/test_msg_queue"
#define MSG_QUIT "quit"
#define MAX_SIZE 512
static mqd_t mq;
void *thr_fn(void *arg)
{
sleep(10);
pr_info("close mq descriptor now!\n");
/* cleanup */
if ((mqd_t)-1 == mq_close(mq)) {
err_exit("mq_close error\n");
}
return(0);
}
int mq_receive_timeout(mqd_t mq, char *buf, int len, int second)
{
int bytes_read;
struct timespec timeout;
do {
clock_gettime(CLOCK_REALTIME, &timeout);
pr_info("tv_sec:%ld tv_nsec:%ld\n",
timeout.tv_sec,
timeout.tv_nsec);
timeout.tv_sec += second;
bytes_read = mq_timedreceive(mq, buf, len, NULL, &timeout);
if(bytes_read < 0) {
if (errno != ETIMEDOUT)
err_exit("mq_receive error, %s\n", strerror(errno));
else
pr_info("mq_timedreceive timeout\n");
} else if (bytes_read > 0)
break;
} while(1);
return bytes_read;
}
int main(int argc, char **argv)
{
struct mq_attr attr;
char buffer[MAX_SIZE + 1];
int must_stop = 0, err;
pthread_t tid;
/* initialize the queue attributes */
attr.mq_flags = 0;
attr.mq_maxmsg = 10;
attr.mq_msgsize = MAX_SIZE;
attr.mq_curmsgs = 0;
/* create the message queue */
mq = mq_open(MSG_QUEUE_NAME, O_CREAT | O_RDONLY, 0644, &attr);
if ((mqd_t)-1 == mq) {
err_exit("mq_open error, %s\n", strerror(errno));
}
/*
* Create a child thread
*/
err = pthread_create(&tid, NULL, thr_fn, 0);
if (err != 0)
err_exit("can't create thread\n");
pr_info("mq_open success\n");
do {
ssize_t bytes_read;
pr_info("start received...\n");
/* receive the message */
#if 1
bytes_read = mq_receive_timeout(mq, buffer, MAX_SIZE, 3);
if(bytes_read < 0) {
err_exit("mq_receive error, %s\n", strerror(errno));
}
#else
bytes_read = mq_receive(mq, buffer, MAX_SIZE, NULL);
if(bytes_read < 0) {
err_exit("mq_receive error, %s\n", strerror(errno));
}
#endif
buffer[bytes_read] = '\0';
if (!strncmp(buffer, MSG_QUIT, strlen(MSG_QUIT))) {
must_stop = 1;
} else {
pr_info("Received: %s\n", buffer);
}
} while (!must_stop);
/* cleanup */
if ((mqd_t)-1 == mq_close(mq)) {
err_exit("mq_close error\n");
}
if ((mqd_t)-1 == mq_unlink(MSG_QUEUE_NAME)) {
err_exit("mq_unlink error\n");
}
return 0;
}