本章是libevent源码分析的最后一篇,主要分析一下libevent对信号处理的支持方式。
1).evsignal_init()函数分析:
int
evsignal_init(struct event_base *base)
{
int i;
/*
* Our signal handler is going to write to one end of the socket
* pair to wake up our event loop. The event loop then scans for
* signals that got delivered.
*/
if (evutil_socketpair(
AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1) {
#ifdef WIN32
/* Make this nonfatal on win32, where sometimes people
have localhost firewalled. */
event_warn("%s: socketpair", __func__);
#else
event_err(1, "%s: socketpair", __func__);
#endif
return -1;
}
FD_CLOSEONEXEC(base->sig.ev_signal_pair[0]);
FD_CLOSEONEXEC(base->sig.ev_signal_pair[1]);
base->sig.sh_old = NULL;
base->sig.sh_old_max = 0;
base->sig.evsignal_caught = 0;
memset(&base->sig.evsigcaught, 0, sizeof(sig_atomic_t)*NSIG);
/* initialize the queues for all events */
for (i = 0; i < NSIG; ++i)
TAILQ_INIT(&base->sig.evsigevents[i]);
evutil_make_socket_nonblocking(base->sig.ev_signal_pair[0]);
event_set(&base->sig.ev_signal, base->sig.ev_signal_pair[1],
EV_READ | EV_PERSIST, evsignal_cb, &base->sig.ev_signal);
base->sig.ev_signal.ev_base = base;
base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL;
return 0;
}
该函数主要初始化signal处理相关的事件,会在event_base_new()函数中调用各个系统平台的*_init()函数里面调用进行初始化。主要处理流程如下:
a).创建一对socket pair;
b).event base中signal的event会监听socketpair[1]的读事件,回调函数为evsignal_cb;
2).evsignal_process()函数:
void
evsignal_process(struct event_base *base)
{
struct evsignal_info *sig = &base->sig;
struct event *ev, *next_ev;
sig_atomic_t ncalls;
int i;
base->sig.evsignal_caught = 0;
for (i = 1; i < NSIG; ++i) {
ncalls = sig->evsigcaught[i];
if (ncalls == 0)
continue;
sig->evsigcaught[i] -= ncalls;
for (ev = TAILQ_FIRST(&sig->evsigevents[i]);
ev != NULL; ev = next_ev) {
next_ev = TAILQ_NEXT(ev, ev_signal_next);
if (!(ev->ev_events & EV_PERSIST))
event_del(ev);
event_active(ev, EV_SIGNAL, ncalls);
}
}
}
该函数主要在event_base_loop()事件处理主循环中会调用,为对所有信号产生的事件的处理,该函数的主要流程如下:
a).该函数中,会遍历二维数组base->sig.evsigevents中注册在信号上的事件,如果base->sig.evsigcaughts数组中的对应项不为0,则表明程序接收到相应的信号,则处理其上注册的所有的事件,即将这些事件添加到activequeue中去,从而在event_base_loop()函数中统一为IO事件来处理 ;
3).evsignal_add()函数分析:
int
evsignal_add(struct event *ev)
{
int evsignal;
struct event_base *base = ev->ev_base;
struct evsignal_info *sig = &ev->ev_base->sig;
if (ev->ev_events & (EV_READ|EV_WRITE))
event_errx(1, "%s: EV_SIGNAL incompatible use", __func__);
evsignal = EVENT_SIGNAL(ev);
assert(evsignal >= 0 && evsignal < NSIG);
if (TAILQ_EMPTY(&sig->evsigevents[evsignal])) {
event_debug(("%s: %p: changing signal handler", __func__, ev));
if (_evsignal_set_handler(
base, evsignal, evsignal_handler) == -1)
return (-1);
/* catch signals if they happen quickly */
evsignal_base = base;
if (!sig->ev_signal_added) {
if (event_add(&sig->ev_signal, NULL))
return (-1);
sig->ev_signal_added = 1;
}
}
/* multiple events may listen to the same signal */
TAILQ_INSERT_TAIL(&sig->evsigevents[evsignal], ev, ev_signal_next);
return (0);
}
这个函数是注册signal的event事件,这个函数会将注册的event插入到相应signal的sig->evsigevents中去;从而在上面的evsignal_process()函数中统一处理;
注意上面的_evsignal_set_handler()函数调用,该函数只在第一次注册相应信号的事件时才会调用;该函数主要修改系统的信号处理函数为evsignal_handler()函数,该函数会在程序收到信号时调用,主要流程是增加event base中的sig.evsigcaught[sig]的计数,同时标记evsignal_caught为1,表明程序收到了信号,最后写入一个字节到socket pair[0];
通过上述分析,我们基本能总结出来libevent对信号的处理流程:
1).添加信号的event,会修改信号的默认handler函数;
2).程序接收到signal,调用设置的handler函数,该函数会添加相应的event base中的signal的数组中对应信号的计数;同时会向socket pair[0]中写入一个字节;
3).在event loop中,socket pair[1]上会检测到读事件,从而epoll_wait()会返回,通过eventbase->sig.sigevent_caught是否置位来判断产生了信号,从而去调用在注册的signal事件上的回调函数;