System V消息队列的问题之一是无法通知一个进程何时在某个队列中放置了一个消息。采用轮询(poling),是对CPU时间的一种浪费。
Posix消息队列容许 异步事件通知,以告知何时有一个消息放置到某个空消息队列中。
该通知有两种方式:当一个消息被放置某个空队列时,要么产生一个信号来通知,要么通过创建一个线程来执行一个特定程序,来完成消息到来时的该做的事情。
这种通知通过调用mq_notify建立
#include <mqueue.h>
int mq_notify(mqd_t mqdes, const struct sigevent* notification);
该函数为指定队列建立或删除异步事件通知
(1).如果notification参数为非空,那么当前进程希望在有一个消息到达所指定的先前为空的对列时得到通知。
(2).如果notification参数为空,而且当前进程被注册为接收指定队列的通知,那么已存在的注册将被撤销。
struct sigevent的结构是:
union sigval { /*传递的参数*/
int sival_int; /* 信号机制传递的参数 */
void *sival_ptr; /* 若是线程机制传递的参数 */
};
struct sigevent {
int sigev_notify; /* 设置通知机制方法,线程为SIGEV_THREAD,信号为SIGEV_SIGNAL*/
int sigev_signo; /* 若是信号机制,该参数设置为触发的信号 */
union sigval sigev_value;/* 传递的参数*/
void (*sigev_notify_function)(union sigval); /* 若是线程机制,该参数为线程函数*/
void *sigev_notify_attributes; /* 线程函数的属性 */
};
需要注意的几点:
1. 只需注册一个线程函数。
2. 消息机制触发条件是:在消息队列为空的情况下有数据到来才会触发,当消息队列不为空时,有数据到来不触发。
3. 当消息机制触发后,需要重新注册,在我们的应用中,当线程函数触发后需要重新注册线程函数。在后面的代码中可以看到。
2. 简单示例
#include
#include
#include
#include
#include
#include
#include
#include
const char *mq_name = "/mq_test";
inline void handle_error(const char *msg) {
perror(msg);
exit(EXIT_FAILURE);
}
static void tfunc(union sigval sv) {
struct mq_attr attr;
ssize_t nr;
void *buf;
mqd_t mqdes = *((mqd_t *) sv.sival_ptr);
if (mq_getattr(mqdes, &attr) == -1) {
handle_error("mq_getattr() error\n");
}
buf = malloc(attr.mq_msgsize);
if (buf == nullptr) {
handle_error("malloc() error\n");
}
nr = mq_receive(mqdes, (char *) buf, attr.mq_msgsize, nullptr);
if (nr == -1) {
handle_error("mq_receive() error\n");
}
printf("Read %zd bytes from MQ\n", nr);
free(buf);
exit(EXIT_SUCCESS);
}
int main() {
mqd_t mqdes = mq_open(mq_name, O_CREAT | O_RDWR, 0777, nullptr);
if (mqdes < (mqd_t) 0) {
handle_error("mq_open() error\n");
}
struct sigevent sev;
bzero(&sev, sizeof(sev));
sev.sigev_notify = SIGEV_THREAD;
sev.sigev_notify_function = tfunc;
sev.sigev_notify_attributes = nullptr;
sev.sigev_value.sival_ptr = &mqdes; // 传递给tfunc的参数
if (mq_notify(mqdes, &sev) == -1) {
handle_error("mq_notify() error\n");
}
puts("sleep for 2 second...");
sleep(2);
const char *msg = "hello world!";
mq_send(mqdes, msg, strlen(msg), 1);
pause();
exit(EXIT_SUCCESS);
}