《UNIX网络编程 卷2》 笔记: Posix消息队列(1)

来源:互联网 发布:上海大华电子秤软件 编辑:程序博客网 时间:2024/05/18 18:15

消息队列可认为是由多个消息组成的链表,每个消息被赋予了优先级,它具有随内核的持续性(持续性的定义见简介)。有权限的进程可以放置消息到消息队列中,也可以从消息队列中取消息,取出的消息是放置时间最早且优先级最高的。

POSIX表示可移植操作系统接口(Portable Operating System Interface of UNIX),POSIX标准定义了操作系统应该为应用程序提供的接口标准。本系列文章关注的都是POSIX标准的IPC,不关注书中讲述的System V IPC。

下图展示了一个Posix消息队列的可能布局。


链表头中含有当前队列的两个属性:队列中允许的最大消息数和每个消息的最大字节数。

Posix定义的消息队列的接口如下:

#include <fcntl.h>           /* For O_* constants */#include <sys/stat.h>        /* For mode constants */#include <mqueue.h>/*打开或创建一个消息队列*/mqd_t mq_open(const char *name, int oflag);mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);/*关闭一个消息队列*/int mq_close(mqd_t mqdes);/*销毁一个消息队列*/int mq_unlink(const char *name);/*获取一个消息队列的属性*/int mq_getattr(mqd_t mqdes, struct mq_attr *attr);/*设置一个消息队列的属性*/int mq_setattr(mqd_t mqdes, const struct mq_attr *newattr,struct mq_attr *oldattr);/*放置一个消息到消息队列中*/int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio);/*从消息队列中取出一个消息*/ ssize_t mq_receive(mqd_t mqdes, char *msg_ptr,  size_t msg_len, unsigned int *msg_prio);  /*为进程注册一个消息队列通知*/  int mq_notify(mqd_t mqdes, const struct sigevent *sevp);

下面给出几个使用这些接口的小程序的代码。

1. mqcreate1:创建一个默认属性的消息队列,用法为 mqcreate1 [-e] <name>。如果指定-e参数,消息队列存在时会报错。

#include "unpipc.h"int main(int argc, char **argv){int c, flags;mqd_t mqd;flags = O_RDWR | O_CREAT;while ((c = getopt(argc, argv, "e")) != -1) {switch (c) {case 'e':flags |= O_EXCL;break;}}if (optind != argc - 1)err_quit("usage: mqcreate1 [-e] <name>");/*创建一个消息队列*/mqd = Mq_open(argv[optind], flags, FILE_MODE, NULL);Mq_close(mqd);exit(0);}

执行如下的命令创建一个消息队列。(Ubuntu16.04环境下)

liu@ubuntu:~/work$ ./mqcreate1 /tmp.123
liu@ubuntu:~/work$ ls -l /dev/mqueue/tmp.123
-rw-r--r-- 1 liu liu 80 Sep  4 07:45 /dev/mqueue/tmp.123

注意:mq_open函数的name字符串参数必须以‘/’字符开头,后面是消息队列真正的名字“tmp.123”,名字中不能再包含‘/’字符,否则会报错。可以看到创建后的消息队列实际是/dev/mqueue目录下的一个文件。


2. mqcreate:创建一个指定属性的消息队列,用法为 mqcreate [-e] [-m maxmsg -z msgsize] <name>。name为消息队列的名字,maxmsg为该消息队列的最大消息数,magsize为一个消息的最大字节数。

int main(int argc, char **argv){int c, flags;mqd_t mqd;/*可读可写,创建*/flags = O_RDWR | O_CREAT;/*"em:z:"表示选项m和z后面跟着参数,选项e后面没有参数*/while ((c = getopt(argc, argv, "em:z:")) != -1) {switch (c) {case 'e':/*同时指定了O_CREAT和O_EXCL选项,文件存在时就会报错*/flags |= O_EXCL;break;case 'm':attr.mq_maxmsg = atol(optarg);break;case 'z':attr.mq_msgsize = atol(optarg);break;}}if (optind != argc - 1)err_quit("usage: mqcreate [-e] [-m maxmsg -z msgsize] <name>");if ((attr.mq_maxmsg != 0 && attr.mq_msgsize == 0) || (attr.mq_maxmsg == 0 && attr.mq_msgsize != 0))err_quit("must specify both -m maxmsg and -z msgsize");/*创建一个指定属性的消息队列*/mqd = Mq_open(argv[optind], flags, FILE_MODE, (attr.mq_maxmsg != 0) ? &attr : NULL);Mq_close(mqd);exit(0);}


