前面我们对整个event
的功能做了比较全面的分析, 但是主要分析事件都是 IO或者定时事件, 却没有分析过信号又是怎么集成到就绪队列中的.
evsignal_info结构
evsignal_info
在evsignal.h
中
typedef void (*ev_sighandler_t)(int);
struct evsignal_info {
// 读事件使用的 event 结构
struct event ev_signal;
// ev_signal_pair[1] : 作为 read 放入事件到循环中监听, 当有信号时
// 通过写入 ev_signal_pair[0], 这样就可以唤醒并回调与 ev_signal_pair[1] 关联并执行.
// 这样的处理的好处就是不用关心中断的问题. 当同时有多个信号时这样的处理更加的高效迅速
int ev_signal_pair[2];
// 事件信号是否加入了 event_loop主循环中
int ev_signal_added;
// 表示捕捉到信号, 并且是 volatile 类型
volatile sig_atomic_t evsignal_caught;
// 信号队列. 这样设置成数组链表好处是可以响应相同信号
struct event_list evsigevents[NSIG];
// 每个信号捕捉到的次数
sig_atomic_t evsigcaught[NSIG];
// 保存之前的信号
#ifdef HAVE_SIGACTION
struct sigaction **sh_old;
#else
ev_sighandler_t **sh_old;
#endif
// 信号类型的最大值
int sh_old_max;
};
结构中主要三个成员参数
- evsignal_caught : 表示有捕捉到到来的信号.
volatile
的特点就是每次都会从内存中读取, 而不是从缓冲区中读, 保证读取的数据都是更新过的. - evsigevents[NSIG] : 数组链表, 每一个链表表示一个信号, 之后排成链表就是每个信号都可以是一个信号队列
- ev_signal_pair[2] : 两个文件描述符(socket pair). 后面我们会重点来分析.
信号在前面出现的地方
在分析event 注册和激活时, 其实evnt_add
函数都是通过evsel->add(evbase, ev);
将 IO, 定时或者信号都交给它来进行注册的并加入注册队列, 但是在主循环中我们并没有看到信号的是怎么加入就绪队列的, 在主循环我们只看到了信号默认交给了信号处理函数.
int event_add(struct event *ev, const struct timeval *tv)
{
// 该事件是处于读/写/信号并且没有处于就绪队列中
if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
// 添加处理函数. 信号也是在这里被添加的, 都是统一的添加接口
res = evsel->add(evbase, ev);
if (res != -1)
// 将设置的事件插入到注册链表中
event_queue_insert(base, ev, EVLIST_INSERTED);
}
}
int event_base_loop(struct event_base *base, int flags)
{
// 如果设置有信号事件, 就将 base 交给专门处理信号的结构处理
// 将信号单独作处理
if (base->sig.ev_signal_added)
evsignal_base = base;
done = 0;
while (!done) {
/* You cannot use this interface for multi-threaded apps */
// 这个我们不分析. 就是处理信号
while (event_gotsig) {
event_gotsig = 0;
if (event_sigcb) {
res = (*event_sigcb)();
if (res == -1) {
errno = EINTR;
return (-1);
}
}
}
}
}
而evsignal_base
和event_gotsig
还有event_sigcb
回调函数都是event.c
中定义的全局变量
extern struct event_base *evsignal_base;
/* Handle signals - This is a deprecated interface */
int (*event_sigcb)(void); /* Signal callback when gotsig is set */
volatile sig_atomic_t event_gotsig; /* Set in signal handler */
还有event_base
中也定义了信号成员哦
struct event_base {
/* signal handling info */
struct evsignal_info sig; // 管理信号的成员
};
总结
本节其实就只是看了一下evsignal_info
的结构并回忆了一下前面分析的时候出现过信号处理的地方, 希望下面分析你还能联想到前面对信号调用的地方.