libevent源码浅析(三):libevent的信号的处理惩罚
副标题#e#
在libevent中通过利用socketpair成立一对流管道,也就是全双工管道,来将信号事件与句柄事件统一起来。
先来看数据布局:
struct evsignal_info {
struct event ev_signal; ///<所属的event
int ev_signal_pair[2]; ///<建设的流管道
int ev_signal_added; ///<信号是否已被插手到event中的标志。
volatile sig_atomic_t evsignal_caught; ///<事件触发标志,1暗示有信号被触发
struct event_list evsigevents[NSIG]; ///<多个事件有大概注册到同一个信号,因此这里每个信号的事件都是一个event_list.
sig_atomic_t evsigcaught[NSIG]; ///<由于一个信号大概被注册多次,这里生存信号被捕获的次数
#ifdef HAVE_SIGACTION
struct sigaction **sh_old;
#else
ev_sighandler_t **sh_old;
#endif
int sh_old_max;
};
接下来可以看几个主要的函数:
evsignal_init函数主要用来初始化一些数据布局。
void
evsignal_init(struct event_base *base)
{
int i;
///建设一对流管道
if (evutil_socketpair(
AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1)
event_err(1, "%s: socketpair", __func__);
///配置fd
FD_CLOSEONEXEC(base->sig.ev_signal_pair[0]);
FD_CLOSEONEXEC(base->sig.ev_signal_pair[1]);
///初始化sig数据布局
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 */
///在libevent内里,所有的事件行列都用tail queue实现,linux下它利用的是linux自带的taile queue,详细用法可以去看man手册。
for (i = 0; i < NSIG; ++i)
TAILQ_INIT(&base->sig.evsigevents[i]);
///配置非阻塞
evutil_make_socket_nonblocking(base->sig.ev_signal_pair[0]);
///初始化event布局
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;
}
#p#副标题#e#
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);
///假如此信号的事件行列为空则说明此信号第一次被注册。因此配置信号处理惩罚函数,这里所有的信号都注册到沟通的处理惩罚函数evsignal_handler,接下来我们会来阐明这个函数。
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);
}
evsignal_handler就是所有信号的处理惩罚函数。
static void
evsignal_handler(int sig)
{
int save_errno = errno;
if (evsignal_base == NULL) {
event_warn(
"%s: received signal %d, but have no base configured",
__func__, sig);
return;
}
///进入此函数,说明信号已经光降,因此这里配置捕获次数,以及此信号已经被捕获的标志。
evsignal_base->sig.evsigcaught[sig]++;
evsignal_base->sig.evsignal_caught = 1;
#ifndef HAVE_SIGACTION
signal(sig, evsignal_handler);
#endif
///流管道开始发送数据,说明信号已经光降。此时另一端就会检测到事件从而挪用我们初始化注册的回调函数。
/* Wake up our notification mechanism */
send(evsignal_base->sig.ev_signal_pair[0], "a", 1, 0);
errno = save_errno;
}
evsignal_process主要是用来将相应的信号事件插手到激活列表中,以便于挪用相应的回调函数。
#p#分页标题#e#
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;
///轮回遍历,获得已产生的信号事件。
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);
}
sig->evsigcaught[i] = 0;
}
}