sigevent简介
首先给出官方文档地址:http://man7.org/linux/man-pages/man7/sigevent.7.html
首先,要明确sigevent
的作用。sigevent
只是一个结构体,而结构体本质上是用于存储数据信息的。由此,我们认为,sigevent
的作用是为Linux/Unix系统调用函数API提供一个调用接口的通用参数;这类API的作用以某种特定的方式来通知进程到达的事件。API根据sigevent
提供的结构,来具体处理有关信息。
下面给出sigevent
的具体结构:
#include <signal.h>
union sigval { /* Data passed with notification */
int sival_int; /* Integer value */
void *sival_ptr; /* Pointer value */
};
struct sigevent {
int sigev_notify; /* Notification method */
int sigev_signo; /* Notification signal */
union sigval sigev_value; /* Data passed with notification */
void (*sigev_notify_function) (union sigval);/* Function used for thread notification (SIGEV_THREAD) */
void *sigev_notify_attributes;/* Attributes for notification thread (SIGEV_THREAD) */
pid_t sigev_notify_thread_id; /* ID of thread to signal (SIGEV_THREAD_ID) */
};
每个参数的作用已经在英文的注释中说明了,不在赘述。下面主要说一下sigev_notify
参数。该参数几个取值和作用如下:
SIGEV_NONE
:空的提醒,事件发生时不做任何事情SIGEV_SIGNAL
:向进程发送sigev_signo
中指定的信号,具体详细的状况参照上面的文档,这涉及到sigaction
的使用SIGEV_THREAD
:通知进程在一个新的线程中启动sigev_notify_function
函数,函数的实参是sigev_value
,系统API自动启动一个线程,我们不用显式启动。
综上可知,sigevent
结构体实际上是为各类系统调用API提供了一个统一的处理结构,我们只需要对提供sigevent
结构的API,提供一个具体赋值结构体参数即可。下面通过mq_notify
来实际说明该问题
mq_notify()简介
在这篇笔记中介绍了Posix消息队列的一般操作。
先给出该函数的接口
#include <mqueue.h>
int mq_notify(mqd_t mqdes, const struct sigevent *sevp);
mqdes
:消息队列的标识符sevp
:实际调用的参数,参数的意义参照sigevent
结构体,或者直接看官方文档.
说明函数的作用:如果有新消息到达一个空的队列,允许调用线程注册一个异步通知,该通知会在消息到达时触发。核心的思想在异步这两个字上,也就是说,我们希望从消息队列中获取消息,但是不想让当前进程阻塞在一个空的消息队列上,那么通过该函数可以注册异步通知,来处理将来到达的事件。
几个注意事项:
- 一个消息队列只能注册一个处理process
- 如果当前进程想要移除注册的事件,只需要把
sevp
设置为NULL
即可 - 如果已经存在一个注册的事件进程,那么其他注册进程不会收到信号
- 只有空消息队列在接收到第一个消息是,才会触发通知
代码实例
主进程创建一个消息队列,并注册一个事件,然后睡眠2秒模拟将来消息到达。之后主进程阻塞,模拟处理其他事件,在子进程中进行数据读取和程序退出。
#include <pthread.h>
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <signal.h>
#include <cstring>
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);
}