论坛首页 编程语言技术论坛

libevent源码浅析(三)

浏览 2533 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-03-17   最后修改:2009-05-15
C
这次我们来看libevent的信号的处理。

在libevent中通过使用socketpair建立一对流管道,也就是全双工管道,来将信号事件与句柄事件统一起来。

先来看数据结构:

struct evsignal_info {
	struct event ev_signal;   ///<所属的event
	int ev_signal_pair[2];    ///<创建的流管道
	int ev_signal_added;     ///<信号是否已被加入到event中的标记。
	volatile sig_atomic_t evsignal_caught; ///<事件触发标记,1表示有信号被触发
	struct event_list evsigevents[NSIG];   ///<多个事件有可能注册到同一个信号,因此这里每个信号的事件都是一个event_list.
	sig_atomic_t evsigcaught[NSIG];   ///<由于一个信号可能被注册多次,这里保存信号被捕捉的次数
#ifdef HAVE_SIGACTION
	struct sigaction **sh_old;
#else
	ev_sighandler_t **sh_old;
#endif
	int sh_old_max;
};


接下来可以看几个主要的函数:

evsignal_init函数主要用来初始化一些数据结构。

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

	///创建一对流管道
	if (evutil_socketpair(
		    AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1)
		event_err(1, "%s: socketpair", __func__);
        
        ///设置fd
	FD_CLOSEONEXEC(base->sig.ev_signal_pair[0]);
	FD_CLOSEONEXEC(base->sig.ev_signal_pair[1]);
        ///初始化sig数据结构
	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 */
        ///在libevent里面,所有的事件队列都用tail queue实现,linux下它使用的是linux自带的taile queue,具体用法可以去看man手册。
	for (i = 0; i < NSIG; ++i)
		TAILQ_INIT(&base->sig.evsigevents[i]);

       ///设置非阻塞
        evutil_make_socket_nonblocking(base->sig.ev_signal_pair[0]);
        
        ///初始化event结构
	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;
}



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);
///如果此信号的事件队列为空则说明此信号第一次被注册。因此设置信号处理函数,这里所有的信号都注册到相同的处理函数evsignal_handler,接下来我们会来分析这个函数。
	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);
}


evsignal_handler就是所有信号的处理函数。

static void
evsignal_handler(int sig)
{
	int save_errno = errno;

	if (evsignal_base == NULL) {
		event_warn(
			"%s: received signal %d, but have no base configured",
			__func__, sig);
		return;
	}

///进入此函数,说明信号已经来临,因此这里设置捕捉次数,以及此信号已经被捕捉的标记。
	evsignal_base->sig.evsigcaught[sig]++;
	evsignal_base->sig.evsignal_caught = 1;

#ifndef HAVE_SIGACTION
	signal(sig, evsignal_handler);
#endif

///流管道开始发送数据,说明信号已经来临。此时另一端就会检测到事件从而调用我们初始化注册的回调函数。
	/* Wake up our notification mechanism */
	send(evsignal_base->sig.ev_signal_pair[0], "a", 1, 0);
	errno = save_errno;
}


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;
///循环遍历,得到已发生的信号事件。
		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);
		}

		sig->evsigcaught[i] = 0;
	}
}

论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics