消息队列可以认为是一个消息链表,每个消息都是一个记录,它由发送者赋予一个优先级。某个进程往一个消息队列中写入消息之前,不需要另外某个进程在该队列上等待消息的达到,这一点与管道和FIFO相反,与管道和 FIFO的另一区别是消息队列具有随内核的持续性。
POSIX消息队列特性:
1.对posix消息队列的读总是返回最高优先级的消息。
2.当往一个空队列放置一个消息时,posix消息队列允许产生一个信号或启动一个线程。
POSIX消息队列函数
1.mqd_t mq_open(...)
2.int mq_close(...)
3.int mq_unlink(...)
4.int mq_getattr(...)
5.int mq_setattr(...)
6.int mq_send(...)
7.int mq_receive(...)
8.int mq_notify(...)
1.使用非阻塞mq_receive的信号通知
#include "unpipc.h"
#include <mqueue.h>
#include <error.h>
volatile sig_atomic_t mqflag;
static void sig_usr1(int);
int
main(int argc, char **argv)
{
mqd_t mqd;
void *buff;
ssize_t n;
sigset_t zeromask, newmask, oldmask;
struct mq_attr attr;
struct sigevent sigev;
int rc;
if( argc != 2 )
err_quit("usage:mqnotifysig2 <name>");
mqd = Mq_open(argv[1], O_RDONLY | O_NONBLOCK);//以非阻塞形式打开mq
rc = Mq_getattr(mqd, &attr);
Sigemptyset(&zeromask);
Sigemptyset(&newmask);
Sigemptyset(&oldmask);
Sigaddset(&newmask, SIGUSR1);
Signal(SIGUSR1, sig_usr1);
sigev.sigev_notify = SIGEV_SIGNAL;
sigev.sigev_signo = SIGUSR1;
Mq_notify(mqd, &sigev);
buff = Malloc(attr.mq_msgsize);
for(; ;)
{
Sigprocmask(SIG_BLOCK, &newmask, &oldmask);
/*若不设置阻塞SIGUSR1,则可能在测试mqflag==0时,在调用sigsuspend之前,
SIGUSR1信号可能被捕获,并从信号sig_usr1返回,则最终导致sigsuspend将进程
永远挂起
*/
while( 0 == mqflag )
sigsuspend(&zeromask);
mqflag = 0;
Mq_notify(mqd, &sigev);
//循环从mqd中读出所有消息,mqd被设置为非阻塞,若无无消息可读
//则mq_receive立即返回,并设置errno=EAGAIN
while( (n = mq_receive(mqd, buff, attr.mq_msgsize, NULL)) >= 0){
printf("read %ld bytes\n", (long)n );
}
if( errno != EAGAIN ) //若errno==EAGAIN,则表明暂无消息可读
err_sys("mq_receive error\n");
Sigprocmask(SIG_UNBLOCK, &newmask, NULL);
}
Free(buff);
exit(0);
}
static void sig_usr1(int signo)
{
mqflag = 1;
return;
}
2.使用sigwait代替信号处理程序的信号通知
#include "unpipc.h"
#include <mqueue.h>
#include <error.h>
int
main(int argc, char **argv)
{
int signo;
mqd_t mqd;
void *buff;
ssize_t n;
sigset_t newmask;
struct mq_attr attr;
struct sigevent sigev;
if( argc != 2 )
err_quit("usage:mqnotifysig4 <name>");
mqd = Mq_open(argv[1], O_RDONLY | O_NONBLOCK);//以非阻塞形式打开mq
Mq_getattr(mqd, &attr);
Sigemptyset(&newmask);
Sigaddset(&newmask, SIGUSR1);
Sigprocmask(SIG_BLOCK, &newmask, NULL);
sigev.sigev_notify = SIGEV_SIGNAL;
sigev.sigev_signo = SIGUSR1;
Mq_notify(mqd, &sigev);
buff = Malloc(attr.mq_msgsize);
for(; ;)
{
Sigwait(&newmask, &signo);
if(signo == SIGUSR1 ){
Mq_notify(mqd, &sigev);
while( (n = mq_receive(mqd, buff, attr.mq_msgsize, NULL)) >= 0)
{
printf("read %ld bytes\n", (long)n );
}
if( errno != EAGAIN ) //若errno==EAGAIN,则表明暂无消息可读
err_sys("mq_receive error\n");
}
}
Free(buff);
exit(0);
}
3.使用select的posix消息队列
#include "unpipc.h"
#include <mqueue.h>
#include <error.h>
int pipefd[2];//用管道实现统一事件源
static void sig_usr1(int);
int
main(int argc, char **argv)
{
int nfds;
char c;
fd_set rset;
mqd_t mqd;
void *buff;
ssize_t n;
struct mq_attr attr;
struct sigevent sigev;
if(2 != argc )
err_quit("usage:mqnotifysig5 <name>");
mqd = Mq_open(argv[1], O_RDONLY | O_NONBLOCK );//以非阻塞形式打开mq
Mq_getattr(mqd, &attr);
buff = Malloc(attr.mq_msgsize);
Pipe(pipefd);
Signal(SIGUSR1, sig_usr1);
sigev.sigev_notify = SIGEV_SIGNAL;
sigev.sigev_signo = SIGUSR1;
Mq_notify(mqd, &sigev);
FD_ZERO(&rset);
for(; ;)
{
FD_SET(pipefd[0], &rset);
nfds = Select(pipefd[0]+1, &rset, NULL, NULL, NULL);//select监听pipefd[0]描述符
/*当往空Mq中放入消息时,产生SIGUSR1信号
* 当SIGUSR1信号被捕获时,信号处理函数往pipefd[1]中写,从而可以监听到pipefd[0]可读
*/
if(FD_ISSET(pipefd[0], &rset) )
{
Read(pipefd[0], &c, 1);
Mq_notify(mqd, &sigev); //重新注册notify
while((n = mq_receive(mqd, buff, attr.mq_msgsize, NULL)) >= 0)
{
printf("read %ld bytes\n", (long)n );
}
if( errno != EAGAIN )
err_sys("mq_receive error\n");
}
}
Free( buff );
exit(0);
}
static void sig_usr1(int signo)//SIGUSER1信号处理函数
{
Write(pipefd[1], "", 1);//往pipefd[1]中写一个空字符
return;
}
4.异步通知之设置sigev_notify为SIGEV_THREAD
#include "unpipc.h"
#include <mqueue.h>
#include <error.h>
mqd_t mqd;
struct mq_attr attr;
struct sigevent sigev;
static void notify_thread(union sigval);
int
main(int argc, char **argv)
{
if( argc != 2 )
err_quit("usage:notifythread1 <name>");
mqd = Mq_open(argv[1], O_RDONLY | O_NONBLOCK);
Mq_getattr(mqd, &attr);
sigev.sigev_notify = SIGEV_THREAD;//当空mq中存入消息时,启动一个线程(notify_thread)
sigev.sigev_value.sival_ptr = NULL;//指定线程无参
sigev.sigev_notify_function = notify_thread;//指定线程函数入口
sigev.sigev_notify_attributes = NULL;//使用默认线程属性
Mq_notify(mqd, &sigev);
for(; ; )
pause();
exit(0);
}
static void notify_thread(union sigval arg)
{
ssize_t n;
void *buff;
printf("notify_thread started\n");
buff = Malloc(attr.mq_msgsize);
Mq_notify(mqd, &sigev);
while( (n = mq_receive(mqd, buff, attr.mq_msgsize, NULL)) >= 0)
printf("read %ld bytes\n", (long)n );
if( errno != EAGAIN )
err_sys("mq_receive error!\n");
free( buff );
pthread_exit(NULL);
}