3. mqgetattr:获取一个消息队列的属性,用法为mqgetattr <name>。

#include "unpipc.h"int main(int argc, char **argv){mqd_t mqd;struct mq_attr attr;if (argc != 2)err_quit("usage: mqgetattr <name>");mqd = Mq_open(argv[1], O_RDONLY);Mq_getattr(mqd, &attr);printf("max #mesgs = %ld, max #bytes/msg = %ld,""#currently on queue = %ld\n", attr.mq_maxmsg, attr.mq_msgsize, attr.mq_curmsgs);Mq_close(mqd);exit(0);}

我们执行如下命令获取我们刚创建的消息队列的属性。

liu@ubuntu:~/work$ ./mqgetattr /tmp.123         
max #mesgs = 10, max #bytes/msg = 8192,#currently on queue = 0

可以看到,ubuntu16.04环境下,消息队列的最大长度是10,每个消息最大的字节数是8192。


4. mqsend:放置一个消息到消息队列,用法为mqsend <name> <#bytes> <priority>。bytes为消息字节数,priority为消息优先级。

#include "unpipc.h"int main(int argc, char **argv){mqd_t mqd;void *ptr;size_t len;unsigned int prio;if (argc != 4)err_quit("usage: mqsend <name> <#bytes> <priority>");len = atoi(argv[2]);prio = atoi(argv[3]);/*以写的方式打开消息队列*/mqd = Mq_open(argv[1], O_WRONLY);ptr = Calloc(len, sizeof(char));/*发送消息,如果消息队列满,则会阻塞*/Mq_send(mqd, ptr, len, prio);exit(0);}

执行如下的命令放置消息到我们创建的消息队列。

liu@ubuntu:~/work$ ./mqsend /tmp.123 100 10
liu@ubuntu:~/work$ ./mqsend /tmp.123 100 20
liu@ubuntu:~/work$ ./mqsend /tmp.123 100 30
liu@ubuntu:~/work$ ./mqsend /tmp.123 200 30
liu@ubuntu:~/work$ ./mqgetattr /tmp.123
max #mesgs = 10, max #bytes/msg = 8192,#currently on queue = 4

可以看到该消息队列里已经有了4条消息。


5. mqreceive:从消息队里中获取一个消息,用法为mqreceive [-n] <name>。如果指定-n参数,则以非阻塞的方式打开消息队列。

int main(int argc, char **argv){int c, flags;mqd_t mqd;ssize_t n;unsigned int prio;void *buff;struct mq_attr attr;flags = O_RDONLY;while ((c = getopt(argc, argv, "n")) != -1) {switch (c) {case 'n':flags |= O_NONBLOCK;break;}}if (optind != argc - 1)err_quit("usage: mqreceive [-n] <name>");/*打开消息队列*/mqd = Mq_open(argv[optind], flags);Mq_getattr(mqd, &attr);buff = Malloc(attr.mq_msgsize);/*读取消息,如果消息队列为空,则会阻塞以非阻塞方式打开,则会立即返回*/n = Mq_receive(mqd, buff, attr.mq_msgsize, &prio);printf("read %ld bytes, priority = %u\n", (long)n, prio);exit(0);}
执行如下命令读取消息队列中的消息。
liu@ubuntu:~/work$ ./mqreceive /tmp.123    
read 100 bytes, priority = 30
liu@ubuntu:~/work$ ./mqreceive /tmp.123
read 200 bytes, priority = 30
liu@ubuntu:~/work$ ./mqreceive /tmp.123
read 100 bytes, priority = 20
liu@ubuntu:~/work$ ./mqreceive /tmp.123
read 100 bytes, priority = 10
liu@ubuntu:~/work$ ./mqreceive /tmp.123

^C

可以看到首先获取的是放置时间最早的优先级最高的消息,然后是优先级低的消息。当消息队列为空时,mq_receive一直阻塞等待一个消息。

原创粉丝点击