libevent源码学习(15):信号event的处理

 

目录

信号event处理流程

与信号event相关的结构体

初始化工作

创建一个信号event

添加一个信号event

信号回调函数

信号event的激活


       Libevent中的event,主要分为三大类:io读写event、超时事件以及信号event。前面的文章对前两类的event都进行了分析,下面就来说一下Libevent是如何处理信号event的。

信号event处理流程

       不管使用的是什么后端IO复用模型,这些复用模型本身都是只支持读写IO事件的,Libevent所实现的“信号event处理”,实际上也是把信号event最终转换到了IO读写event。它的工作原理为:用户层面指定一个需要监听的信号sig以及回调函数custom_cb,对于Libevent,它会为sig定义一个信号处理函数evsig_handler,然后再定义一个socket pair,这是一对全双工套接字,向其中一个写入数据那么就可以从另一个中读出数据,Libevent会将其中之一固定为读端,而另一个则为写端,并为读端套接字注册一个IOevent名为ev_signal,其回调函数为evsig_cb

      当需要监听的信号sig到达,就会自动去调用刚才设置的信号处理函数evsig_handler,在evsig_handler中会向写端套接字写入一个字节(这个字节实际上就是sig的值),此时读端套接字就会收到这个字节,触发可读事件,激活ev_signal然后调用evsig_cb,在evsig_cb函数中,会将所有监听sig信号的event全部激活,这些event中就包含根据用户要求所定义的event,处理这个event的时候就会回调custom_cb,这样,就完成了监听sig信号到回调custom_cb的信号event处理过程。如下图所示:

与信号event相关的结构体

struct event_base {
	......
	const struct eventop *evsigsel;//信号处理后端相关函数
	/** Data to implement the common signal handelr code. */
	struct evsig_info sig;//信号相关信息
        ......
	/** Mapping from file descriptors to enabled (added) events */
	struct event_io_map io;

	/** Mapping from signal numbers to enabled (added) events. */
	struct event_signal_map sigmap;

	......
};

       前面说过,event_base在初始化的时候就会绑定一个IO复用模型后端到evsel成员中,但是对于信号event,整个处理的过程中是不会也不应该直接使用这些IO复用后端,而是使用信号event专用的后端,并且将其绑定到evsigsel中,在该后端结构体中只含有添加和删除函数,如下所示:

static const struct eventop evsigops = {
	"signal",
	NULL,
	evsig_add,
	evsig_del,
	NULL,
	NULL,
	0, 0, 0
};

        然后再说event_base中的sig成员,这是一个evsig_info类型的结构体变量,该类型定义如下:

typedef void (*ev_sighandler_t)(int);

struct evsig_info {
	struct event ev_signal; //需要添加到event_io_map中监听读端套接字
	/* 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;  //标识ev_signal是否被添加到io map中
	/* Count of the number of signals we're currently watching. */
	int ev_n_signals_added; //监听的信号数量

#ifdef _EVENT_HAVE_SIGACTION
	struct sigaction **sh_old; //sigaction *数组,每一个元素都指向一个sigaction,其中含有信号的处理函数
#else
	ev_sighandler_t **sh_old; //如果没有定义_EVENT_HAVE_SIGACTION,那么就是一个函数指针数组,存储每个信号的回调函数指针
#endif
	/* Size of sh_old. */
	int sh_old_max;  //sh_old数组的大小
};

       这里的ev_signal_pair[2]就是前面所说的socket pair全双工套接字。而evsig_info中的ev_signal,就是用于监听socket pair中读端套接字的event。sh_old是一个二级指针,也可以看做数组,它每个元素都对应了一个信号的处理函数。

初始化工作

       对于普通的IO复用模型,是在event_base的初始化过程中将相应的后端结构体绑定到evsel中的,同样的,信号相关的后端结构体也是在event_base的初始化过程中绑定到evsigsel中的,并且还会做很多事情,如下所示:

struct event_base *
event_base_new_with_config(const struct event_config *cfg)
{
	......
	for (i = 0; eventops[i] && !base->evbase; i++) { 
		......
		base->evsel = eventops[i]; //将该backup作为base使用的backup
		base->evbase = base->evsel->init(base); //用选定的backup的初始化函数来初始化base中的evbase
	}
        ......
}
//以epoll后端为例
static void *
epoll_init(struct event_base *base)
{
	......
	evsig_init(base);
}

int
evsig_init(struct event_base *base
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值