Posix消息队列
Posted _小百
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Posix消息队列相关的知识,希望对你有一定的参考价值。
转载于:http://blog.csdn.net/zx714311728/article/details/53197196、
1.消息队列
消息队列可以认为是一个消息链表,消息队列是随内核持续的。队列中每个消息的属性有:一个无符号整数优先级(Poxis)或一个长整数类型(System V);消息的数据部分长度(可以为0);数据本身。链表头含有当前队列的两个属性:队列中运行的最大消息数、每个消息的最大大小。消息队列的可能布局如下:
Posix消息队列与System V消息队列主要区别:
1.对Posix消息队列的读总是返回最高优先级的消息,对System V消息队列的读则可以返回任一指定优先级消息。
2.往空队列放置消息时,Posix消息队列允许产生一个信号或启动一个线程,System V则不提供类似机制
Posix消息队列与管道或FIFO的主要区别:
1.在某个进程往一个队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达。对管道和FIFO来说,除非读出者已存在,否则先有写入者是没有意义的。
2.管道和FIFO是字节流模型,没有消息边界;消息队列则指定了数据长度,有边界。
2.mq_open 、 mq_close、 mq_unlink
//创建新消息队列或打开已存在的消息队列
#include <mqueue.h>
mqd_t mq_open(const char *name, int oflag, .../*mode_t mode, struct mq_attr *attr*/);
返回值:成功,消息队列描述符;失败,-1
消息队列描述符用作其余消息队列函数(mq_unlink除外)的第一个参数。
name规则:必须以一个斜杠符打头,并且不能再包含任何其他斜杠符
oflag:O_RDONLY、O_WRONLY、O_RDWR三者之一,按位或上O_CREAT、O_EXCL
mode:S_ISRUSR、S_ISWUSR、S_ISRGRP、S_ISWGRP、S_ISROTH、S_ISWOTH
attr:
struct mq_attr
{
long mq_flags;//阻塞标志, 0或O_NONBLOCK
long mq_maxmsg;//最大消息数
long mq_msgsize;//每个消息最大大小
long mq_curmsgs;//当前消息数
};
//关闭已打开的消息队列
int mq_close(mqd_t mqd);
返回值:成功,0;出错,-1
一个进程终止时,它的所有打开的消息队列都关闭,如同调用了mq_close。
//删除消息队列的name
int mq_unlink(const char *name);
返回值:成功,0;出错,-1
每个消息队列有一个保存其当前打开的描述符数的引用计数,只有当引用计数为0时,才删除该消息队列。mq_unlink和mq_close都会让引用数减一
例1:创建一个消息队列,并可使用排他性检验
程序:
- #include <stdio.h>
- #include <mqueue.h>
- #include <unistd.h>
- #include <sys/stat.h>
- #include <errno.h>
- #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
- int main(int argc, char *argv[])
- {
- int c;
- int flag = O_RDWR | O_CREAT;
- while ((c = getopt(argc, argv, "e")) != -1)
- {
- switch(c)
- {
- case ‘e‘:
- flag |= O_EXCL;
- break;
- }
- }
- if (optind != argc -1)
- {
- printf("usage: mqcreate [- e] <name>\n");
- exit(0);
- }
- mqd_t mqd = mq_open(argv[optind], flag, FILE_MODE, NULL);
- if (mqd == -1)
- {
- printf("mq_open() error %d : %s\n", errno, strerror(errno));
- exit(-1);
- }
- mq_close(mqd);
- exit(0);
- }
#include <stdio.h> #include <mqueue.h> #include <unistd.h> #include <sys/stat.h> #include <errno.h> #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) int main(int argc, char *argv[]) { int c; int flag = O_RDWR | O_CREAT; while ((c = getopt(argc, argv, "e")) != -1) { switch(c) { case ‘e‘: flag |= O_EXCL; break; } } if (optind != argc -1) { printf("usage: mqcreate [- e] <name>\n"); exit(0); } mqd_t mqd = mq_open(argv[optind], flag, FILE_MODE, NULL); if (mqd == -1) { printf("mq_open() error %d : %s\n", errno, strerror(errno)); exit(-1); } mq_close(mqd); exit(0); }
分析:
1.mq_xxx()函数不是标准库函数,所以链接时需指定库,通过在最后加上-lrt选项来指定。
2.程序运行之后可能会看不到消息队列,要看到创建的Posix消息队列,需执行以下操作:
mkdir /dev/mqueue
mount -t mqueue none /dev/mqueue
3.程序通过getopt()函数获取带选项的命令行参数,optind指向下一个要读取的参数在argv[]中的位置。若遇到没包含在getopt第三个参数的选项字母,或者遇到一个没有所需参数的选项字母(通过在第三个参数后跟一个冒号指示)则出错。
4.若命令行参数带有-e选项,则进行排他性检测。
5.若mq_open()出错,则通过strerror(errno)函数得到错误说明。
结果:
例2:使用mq_unlink删除一个消息队列
程序:
- #include <stdio.h>
- #include <mqueue.h>
- #include <errno.h>
- int main(int argc, char *argv[])
- {
- if (argc != 2)
- {
- printf("argument error\n");
- exit(-1);
- }
- int val = mq_unlink(argv[1]);
- if (val != 0)
- {
- printf("mq_unlink() error %d : %s\n", errno, strerror(errno));
- exit(-2);
- }
- exit(0);
- }
#include <stdio.h> #include <mqueue.h> #include <errno.h> int main(int argc, char *argv[]) { if (argc != 2) { printf("argument error\n"); exit(-1); } int val = mq_unlink(argv[1]); if (val != 0) { printf("mq_unlink() error %d : %s\n", errno, strerror(errno)); exit(-2); } exit(0); }
分析:通过strerror(errno)得到错误信息
结果:
3.mq_getattr、 mq_setattr
//获取、设置消息队列属性
int mq_getattr(mqd_t mqd, struct mq_attr *attr);
int mq_setattr(mqd_t mqd, const struct mq_attr *attr, struct mq_attr *oattr);
返回值:成功,0;出错,-1
消息队列先前属性返回到oattr中
消息队列有4个属性,如下:
struct mq_attr
{
long mq_flags;//阻塞标志, 0或O_NONBLOCK
long mq_maxmsg;//最大消息数
long mq_msgsize;//每个消息最大大小
long mq_curmsgs;//当前消息数
};
其中,mq_setattr只能设置mq_flags属性;mq_open只能设置mq_maxmsg和mq_msgsize属性,并且两个必须要同时设置;mq_getattr返回全部4个属性。
例3:打开一个消息队列并输出其属性
程序:
- #include <stdio.h>
- #include <mqueue.h>
- #include <errno.h>
- int main(int argc, char *argv[])
- {
- if (argc != 2)
- {
- printf("must have 2 arguments !\n");
- exit(-1);
- }
- mqd_t mqd = mq_open(argv[1], O_RDONLY);
- if (mqd == -1)
- {
- printf("mq_open() error %d: %s\n", errno, strerror(errno));
- exit(-2);
- }
- struct mq_attr attr;
- if (mq_getattr(mqd, &attr) != 0)
- {
- printf("mq_open() error %d: %s\n", errno, strerror(errno));
- exit(-3);
- }
- if (attr.mq_flags == 0)
- printf("mq_flags = 0, ");
- else
- printf("mq_flags = O_NONBLOCK, ");
- printf("mq_maxmsg = %d, ", attr.mq_maxmsg);
- printf("mq_msgsize = %d, ", attr.mq_msgsize);
- printf("mq_curmsgs = %d\n", attr.mq_curmsgs);
- exit(0);
- }
#include <stdio.h> #include <mqueue.h> #include <errno.h> int main(int argc, char *argv[]) { if (argc != 2) { printf("must have 2 arguments !\n"); exit(-1); } mqd_t mqd = mq_open(argv[1], O_RDONLY); if (mqd == -1) { printf("mq_open() error %d: %s\n", errno, strerror(errno)); exit(-2); } struct mq_attr attr; if (mq_getattr(mqd, &attr) != 0) { printf("mq_open() error %d: %s\n", errno, strerror(errno)); exit(-3); } if (attr.mq_flags == 0) printf("mq_flags = 0, "); else printf("mq_flags = O_NONBLOCK, "); printf("mq_maxmsg = %d, ", attr.mq_maxmsg); printf("mq_msgsize = %d, ", attr.mq_msgsize); printf("mq_curmsgs = %d\n", attr.mq_curmsgs); exit(0); }
分析:因为要先通过mq_open打开消息队列,才能获取其属性,所以消息队列的命令行参数必须要符合name的规则。
结果:
例4:重新实现例1,这次创建消息队列时,指定其属性。
程序:
- #include <stdio.h>
- #include <mqueue.h>
- #include <errno.h>
- #include <unistd.h>
- #include <fcntl.h>
- #define MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
- struct mq_attr attr;
- int main(int argc, char *argv[])
- {
- int c;
- int flag = O_RDWR | O_CREAT;
- while ((c = getopt(argc, argv, "em:z:")) != -1)
- {
- switch (c)
- {
- case ‘e‘:
- flag |= O_EXCL;
- break;
- case ‘m‘:
- attr.mq_maxmsg = atol(optarg);
- break;
- case ‘z‘:
- attr.mq_msgsize = atol(optarg);
- break;
- }
- }
- if (optind != argc -1)
- {
- printf("usage: mqcreate -e [-m maxsg] [-z msgsize] <name>\n");
- exit(-1);
- }
- if ((attr.mq_maxmsg != 0 && attr.mq_msgsize == 0)
- || (attr.mq_maxmsg == 0 && attr.mq_msgsize != 0))
- {
- printf("must specify both -m maxmsg and -z msgsize\n");
- exit(-2);
- }
- mqd_t mqd = mq_open(argv[optind], flag, MODE, (attr.mq_maxmsg != 0) ? &attr : NULL);
- if (mqd == -1)
- {
- printf("mq_open() error %d: %s\n", errno, strerror(errno));
- exit(-3);
- }
- int rtn = mq_close(mqd);
- if (rtn == -1)
- {
- printf("mq_close() error\n");
- exit(-4);
- }
- exit(0);
- }
#include <stdio.h> #include <mqueue.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #define MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) struct mq_attr attr; int main(int argc, char *argv[]) { int c; int flag = O_RDWR | O_CREAT; while ((c = getopt(argc, argv, "em:z:")) != -1) { switch (c) { case ‘e‘: flag |= O_EXCL; break; case ‘m‘: attr.mq_maxmsg = atol(optarg); break; case ‘z‘: attr.mq_msgsize = atol(optarg); break; } } if (optind != argc -1) { printf("usage: mqcreate -e [-m maxsg] [-z msgsize] <name>\n"); exit(-1); } if ((attr.mq_maxmsg != 0 && attr.mq_msgsize == 0) || (attr.mq_maxmsg == 0 && attr.mq_msgsize != 0)) { printf("must specify both -m maxmsg and -z msgsize\n"); exit(-2); } mqd_t mqd = mq_open(argv[optind], flag, MODE, (attr.mq_maxmsg != 0) ? &attr : NULL); if (mqd == -1) { printf("mq_open() error %d: %s\n", errno, strerror(errno)); exit(-3); } int rtn = mq_close(mqd); if (rtn == -1) { printf("mq_close() error\n"); exit(-4); } exit(0); }
分析:
1.通过getopt参数获取命令行选项,可见例1的分析。其中“em:z:”,选项字母后带冒号的意思是,-m选项后要跟参数,-z选项后也要跟参数,否则出错。
2.optarg代表选项后的参数。
3.如果设置,则必须同时设置mq_maxmsg和mq_msgsize两个属性。
结果:
4.mq_send、 mq_receive
//往消息队列中放置一个消息
int mq_send(mqd_t mqd, const char *ptr, size_t len, unsigned int prio);
返回值:成功,0;出错,-1
说明:优先级prio要小于MQ_PRIO_MAX(此值最少为32)。
//从消息队列中取走一个消息
ssize_t mq_receive(mqd_t mqd, char *ptr, size_t len, unsigned int *prio);
返回值:成功,消息中字节数;出错,-1
说明:消息内存的长度len,最小要等于mq_msgsize。mq_receive总是返回消息队列中最高优先级的最早消息。
例5:往消息队列中添加一个消息
- #include <stdio.h>
- #include <mqueue.h>
- #include <errno.h>
- int main(int argc, char *argv[])
- {
- char *ptr;
- size_t size;
- unsigned long prio;
- mqd_t mqd;
- if (argc != 4)
- {
- printf("usage: mqsend <name> <#bytes> <priority>\n");
- exit(-1);
- }
- if ((mqd = mq_open(argv[1], O_WRONLY)) == -1)
- {
- printf("mq_open() error %d: %s\n", errno, strerror(errno));
- exit(-1);
- }
- size = atoi(argv[2]);
- prio = atoi(argv[3]);
- ptr = calloc(size, sizeof(char));
- if (mq_send(mqd, ptr, size, prio) == -1)
- {
- printf("mq_send() error %d: %s\n", errno, strerror(errno));
- exit(-1);
- }
- exit(0);
- }
#include <stdio.h> #include <mqueue.h> #include <errno.h> int main(int argc, char *argv[]) { char *ptr; size_t size; unsigned long prio; mqd_t mqd; if (argc != 4) { printf("usage: mqsend <name> <#bytes> <priority>\n"); exit(-1); } if ((mqd = mq_open(argv[1], O_WRONLY)) == -1) { printf("mq_open() error %d: %s\n", errno, strerror(errno)); exit(-1); } size = atoi(argv[2]); prio = atoi(argv[3]); ptr = calloc(size, sizeof(char)); if (mq_send(mqd, ptr, size, prio) == -1) { printf("mq_send() error %d: %s\n", errno, strerror(errno)); exit(-1); } exit(0); }
分析:
1.通过atoi()函数将命令行参数转换成int变量
2.通过calloc()函数分配内存,并初始化为0
3.要发送消息,要先为消息分配内存。 结果:和例6一起
例6:从消息队列中取出一个消息
程序:
- #include <stdio.h>
- #include <mqueue.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <unistd.h>
- #define ERR_EXIT(m)\
- {\
- printf("%s() error %d: %s\n", m, errno, strerror(errno));\
- exit(EXIT_FAILURE);\
- }
- int main(int argc, char *argv[])
- {
- int c;
- int flag = O_RDONLY;
- //设置非阻塞方式
- while ((c = getopt(argc, argv, "n")) != -1)
- {
- switch(c)
- {
- case ‘n‘:
- flag |= O_NONBLOCK;
- break;
- }
- }
- if (optind != argc -1)
- {
- printf("usage: mqreceive [-n] <name>\n");
- exit(-1);
- }
- //打开消息队列
- mqd_t mqd;
- if ((mqd = mq_open(argv[optind], flag)) == -1)
- ERR_EXIT("mq_open");
- //获取消息队列中每个消息最大大小
- struct mq_attr attr;
- if ((mq_getattr(mqd, &attr)) == -1)
- ERR_EXIT("mq_getattr");
- long len = attr.mq_msgsize;
- //从消息队列中取出消息
- unsigned int prio;//无符号整型
- ssize_t n;
- char *buff = (char *)malloc(attr.mq_msgsize * sizeof(char));
- if ((n = mq_receive(mqd, buff, len, &prio)) == -1)
- ERR_EXIT("mq_getattr");
- printf("read %d bytes, priority = %d\n", n, prio);
- exit(0);
- }
#include <stdio.h> #include <mqueue.h> #include <errno.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #define ERR_EXIT(m) { printf("%s() error %d: %s\n", m, errno, strerror(errno)); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { int c; int flag = O_RDONLY; //设置非阻塞方式 while ((c = getopt(argc, argv, "n")) != -1) { switch(c) { case ‘n‘: flag |= O_NONBLOCK; break; } } if (optind != argc -1) { printf("usage: mqreceive [-n] <name>\n"); exit(-1); } //打开消息队列 mqd_t mqd; if ((mqd = mq_open(argv[optind], flag)) == -1) ERR_EXIT("mq_open"); //获取消息队列中每个消息最大大小 struct mq_attr attr; if ((mq_getattr(mqd, &attr)) == -1) ERR_EXIT("mq_getattr"); long len = attr.mq_msgsize; //从消息队列中取出消息 unsigned int prio;//无符号整型 ssize_t n; char *buff = (char *)malloc(attr.mq_msgsize * sizeof(char)); if ((n = mq_receive(mqd, buff, len, &prio)) == -1) ERR_EXIT("mq_getattr"); printf("read %d bytes, priority = %d\n", n, prio); exit(0); }
分析:
1.要取出消息,则先要为消息分配内存。
2.open时,若未设置O_NONBLOCK选项,则一直阻塞,直到消息队列中有消息为止;设置了O_NONBLOCK选项,若消息队列中没有消息,则立刻出错返回。
结果:
5.mq_notify
Posix消息队列允许异步事件通知,以告知何时有一个消息放置到了某个空消息队列中。这种通知有两种方式可供选择:
(1)产生一个信号
(2)创建一个线程来执行指定的函数
//这种通知通过调用mq_notify建立
int mq_notify(mqd_t mqd, const struct sigevent *notification);
返回值:成功,0;出错-1
1.notification非空,该进程被注册为接收该消息队列的通知
2.notification空,且当前队列已被注册,则已存在的注册被撤销
3.对一个消息队列来说,任一时刻只有一个进程可以被注册
4.在mq_receive调用中的阻塞比任何通知的注册都优先收到消息
5.当通知被发送给注册进程时,注册即被撤销,该进程若要重新注册,则必须重新调用mq_notify()
#include <signal.h>
struct sigevent
{
int sigev_notify;
int sigev_signo;
union sigval sigev_value;
void (*sigev_notify_function)(union sigval);
pthread_attr_t *sigev_notify_attributes;
};
sigev_notify取值:
SIGEV_NONE:事件发生时,什么也不做;SIGEV_SIGNAL:事件发生时,将sigev_signo指定的信号发送给指定的进程;SIGEV_THREAD:事件发生时,内核会(在此进程内)以sigev_notify_attributes为线程属性创建一个线程,并让其执行sigev_notify_function,并以sigev_value为其参数
sigev_signo:在sigev_notify=SIGEV_SIGNAL时使用,指定信号类别
sigev_value:sigev_notify=SIGEV_SIGEV_THREAD时使用,作为sigev_notify_function的参数
union sigval
{
int sival_int;
void *sival_ptr;
};
sigev_notify_function:在sigev_notify=SIGEV_THREAD时使用,其他情况下置NULL
sigev_notify_attributes:在sigev_notify=SIGEV_THREAD时使用,指定创建线程的属性,其他情况下置NULL
例7:注册一个进程,当消息队列中有消息到来时,该进程被通知,并在其信号处理程序中取出消息。
程序:
- #include <stdio.h>
- #include <mqueue.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <signal.h>
- #define ERR_EXIT(m)\
- {\
- printf("%s() error %d: %s\n", m, errno, strerror(errno));\
- exit(EXIT_FAILURE);\
- }
- mqd_t mqd;
- void *buff;
- struct mq_attr attr;
- struct sigevent event;
- static void sig_usr1(int signo);
- int main(int argc, char *argv[])
- {
- if (signal(SIGUSR1, sig_usr1) == SIG_ERR)
- ERR_EXIT("signal");
- if ((mqd = mq_open(argv[1], O_RDONLY)) == -1)
- ERR_EXIT("mq_open");
- if (mq_getattr(mqd, &attr) == -1)
- ERR_EXIT("mq_getattr");
- buff = malloc(attr.mq_msgsize);
- //注册进程接收队列的通知
- event.sigev_notify = SIGEV_SIGNAL;
- event.sigev_signo = SIGUSR1;
- if (mq_notify(mqd, &event) == -1)//注册进程,消息队列中有消息来时,触发事件。
- ERR_EXIT("mq_notify");
- //一直执行,等待信号到来
- for (;;)
- pause();
- exit(0);
- }
- static void sig_usr1(int signo)
- {
- //重新注册
- if (mq_notify(mqd, &event) == -1)
- ERR_EXIT("mq_notify");
- //取出消息
- ssize_t n;
- unsigned int prio;
- if ((n = mq_receive(mqd, buff, attr.mq_msgsize, &prio)) == -1)
- ERR_EXIT("mq_receive");
- printf("SIGUSR1 received, read %d bytes, priority = %d\n", n, prio);
- }
#include <stdio.h> #include <mqueue.h> #include <errno.h> #include <stdlib.h> #include <signal.h> #define ERR_EXIT(m) { printf("%s() error %d: %s\n", m, errno, strerror(errno)); exit(EXIT_FAILURE); } mqd_t mqd; void *buff; struct mq_attr attr; struct sigevent event; static void sig_usr1(int signo); int main(int argc, char *argv[]) { if (signal(SIGUSR1, sig_usr1) == SIG_ERR) ERR_EXIT("signal"); if ((mqd = mq_open(argv[1], O_RDONLY)) == -1) ERR_EXIT("mq_open"); if (mq_getattr(mqd, &attr) == -1) ERR_EXIT("mq_getattr"); buff = malloc(attr.mq_msgsize); //注册进程接收队列的通知 event.sigev_notify = SIGEV_SIGNAL; event.sigev_signo = SIGUSR1; if (mq_notify(mqd, &event) == -1)//注册进程,消息队列中有消息来时,触发事件。 ERR_EXIT("mq_notify"); //一直执行,等待信号到来 for (;;) pause(); exit(0); } static void sig_usr1(int signo) { //重新注册 if (mq_notify(mqd, &event) == -1) ERR_EXIT("mq_notify"); //取出消息 ssize_t n; unsigned int prio; if ((n = mq_receive(mqd, buff, attr.mq_msgsize, &prio)) == -1) ERR_EXIT("mq_receive"); printf("SIGUSR1 received, read %d bytes, priority = %d\n", n, prio); }
分析:
因为通知被发送给注册进程时,其注册即被撤销,所以在信号处理程序中重新注册。 结果:
例7中的程序有个问题:
在信号处理程序中调用的函数都不是异步信号安全函数,即不可重入的,具体参考apue 10.6节。http://blog.csdn.net/zx714311728/article/details/53056927#t4
前面说过,通知有两种方式,一种是产生一个信号,另一种是创建一个线程执行指定函数。例7是产生一个信号,下面例8是创建一个线程。
例8:空消息队列有消息到来时,通知注册进程,注册进程创建一个线程执行指定函数操作。
程序:
- #include <stdio.h>
- #include <mqueue.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <unistd.h>
- #define ERR_EXIT(m)\
- {\
- printf("%s() error %d: %s\n", m, errno, strerror(errno));\
- exit(EXIT_FAILURE);\
- }
- mqd_t mqd;
- struct mq_attr attr;
- struct sigevent event;
- static void thread_func(union sigval);
- int main(int argc, char *argv[])
- {
- if (argc != 2)
- {
- printf("argument error\n");
- exit(-1);
- }
- //打开消息队列
- if ((mqd = mq_open(argv[1], O_RDONLY | O_NONBLOCK)) == -1)
- ERR_EXIT("mq_open");
- if (mq_getattr(mqd, &attr) == -1)
- ERR_EXIT("mq_getattr");
- //注册进程
- event.sigev_notify = SIGEV_THREAD;
- event.sigev_value.sival_ptr = NULL;
- event.sigev_notify_attributes = NULL;
- event.sigev_notify_function = thread_func;
- if (mq_notify(mqd, &event) == -1)
- ERR_EXIT("mq_notify");
- for (;;)
- pause();
- exit(0);
- }
- void thread_func(union sigval value)
- {
- ssize_t n;
- void *buff;
- long len;
- unsigned int prio;
- buff = malloc(attr.mq_msgsize);
- len = attr.mq_msgsize;
- if (mq_notify(mqd, &event) == -1)//重新注册进程
- ERR_EXIT("mq_notify");
- if ((n = mq_receive(mqd, buff, len, &prio)))
- printf("read %d bytes, priority = %d\n", n, prio);
- free(buff);
- pthread_exit((void *)0);
- }
#include <stdio.h> #include <mqueue.h> #include <errno.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #define ERR_EXIT(m) { printf("%s() error %d: %s\n", m, errno, strerror(errno)); exit(EXIT_FAILURE); } mqd_t mqd; struct mq_attr attr; struct sigevent event; static void thread_func(union sigval); int main(int argc, char *argv[]) { if (argc != 2) { printf("argument error\n"); exit(-1); } //打开消息队列 if ((mqd = mq_open(argv[1], O_RDONLY | O_NONBLOCK)) == -1) ERR_EXIT("mq_open"); if (mq_getattr(mqd, &attr) == -1) ERR_EXIT("mq_getattr"); //注册进程 event.sigev_notify = SIGEV_THREAD; event.sigev_value.sival_ptr = NULL; event.sigev_notify_attributes = NULL; event.sigev_notify_function = thread_func; if (mq_notify(mqd, &event) == -1) ERR_EXIT("mq_notify"); for (;;) pause(); exit(0); } void thread_func(union sigval value) { ssize_t n; void *buff; long len; unsigned int prio; buff = malloc(attr.mq_msgsize); len = attr.mq_msgsize; if (mq_notify(mqd, &event) == -1)//重新注册进程 ERR_EXIT("mq_notify"); if ((n = mq_receive(mqd, buff, len, &prio))) printf("read %d bytes, priority = %d\n", n, prio); free(buff); pthread_exit((void *)0); }
结果:已测试通过,和例7一样,懒得放图了。
6.sigwait
在多线程的环境中应使用sigwait、sigwaitinfo和sigtimedwait来处理所有信号,而绝不要用异步信号处理程序。多线程环境中也不应使用sigprocmask,而应使用pthread_sigmask。具体见:http://blog.csdn.net/yusiguyuan/article/details/14230719
#include <signal.h>
int sigwait(const sigset_t *set, int *sig);
返回值:成功,0;出错,正的Exxx值
调用sigwait前阻塞set信号集,sigwait然后一直阻塞到这些信号中有一个或多个待处理,这时它返回其中一个信号存放到sig。此过程为“同步的等待一个异步事件”:我们使用了信号,但没有涉及异步信号处理程序。
例9:修改例7的问题,不使用异步信号处理程序,而是使用sigwait。
程序:
- #include <stdio.h>
- #include <mqueue.h>
- #include <signal.h>
- #include <errno.h>
- #include <stdlib.h>
- #define ERR_EXIT(m)\
- {\
- printf("%s() error %d: %s\n", m, errno, strerror(errno));\
- exit(EXIT_FAILURE);\
- }
- int main(int argc, char *argv[])
- {
- if (argc != 2)
- {
- printf("argument error\n");
- exit(-1);
- }
- mqd_t mqd;
- struct sigevent event;
- sigset_t newmask, oldmask, zeromask;
- struct mq_attr attr;
- void *buff;
- long len;
- int signo;
- ssize_t n;
- unsigned int prio;
- //打开消息队列,获取消息队列属性
- if ((mqd = mq_open(argv[1], O_RDONLY | O_NONBLOCK)) == -1)
- ERR_EXIT("mq_open");
- if (mq_getattr(mqd, &attr) == -1)
- ERR_EXIT("mq_getattr");
- buff = malloc(attr.mq_msgsize);
- len = attr.mq_msgsize;
- //设置阻塞信号集,阻塞信号
- sigemptyset(&newmask);
- sigaddset(&newmask, SIGUSR1);
- sigprocmask(SIG_BLOCK, &newmask, NULL);
- //注册进程
- event.sigev_notify = SIGEV_SIGNAL;
- event.sigev_signo = SIGUSR1;
- if (mq_notify(mqd, &event) == -1)
- ERR_EXIT("mq_notify");
- //使注册进程永久执行,当空消息队列中有消息到来时,进行处理
- for (;;)
- {
- sigwait(&newmask, &signo);//阻塞,直到有个信号到来
- if (signo == SIGUSR1)
- {
- if (mq_notify(mqd, &event) == -1)//重新注册
- ERR_EXIT("mq_notify");
- if ((n = mq_receive(mqd, buff, len, &prio)) == -1)//获取消息队列中消息
- {
- ERR_EXIT("mq_receive");
- }
- else
- printf("SIGUSR1 received, read %dbytes, priority = %d\n", n, prio);
- }
- }
- exit(0);
- }
#include <stdio.h> #include <mqueue.h> #include <signal.h> #include <errno.h> #include <stdlib.h> #define ERR_EXIT(m) { printf("%s() error %d: %s\n", m, errno, strerror(errno)); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { if (argc != 2) { printf("argument error\n"); exit(-1); } mqd_t mqd; struct sigevent event; sigset_t newmask, oldmask, zeromask; struct mq_attr attr; void *buff; long len; int signo; ssize_t n; unsigned int prio; //打开消息队列,获取消息队列属性 if ((mqd = mq_open(argv[1], O_RDONLY | O_NONBLOCK)) == -1) ERR_EXIT("mq_open"); if (mq_getattr(mqd, &attr) == -1) ERR_EXIT("mq_getattr"); buff = malloc(attr.mq_msgsize); len = attr.mq_msgsize; //设置阻塞信号集,阻塞信号 sigemptyset(&newmask); sigaddset(&newmask, SIGUSR1); sigprocmask(SIG_BLOCK, &newmask, NULL); //注册进程 event.sigev_notify = SIGEV_SIGNAL; event.sigev_signo = SIGUSR1; if (mq_notify(mqd, &event) == -1) ERR_EXIT("mq_notify"); //使注册进程永久执行,当空消息队列中有消息到来时,进行处理 for (;;) { sigwait(&newmask, &signo);//阻塞,直到有个信号到来 if (signo == SIGUSR1) { if (mq_notify(mqd, &event) == -1)//重新注册 ERR_EXIT("mq_notify"); if ((n = mq_receive(mqd, buff, len, &prio)) == -1)//获取消息队列中消息 { ERR_EXIT("mq_receive"); } else printf("SIGUSR1 received, read %dbytes, priority = %d\n", n, prio); } } exit(0); }
分析:
此程序没有使用异步信号处理程序,而是使用了sigwait以接收信号 结果:
7.Posix实时信号
信号可划分为两个大组:
(1)值在SIGRTMIN和SIGRTMAX之间(包括两者在内)的实时信号
(2)所有其他信号:SIGALRM、SIGINT、SIGKILL等
实时信号中“实时”的意思是:
(1)信号是排队的,是按先进先出(FIFO)顺序排队的。同一信号产生3次,则递交3次
(2)当有多个SIGRTMIN到SIGRTMAX范围内的解阻塞信号排队时,值较小的信号先与较大的信号递交
(3)实时信号能比非实时信号携带更多信息
此部分因涉及到其他部分内容,只粗略看了下。
以上是关于Posix消息队列的主要内容,如果未能解决你的问题,请参考以下文章