Linux posix消息队列使用详解及注意事项

创建标志

O_CREAT
这个标志是在创建消息队列时需要传入的打开标志。它的含义是,如果该消息队列不存在,那么就创建它,它的隐含含义就是如果该消息队列是存在的,那么O_CREAT不起作用,而仅仅执行打开操作,不会报错。

编译测试代码后重复运行该执行文件,都次都会成功打开,打印如下:

$ ./posix_msq 
[6590] INFO:  mq_open success

O_EXCL
一般这个标志是和O_CREAT一起使用来创建一个消息队列,此时当该消息队列已经存在时,返回错误EEXIST。
测试打印如下:

[6495] ERROR: mq_open error, File exists

这两个场景的测试实例代码:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <mqueue.h>
#include <fcntl.h>
#include <string.h>
#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 <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <mqueue.h>
#include <fcntl.h>
#include <string.h>
#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 <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <mqueue.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#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 <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <mqueue.h>
#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 <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <mqueue.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <time.h>
#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;
}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值