在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; }
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主要是用来将相应的信号事件加入到激活列表中,以便于调用相应的回调函数。
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; } }