libevent中的信号管理

一、前言

Libevent(1.4.13 版本)的源码中,event_base 结构体中的成员 sig 用于管理信号事件,sig 的变量类型为 struct evsignal_info

二、相关结构体

struct evsignal_info 定义在 evsignal.h 头文件中,如下:

typedef void (*ev_sighandler_t)(int);

struct evsignal_info {
   
	struct event ev_signal; /* 为 socket pair 的读 socket 向 event_base 注册读事件时使用的 event 结构体 */
	int ev_signal_pair[2]; /* socket pair,当有信号发生时,其中一个 socket 向另一个 socket 写数据,触发可读事件 */
	int ev_signal_added; /* 记录 ev_signal 事件是否已经注册了 */
	volatile sig_atomic_t evsignal_caught; /* 是否有信号发生的标记,是 volatile 类型,因为它会在另外的线程中被修改 */
	struct event_list evsigevents[NSIG]; /* evsigevents[signo] 表示注册到信号 signo 的事件链表 */
	sig_atomic_t evsigcaught[NSIG]; /* 具体记录每个信号触发的次数,evsigcaught[signo] 记录信号 signo 被触发的次数 */
#ifdef HAVE_SIGACTION
	struct sigaction **sh_old; /* 记录了原来的 Signal 处理函数指针,当信号 signo 注册的 event 被清空时,需要恢复其旧的处理函数 */
#else
	ev_sighandler_t **sh_old; /* 记录了原来的 Signal 处理函数指针,当信号 signo 注册的 event 被清空时,需要恢复其旧的处理函数 */
#endif
	int sh_old_max; /* sh_old 数组的最大下标 */
};

下面分析 evsignal_info 结构体中的各个成员:

  • ev_signal,一个 event 类型变量,关注 socket pair 中读 socket 的可读事件,socket pair 后面会分析
  • ev_signal_pair,保存了 socket pair 的两个 socket 描述符,ev_signal_pair[0] 为写 socket,ev_signal_pair[1] 为读 socket,往写 socket 中写的数据会被发送到读 socket。当有信号发生时,在信号处理函数中往写 socket 写入一字节数据,触发**读 socket ** 的可读事件,引起 I/O 复用接口返回(I/O 复用接口关注了 **读 socket ** 的可读事件),达到及时处理信号事件的目的(如果 I/O 复用一直阻塞,会导致信号事件不能及时得到处理)
  • ev_signal_added,记录 ev_signal 是否已经注册到 event_base
  • evsignal_caught,是否有信号发生的标记,会在信号处理函数中被设置为1
  • evsigevent,数组,数组的每个元素是一个链表(用尾队列实现),evsigevent[signo] 表示**信号 signo **的事件链表(即当信号 signo 发生,该事件链表上的所有事件都会被激活)
  • evsigcaught,数组,evsigcaught[signo] 记录信号 signo发生的次数
  • sh_old,数组,sh_old[signo] 记录了信号 signo旧的信号处理函数,HAVE_SIGACTION 这个宏用于判断系统是否支持sigaction系统调用,如果不支持则使用signal系统调用
  • sh_old_max,记录 sh_old 数组的最大下标

从上面的分析可以看出,Libevent使用链表数组 evsigevent 来管理信号事件,signo信号相关的事件被插入链表 evsigevent[signo] 中。也就是说,每个信号都拥有一个链表用于管理与该信号相关的事件。

信号发生时,利用 socket pair 使 I/O 复用接口返回,及时处理信号事件。通过这种方式,信号的处理被合并到事件循环中,体现了 Libevent 统一事件源(I/O事件、定时事件和信号事件)的思想。

三、相关操作

与信号事件相关的函数声明在 evsignal.h 头文件中,如下:

int evsignal_init(struct event_base *); // 信号管理初始化
void evsignal_process(struct event_base *); // 处理信号事件,由 dispatch 函数调用
int evsignal_add(struct event *); // 添加信号事件
int evsignal_del(struct event *); // 删除信号事件
void evsignal_dealloc(struct event_base *); // 析构信号管理

上述在 evsignal.h 中声明的几个函数都是外部函数,此外还有几个内部函数。

3.1 信号管理初始化

evsignal_init 函数用于信号管理初始化,即初始化 event_base 中的 sig 成员,代码注释如下:

int
evsignal_init(struct event_base *base)
{
   
	int i;

	// 创建 socket pair,往 ev_signal_pair[0](写socket) 中写入的数据会被发送到 ev_signal_pair[1](读socket)
	if (evutil_socketpair(
		    AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1) {
   
#ifdef WIN32
		event_warn("%s: socketpair", __func__);
#else
		event_err(1, "%s: socketpair", __func__);
#endif
		return -1;
	}
	// closeonexec: 当 fork 一个子进程,在子进程执行 exec 时该描述符被自动关闭
	FD_CLOSEONEXEC(base->sig.ev_signal_pair[0]);
	FD_CLOSEONEXEC(base->sig.ev_signal_pair[1]);
	base->sig.sh_old = NULL;
	base->sig.sh_old_max = 0;
	base->sig.evsignal_caught = 0;
	memset(&base->sig.evsigcaught, 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值