event 信号注册和激活

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Function_Dou/article/details/87904134

上节咱们分析了信号的初始化以及最重要的socket pair以及使用原因. 本节继续来分析event信号的注册和激活功能, 其实上一节最后分析socket pair已经提到了一些.

信号注册

信号调用evsignal_add函数来实现信号的注册, 有点向event调用event_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);
}
  1. 很明显这是信号的注册函数, 所以如果是IO操作肯定返回出错
  2. 查看该信号的监听队列是否已经被初始化过, 如果没有就将它初始化并且设置信号回调的默认回调函数evsignal_handler. 如果信号也没有加入到注册队列中, 则加入到注册队列中(一种信号只注册一次), 并设置ev_signal_added标志位, 表示加入注册队列中.
  3. 最后将信号加入信号队列中

_evsignal_set_handler 函数

该函数就是很简单的通过signal函数注册信号的回调函数, 默认设置的回调函数都是evsignal_handler

int
_evsignal_set_handler(struct event_base *base,
		      int evsignal, void (*handler)(int))
{
#ifdef HAVE_SIGACTION
	struct sigaction sa;
#else
	ev_sighandler_t sh;
#endif
	struct evsignal_info *sig = &base->sig;
	void *p;

	/*
	 * resize saved signal handler array up to the highest signal number.
	 * a dynamic array is used to keep footprint on the low side.
	 */
	if (evsignal >= sig->sh_old_max) {
		int new_max = evsignal + 1;
		event_debug(("%s: evsignal (%d) >= sh_old_max (%d), resizing",
			    __func__, evsignal, sig->sh_old_max));
        // 为该信号链表分配空间
		p = realloc(sig->sh_old, new_max * sizeof(*sig->sh_old));
		if (p == NULL) {
			event_warn("realloc");
			return (-1);
		}
		// 初始化为 0
		memset((char *)p + sig->sh_old_max * sizeof(*sig->sh_old),
		    0, (new_max - sig->sh_old_max) * sizeof(*sig->sh_old));

		sig->sh_old_max = new_max;
		sig->sh_old = p;
	}

	/* allocate space for previous handler out of dynamic array */
	sig->sh_old[evsignal] = malloc(sizeof *sig->sh_old[evsignal]);
	if (sig->sh_old[evsignal] == NULL) {
		event_warn("malloc");
		return (-1);
	}

	/* save previous handler and setup new handler */
#ifdef HAVE_SIGACTION
	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = handler;	// 设置回调函数
	sa.sa_flags |= SA_RESTART;	// 设置标志位
	sigfillset(&sa.sa_mask);

	if (sigaction(evsignal, &sa, sig->sh_old[evsignal]) == -1) {
		event_warn("sigaction");
		free(sig->sh_old[evsignal]);
		sig->sh_old[evsignal] = NULL;
		return (-1);
	}
#else
	if ((sh = signal(evsignal, handler)) == SIG_ERR) {
		event_warn("signal");
		free(sig->sh_old[evsignal]);
		sig->sh_old[evsignal] = NULL;
		return (-1);
	}
	*sig->sh_old[evsignal] = sh;
#endif

	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 */
	// 回调函数其实就是发送一字节. 实质是通知该处理信号了
	// 唤醒 event_loop 主循环处理信号
	send(evsignal_base->sig.ev_signal_pair[0], "a", 1, 0);
	errno = save_errno;
}

设置信号的捕捉次数以及捕捉标志位. 这里我再将上节话搬过来

当信号发生时, 注册的回调函数evsignal_handler会向服务端发送一字节数据并设置该信号标志和次数, 而服务端则调用统一的就绪回调函数evsignal_cb接收一字节数据, 而libevent通过分发器(如 : epoll 中的epoll_wait) 调用evsignal_process将信号加入就绪队列中.

信号激活

evsignal_process其实就是直接将信号从就绪队列中删除在加入到就绪队列中. 功能现在看来很简单. 其实除去信号的部分成员参数需要修改, 就像是直接调用event_active函数.

// 激活事件
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);
		}

	}
}

总结

本节分析了信号设置默认回调函数还有信号是通过evsignal_process激活的.

猜你喜欢

转载自blog.csdn.net/Function_Dou/article/details/87904134