Libevent信号event的处理
event_signal_map中数组元素的值它要么是信号值sig,要么是文件描述符fd,
而event_signal_map要求的数组长度一定要大于slot。
那么之后给定一个sig或者fd,就可以直接通过下标操作快速定位了。
这是因为一个sig或者fd就对应在数组中占有一个位置,并且sig或者fd的值等于其在数组位置的下标值。
把信号也转换成IO事件,集成到Libevent中
(1)创建一个管道(Libevent实际上使用的是socketpair)
(2)为这个socketpair的一个读端创建一个event,并将之加入到多路IO复用函数的监听之中
(3)设置信号捕抓函数
(4)有信号发生,就往socketpair写入一个字节
//event-internal.h文件
struct event_base {
const struct eventop *evsigsel;
struct evsig_info sig;
...
struct event_signal_map sigmap;
...
};
//evsignal-internal.h文件
struct evsig_info {
//用于监听socketpair读端的event. ev_signal_pair[1]为读端
struct event ev_signal;
//socketpair
evutil_socket_t ev_signal_pair[2];
//用来标志是否已经将ev_signal这个event加入到event_base中了
int ev_signal_added;
//用户一共要监听多少个信号
int ev_n_signals_added;
//数组。用户可能已经设置过某个信号的信号捕抓函数。但
//Libevent还是要为这个信号设置另外一个信号捕抓函数,
//此时,就要保存用户之前设置的信号捕抓函数。当用户不要
//监听这个信号时,就能够恢复用户之前的捕抓函数。
//因为是有多个信号,所以得用一个数组保存。
#ifdef _EVENT_HAVE_SIGACTION
struct sigaction **sh_old;
#else//保存的是捕抓函数的函数指针,又因为是数组。所以是二级指针
ev_sighandler_t **sh_old;
#endif
/* Size of sh_old. */
int sh_old_max; //数组的长度
};
初始化:
Libevent在初始化时会选择一个多路I/O复用函数
base->evbase = base->evsel->init(base);
以poll的init函数为例
poll_init()->evsig_init();
evsig_init函数完成了创建socketpair,设置各种属性,并将socketpair的一个读端与ev_signal相关联。
event_assign(&base->sig.ev_signal, base, base->sig.ev_signal_pair[1],EV_READ | EV_PERSIST, evsig_cb, base);
将信号event加入到event_base
event_add()->event_add_internal()->evmap_signal_add(){evsel->add实质是evsig_add}
evsig_add()->_evsig_set_handler(){sigaction或signal}设置evsig_handler
假如要对一个绑定了某个信号的event调用event_add函数,那么在event_add的内部会调用event_add_internal函数。
而event_add_internal函数又会调用evmap_signal_add函数
在evmap_signal_add函数中调用(evsel->add(base, ev->ev_fd, 0, EV_SIGNAL, NULL),实质调用evsig_add函数
evsig_add函数设置信号捕抓函数_evsig_set_handler(base, (int)evsignal, evsig_handler),evsig_handler抓捕信号
若用户第一次监听一个信号。要ev_signal这个event添加到event_base中。
{
只需一个event即可完成工作,即使用户要监听多个不同的信号,因为这个event已经和socketpair的读端相关联了。
如果要监听多个信号,那么就在信号处理函数中往这个socketpair写入不同的值即可。
event_base能监听到可读,并可以从读到的内容可以判断是哪个信号发生了。
从代码中也可得知,Libevent并不会为每一个信号监听创建一个event。
它只会创建一个全局的专门用于监听信号的event。这个也是“统一事件源”的工作原理。
}
在_evsig_set_handler函数中使用sigaction和signal设置信号处理函数
信号处理函数evsig_handler,抓捕到信号后,调用函数evsig_handler,向socketpair写端写入一个与信号相关的字节
我们在event_assign中设置了回调函数evsig_cb,用来处理信号对socketpair的I/O事件
evsig_cb这个回调函数并不是用户为监听一个信号调用event_new时设置的用户回调函数,而是Libevent内部为了处理信号而设置的内部回调函数
回调函数的作用是读取socketpair的所有数据,并将数据当作信号,再根据信号值调用evmap_signal_active。
evmap_signal_active()->event_active_nolock()->event_queue_insert(,,EVLIST_ACTIVE);
最后从激活的队列中取出元素进行处理:event_process_active()->event_process_active_single_queue()