信号是一种异步事件:信号处理函数和程序的主循环是两条不同的执行路线。很明显,信号处理函数需要尽可能快地执行完毕,以确保该信号不被屏蔽太久(信号在处理期间,系统不会再次触发它)。
上面这幅图是网上找到的,很好地从整体上表述了原理,摘抄到最前面
下图是本人自己总结的信号事件代码级原理图
下面是展开分析代码实现
一、初始化信号事件
调用base->evbase = base->evsel->init(base); 实际就是调用epoll_init, base->evbase最终指向了epollop结构,
在epoll_init的最后,调用了evsig_init.
/*设置观察ev_signal_pair[1]的event事件ev_signal, 事件类型为EV_READ|EV_PERSIST,
* 这个event将加入到evmap_io数组中
并且设置信号的统一处理函数evsig_cb/
/* Data structure for the default signal-handling implementation in signal.c
*/
struct evsig_info {
/* Event watching ev_signal_pair[1] */
struct event ev_signal;/*观察ev_signal_pair[1]的event事件*/
/* Socketpair used to send notifications from the signal handler */
evutil_socket_t ev_signal_pair[2];
/* True iff we've added the ev_signal event yet. */
int ev_signal_added;
/* Count of the number of signals we're currently watching. */
int ev_n_signals_added;
/* Array of previous signal handler objects before Libevent started
* messing with them. Used to restore old signal handlers. */
#ifdef _EVENT_HAVE_SIGACTION
struct sigaction **sh_old;
#else
ev_sighandler_t **sh_old;
#endif
/* Size of sh_old. */
int sh_old_max;
};
/*此函数主要用于设置event base结构里面的struct evsig_info sig*/
*struct evsig_info主要用于实现通用的信号处理
*创建unix socket一端fd对应的event,并将其保存在base->sig.ev_signal事件
*中,在第一次调用event_add添加信号的时候,将ev_signal事件加入I/O事件处理 */
int
evsig_init(struct event_base *base)
{
/*
* 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.
*/
/*创建一对unix套接字,用于传递信号值*/
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_sock_warn(-1, "%s: socketpair", __func__);
#else
event_sock_err(1, -1, "%s: socketpair", __func__);
#endif
return -1;
}
/*设置unix套接字的exec执行时关闭*/
evutil_make_socket_closeonexec(base->sig.ev_signal_pair[0]);
evutil_make_socket_closeonexec(base->sig.ev_signal_pair[1]);
base->sig.sh_old = NULL;
base->sig.sh_old_max = 0;
/*设置unix套接字为非阻塞套接字*/
evutil_make_socket_nonblocking(base->sig.ev_signal_pair[0]);
evutil_make_socket_nonblocking(base->sig.ev_signal_pair[1]);
/*设置观察ev_signal_pair[1]的event事件ev_signal, 事件类型为EV_READ|EV_PERSIST,
* 这个event将加入到evmap_io数组中
*并且设置信号的统一处理函数evsig_cb*/
event_assign(&base->sig.ev_signal, base, base->sig.ev_signal_pair[1],
EV_READ | EV_PERSIST, evsig_cb, base);
base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL;
/*保证信号的处理进入第一优先级队列*/
event_priority_set(&base->sig.ev_signal, 0);
/*设置evsigsel*/
base->evsigsel = &evsigops;
return 0;
}
二、创建信号事件
#define evsignal_new(b, x, cb, arg)
event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg))
主要的区别在于此处的x(fd)为signal值, events类型为EV_SIGNAL | EV_PERSIST.
/*
* 该函数完成了 1、event内存的分配; 2、设置可所属的event_base;
* 3