libevent 源码学习八 —— 集成信号处理
前言 : 这一节将分析如何将 Signal 集成到事件主循环的框架中
1 集成策略——使用 socket pair
a 开始
b 创建监听 socket
c 绑定本地回环地址, 开始监听本地连接
d 创建一个连接 socket——sock1
e 调用 connect()连接到监听 socket 监听的窗口
f 调用 accept()取得连接成功后返回的 socket —— sock2
g 将 sock1 作为写 socket:sock2 作为读socket : 返回
h 结束
2 集成到事件主循环——通知 event_base
Libevent 会在事件主循环中检查标记,来确定是否有触发的 signal,如果标记被设置就处理这些 signal。
处理框架
1) 初始化阶段:
创建 socket pair
为 socket pair 的读socket 在 libevent 的 event_base 实例上注册一个 persist 的读事件。
2) 信号触发阶段
IN Thread A
将信号发生标记置为1,记录信号 signo;
向写 socket 写入数据,触发读 socket 的读事件,从而通知 event_base 有信号发生
IN libevent thread
I/O 事件发生, event_base 的 I/O 机制马上返回;
检查标记发现 signal 事件发生,遍历链表,将 signo 的注册事件添加到激活链表中
3) 注册信号
注册信号 signo 事件,将对应的 event 添加到信号 signo 的 event 链表中
3 evsignal_info 结构体
Libevent 中 Signal 事件的管理时通过结构体 evsignal_info 完成的,结构体位于 evsignal.h 文件中。
struct evsignal_info(){
struct event ev_signal;
int ev_signal_pair[2];
int ev_signal_added;
volatile sig_atomic_t evsignal_caught;
struct event_list evsigevents[NSIG];
sig_atomic_t evsigcaught[NSIG];
#ifdef HAVE_SIGNAL
struct sigaction **sh_old;
#else
ev_sighandler_t **sh_old;
#endif
int sh_old_max;
};
ev_signal 为 socket pair 的读 socket 向 event_base 注册读事件时使用的 event 结构体
ev_signal_pair : socket pair 对
ev_signal_added : 记录ev_signal 事件是否已经注册了
evsignal_caught : 是否有信号发生标记:是 volatile 类型,直接从内存读取
evsigevents[NSIG] : 数组,evsigevents[signo] 表示注册到信号 signo 的事件链表
evsigcaught[NSIG] : 具体记录每个信号触发的次数
sh_old 记录了原来的 signal 处理函数指针, 当信号 signo 注册的 event 被清空时,需要重新设置器处理函数
evsignal_info 的初始化包括, 创建 socket pair, 设置 ev_signal 事件,将所有标记置0, 初始化信号的注册事件链表指针。
4 注册、注销 signal 事件
注册 signal 事件是通过 evsignal_add(struct event* ev)函数完成的, Libevent 对所有的信号注册使用同一个处理函数 evsignal_handler(),注册过程如下
取得 ev 要注册到的信号 signo
如果信号 signo 未被注册,那么就为 signo 注册信号处理函数 evsignal_handler()
如果事件 ev_signal 还没有被注册,就注册 ev_signal 事件
将事件 ev 添加到 signo 的 event 链表中
从 signo 上注销一个已注册的 signal 事件: 直接从其已注册事件的链表中移除。如果事件链表已空,那么就恢复旧的处理函数。
处理函数 evsignal_handler() 函数就是记录信号的发生次数,并通知 event_base 有信号触发, 需要处理。
static void evsignal_handler(int sig){
int save_errno = errno;
if(evsignal_base == NULL)
{
event_warn("%s : reveived signal %d, but have no base configured", __func__, sig);
return;
}
// 记录信号 sig 的触发次数,并设置 event 触发标记
evsignal_base -> sig.evsigcaught[sig]++;
evsignal_base -> sig.evsignal_caught = 1;
#ifndef HAVE_SIGACTION
signal(sig, evsignal_handler); // 重新注册信号
#endif
// 向写 socket 写一个字节数据,触发 event_base 的 I/O 事件,从而通知有信号触发,需要处理
send(evsignal_base -> sig.ev_signal_pair[0], "a", 1, 0);
errno = save_errno // 错误码
}