libevent源码阅读笔记(五)

本文深入剖析了libevent库中对信号处理的支持方式,包括初始化信号处理、处理信号事件和注册信号事件的过程。详细解释了如何通过创建socketpair、监听特定事件以及设置自定义信号处理器来实现信号处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    本章是libevent源码分析的最后一篇,主要分析一下libevent对信号处理的支持方式。

    1).evsignal_init()函数分析:

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

	/* 
	 * 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.
	 */
	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_warn("%s: socketpair", __func__);
#else
		event_err(1, "%s: socketpair", __func__);
#endif
		return -1;
	}

	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, 0, sizeof(sig_atomic_t)*NSIG);
	/* initialize the queues for all events */
	for (i = 0; i < NSIG; ++i)
		TAILQ_INIT(&base->sig.evsigevents[i]);

        evutil_make_socket_nonblocking(base->sig.ev_signal_pair[0]);

	event_set(&base->sig.ev_signal, base->sig.ev_signal_pair[1],
		EV_READ | EV_PERSIST, evsignal_cb, &base->sig.ev_signal);
	base->sig.ev_signal.ev_base = base;
	base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL;

	return 0;
}

    该函数主要初始化signal处理相关的事件,会在event_base_new()函数中调用各个系统平台的*_init()函数里面调用进行初始化。主要处理流程如下:

    a).创建一对socket pair;

    b).event base中signal的event会监听socketpair[1]的读事件,回调函数为evsignal_cb;


    2).evsignal_process()函数:

void
evsignal_process(struct event_base *base)
{
	struct evsignal_info *sig = &base->sig;
	struct event *ev, *next_ev;
	sig_atomic_t ncalls;
	int i;
	
	base->sig.evsignal_caught = 0;
	for (i = 1; i < NSIG; ++i) {
		ncalls = sig->evsigcaught[i];
		if (ncalls == 0)
			continue;
		sig->evsigcaught[i] -= ncalls;

		for (ev = TAILQ_FIRST(&sig->evsigevents[i]);
		    ev != NULL; ev = next_ev) {
			next_ev = TAILQ_NEXT(ev, ev_signal_next);
			if (!(ev->ev_events & EV_PERSIST))
				event_del(ev);
			event_active(ev, EV_SIGNAL, ncalls);
		}

	}
}

    该函数主要在event_base_loop()事件处理主循环中会调用,为对所有信号产生的事件的处理,该函数的主要流程如下:

    a).该函数中,会遍历二维数组base->sig.evsigevents中注册在信号上的事件,如果base->sig.evsigcaughts数组中的对应项不为0,则表明程序接收到相应的信号,则处理其上注册的所有的事件,即将这些事件添加到activequeue中去,从而在event_base_loop()函数中统一为IO事件来处理 ;


    3).evsignal_add()函数分析:

int
evsignal_add(struct event *ev)
{
	int evsignal;
	struct event_base *base = ev->ev_base;
	struct evsignal_info *sig = &ev->ev_base->sig;

	if (ev->ev_events & (EV_READ|EV_WRITE))
		event_errx(1, "%s: EV_SIGNAL incompatible use", __func__);
	evsignal = EVENT_SIGNAL(ev);
	assert(evsignal >= 0 && evsignal < NSIG);
	if (TAILQ_EMPTY(&sig->evsigevents[evsignal])) {
		event_debug(("%s: %p: changing signal handler", __func__, ev));
		if (_evsignal_set_handler(
			    base, evsignal, evsignal_handler) == -1)
			return (-1);

		/* catch signals if they happen quickly */
		evsignal_base = base;

		if (!sig->ev_signal_added) {
			if (event_add(&sig->ev_signal, NULL))
				return (-1);
			sig->ev_signal_added = 1;
		}
	}

	/* multiple events may listen to the same signal */
	TAILQ_INSERT_TAIL(&sig->evsigevents[evsignal], ev, ev_signal_next);

	return (0);
}

    这个函数是注册signal的event事件,这个函数会将注册的event插入到相应signal的sig->evsigevents中去;从而在上面的evsignal_process()函数中统一处理;

    注意上面的_evsignal_set_handler()函数调用,该函数只在第一次注册相应信号的事件时才会调用;该函数主要修改系统的信号处理函数为evsignal_handler()函数,该函数会在程序收到信号时调用,主要流程是增加event base中的sig.evsigcaught[sig]的计数,同时标记evsignal_caught为1,表明程序收到了信号,最后写入一个字节到socket pair[0];


    通过上述分析,我们基本能总结出来libevent对信号的处理流程:

    1).添加信号的event,会修改信号的默认handler函数;

     2).程序接收到signal,调用设置的handler函数,该函数会添加相应的event base中的signal的数组中对应信号的计数;同时会向socket pair[0]中写入一个字节;

     3).在event loop中,socket pair[1]上会检测到读事件,从而epoll_wait()会返回,通过eventbase->sig.sigevent_caught是否置位来判断产生了信号,从而去调用在注册的signal事件上的回调函数;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值