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处理过程。如下图所示:

libevent源码学习(15):信号event的处理_第1张图片

与信号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)
{
	if (evutil_socketpair(
		    AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1) {//创建了两个互相连接的套接字放到ev_signal_pair中
       ......
	}

	evutil_make_socket_closeonexec(base->sig.ev_signal_pair[0]);//调用exec时关闭套接字
	evutil_make_socket_closeonexec(base->sig.ev_signal_pair[1]);
	base->sig.sh_old = NULL;
	base->sig.sh_old_max = 0;

	evutil_make_socket_nonblocking(base->sig.ev_signal_pair[0]);//将创建的两个套接字都设置为非阻塞
	evutil_make_socket_nonblocking(base->sig.ev_signal_pair[1]);

	......
}

         在evsig_init函数中,会先调用evutil_socketpair来创建一对全双工套接字,该函数定义如下:

int
evutil_socketpair(int family, int type, int protocol, evutil_socket_t fd[2])//创建一对相互连接的套接字,保存在fd[2]中
{
#ifndef WIN32
	return socketpair(family, type, protocol, fd);
#else
	return evutil_ersatz_socketpair(family, type, protocol, fd);
#endif
}

int
evutil_ersatz_socketpair(int family, int type, int protocol,
    evutil_socket_t fd[2])
{
	/* This code is originally from Tor.  Used with permission. */

	/* This socketpair does not work when localhost is down. So
	 * it's really not the same thing at all. But it's close enough
	 * for now, and really, when localhost is down sometimes, we
	 * have other problems too.
	 */
#ifdef WIN32
#define ERR(e) WSA##e
#else
#define ERR(e) e
#endif
	evutil_socket_t listener = -1;
	evutil_socket_t connector = -1;
	evutil_socket_t acceptor = -1;
	struct sockaddr_in listen_addr;
	struct sockaddr_in connect_addr;
	ev_socklen_t size;
	int saved_errno = -1;

	if (protocol
		|| (family != AF_INET
#ifdef AF_UNIX
		    && family != AF_UNIX
#endif
		)) {
		EVUTIL_SET_SOCKET_ERROR(ERR(EAFNOSUPPORT));
		return -1;
	}
	if (!fd) {
		EVUTIL_SET_SOCKET_ERROR(ERR(EINVAL));
		return -1;
	}

	listener = socket(AF_INET, type, 0);
	if (listener < 0)
		return -1;
	memset(&listen_addr, 0, sizeof(listen_addr));
	listen_addr.sin_family = AF_INET;
	listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);//listen监听本地环回地址
	listen_addr.sin_port = 0;	/* kernel chooses port.	 */
	if (bind(listener, (struct sockaddr *) &listen_addr, sizeof (listen_addr))
		== -1)
		goto tidy_up_and_fail;
	if (listen(listener, 1) == -1)//开始监听
		goto tidy_up_and_fail;

	connector = socket(AF_INET, type, 0); //创建connector套接字
	if (connector < 0)
		goto tidy_up_and_fail;
	/* We want to find out the port number to connect to.  */
	size = sizeof(connect_addr);
	if (getsockname(listener, (struct sockaddr *) &connect_addr, &size) == -1)//获取listener绑定的ip地址,保存到connect_addr中
		goto tidy_up_and_fail;
	if (size != sizeof (connect_addr))
		goto abort_tidy_up_and_fail;
	if (connect(connector, (struct sockaddr *) &connect_addr,
				sizeof(connect_addr)) == -1)//将connector与listener连接
		goto tidy_up_and_fail;

	size = sizeof(listen_addr);
	acceptor = accept(listener, (struct sockaddr *) &listen_addr, &size);//通过acceptor可以向connector发送数据
	if (acceptor < 0)
		goto tidy_up_and_fail;
	if (size != sizeof(listen_addr))
		goto abort_tidy_up_and_fail;
	evutil_closesocket(listener);
	/* Now check we are talking to ourself by matching port and host on the
	   two sockets.	 */
	if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -1)//确保是本地环回监听
		goto tidy_up_and_fail;
	if (size != sizeof (connect_addr)
		|| listen_addr.sin_family != connect_addr.sin_family
		|| listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr
		|| listen_addr.sin_port != connect_addr.sin_port)
		goto abort_tidy_up_and_fail;
	fd[0] = connector;//通过connector向listener发信息
	fd[1] = acceptor; //通过acceptor向connector发信息

	return 0;

    ......
}

        创建一对全双工套接字的流程是:先创建一个listener,监听本地环回端口,相当于服务端。创建一个connector作为客户端向listener发起连接,listener通过accept函数与connecotr建立连接,新连接套接字为acceptor,此时acceptor和connector就可以互发消息,成为一对全双工套接字。

       再回到evsig_init函数中,接着就会把创建的这对套接字设置为非阻塞,并且在执行exec时关闭。接下来会调用event_assign函数,设置ev_signal的监听对象就是这对套接字之一,并且监听读事件,回调函数为evsig_cb,这点就对应了前面处理流程中紫色框部分。然后就是将信号处理后端函数结构体绑定到event_base中的evsigsel上。

int
evsig_init(struct event_base *base)
{
	......

	event_assign(&base->sig.ev_signal, base, base->sig.ev_signal_pair[1],
		EV_READ | EV_PERSIST, evsig_cb, base);//pair[1]为读端,为其注册ev_signal,类型为永久性读事件

	base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL;//标识为内部事件
	event_priority_set(&base->sig.ev_signal, 0);//signal event优先级为0

	base->evsigsel = &evsigops;//绑定信号处理后端函数调用结构体

	return 0;
}

       到这里,初始化就算是完成了,关于ev_signal的回调函数后面再说,接下来就进入信号event的处理过程。

创建一个信号event

       创建一个io event的函数是event_new,如果要创建信号event,一种方法是直接设置event_new的参数为EV_SIGNAL,不过这种方法对于用户来说是非常不友好的。为此,Libevent在event.h中进行了如下的宏定义:

#define evsignal_add(ev, tv)		event_add((ev), (tv))
#define evsignal_assign(ev, b, x, cb, arg)			\
	event_assign((ev), (b), (x), EV_SIGNAL|EV_PERSIST, cb, (arg))
#define evsignal_new(b, x, cb, arg)				\
	event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg))
#define evsignal_del(ev)		event_del(ev)
#define evsignal_pending(ev, tv)	event_pending((ev), EV_SIGNAL, (tv))
#define evsignal_initialized(ev)	event_initialized(ev)

        可以看到,这里实际上对io event的相关函数的参数进行了“特殊化处理”,最终得到了信号event的相关函数。此时,就可以通过evsignal_new函数来创建一个信号event了,实际上就是创建了一个EV_SIGNAL|EV_PERSIST的永久信号事件。而该函数内部实际上又会调用event_assign函数。需要注意的是,创建普通event时第二个参数传入的是需要监听的的文件描述符,而这里创建信号event时传入的第二个参数则应当是需要监听的信号值了,比如说需要监听的信号是SIGUSR1,那么调用evsignal_new时,传入的第二个参数就应该直接使用SIGUSR1。evsignal_new的第三个参数cb自然就应当是用户需要监听的信号发生后,期待调用的函数。

       接下来再来分析如何添加一个信号event。

添加一个信号event

       如上所述,添加一个信号event使用evsignal_add函数,实际上就是event_add函数。前面创建的信号event,其events成员已经被设置为了EV_SIGNAL|EV_PERSIST。因此,event_add函数中会调用evmap_signal_add函数将该event添加到event_signal_map中,evmap_signal_add如下所示:

int
evmap_signal_add(struct event_base *base, int sig, struct event *ev)
{
	const struct eventop *evsel = base->evsigsel;//使用的是信号回调函数结构体
	struct event_signal_map *map = &base->sigmap;
	struct evmap_signal *ctx = NULL;

	if (sig >= map->nentries) {
		if (evmap_make_space(
			map, sig, sizeof(struct evmap_signal *)) == -1)
			return (-1);
	}
	GET_SIGNAL_SLOT_AND_CTOR(ctx, map, sig, evmap_signal, evmap_signal_init,
	    base->evsigsel->fdinfo_len); //ctx指向sigmap的entries[sig]对应的evmap_signal,evmap_signal中含有一个event双向链表

	if (TAILQ_EMPTY(&ctx->events)) {
		if (evsel->add(base, ev->ev_fd, 0, EV_SIGNAL, NULL)//调用的实际上是evsigsel中的add函数
		    == -1)
			return (-1);
	}

	TAILQ_INSERT_TAIL(&ctx->events, ev, ev_signal_next);

	return (1);
}

       可以看到,在通过TAILQ_INSERT_TAIL将前面创建的event添加到event_signal_map之前,会先调用信号回调后端函数中的add函数,也就是前面的evsig_add函数,该函数定义如下:

static int
evsig_add(struct event_base *base, evutil_socket_t evsignal, short old, short events, void *p)
{
	struct evsig_info *sig = &base->sig;
	(void)p;

	EVUTIL_ASSERT(evsignal >= 0 && evsignal < NSIG);

	/* catch signals if they happen quickly */
	EVSIGBASE_LOCK();
	if (evsig_base != base && evsig_base_n_signals_added) {
		event_warnx("Added a signal to event base %p with signals "
		    "already added to event_base %p.  Only one can have "
		    "signals at a time with the %s backend.  The base with "
		    "the most recently added signal or the most recent "
		    "event_base_loop() call gets preference; do "
		    "not rely on this behavior in future Libevent versions.",
		    base, evsig_base, base->evsel->name);
	}
	evsig_base = base;
	evsig_base_n_signals_added = ++sig->ev_n_signals_added;
	evsig_base_fd = base->sig.ev_signal_pair[0];//pair[0]是写端
	EVSIGBASE_UNLOCK();

	event_debug(("%s: %d: changing signal handler", __func__, (int)evsignal));
	if (_evsig_set_handler(base, (int)evsignal, evsig_handler) == -1) {//设置信号处理函数
		goto err;
	}


	if (!sig->ev_signal_added) {//如果还没有添加ev_signal,就调用event_add添加,如果添加过了就不用再添加了
		if (event_add(&sig->ev_signal, NULL))//ev_signal的事件类型为READ|PERSIST
			goto err;
		sig->ev_signal_added = 1;
	}

	return (0);

err:
	EVSIGBASE_LOCK();
	--evsig_base_n_signals_added;
	--sig->ev_n_signals_added;
	EVSIGBASE_UNLOCK();
	return (-1);
}

        evsig_add函数做了两件重要的事情:设置信号回调函数,将监听读端套接字事件的ev_signal进行添加。

        信号回调函数的设置是通过_evsig_set_handler函数实现的,实际上它内部还是用的signal或者sigaction函数,并且将设置的信号回调函数都放到了sig的sh_old数组中,数组的索引就是信号值。该函数定义如下:

int
_evsig_set_handler(struct event_base *base,
    int evsignal, void (__cdecl *handler)(int))
{
#ifdef _EVENT_HAVE_SIGACTION
	struct sigaction sa;  //存储新的信号处理信息
#else
	ev_sighandler_t sh; //信号处理函数指针
#endif
	struct evsig_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 = mm_realloc(sig->sh_old, new_max * sizeof(*sig->sh_old));
		if (p == NULL) {
			event_warn("realloc");
			return (-1);
		}

		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] = mm_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 _EVENT_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) {//sig->sh_old[evsignal]中保存evsignal原本的信号处理函数
		event_warn("sigaction");
		mm_free(sig->sh_old[evsignal]);
		sig->sh_old[evsignal] = NULL;
		return (-1);
	}
#else
	if ((sh = signal(evsignal, handler)) == SIG_ERR) {
		event_warn("signal");
		mm_free(sig->sh_old[evsignal]);
		sig->sh_old[evsignal] = NULL;
		return (-1);
	}
	*sig->sh_old[evsignal] = sh; //存储信号处理函数
#endif

	return (0);
}

       通过_evsig_set_handler函数,就把信号sig的回调函数设置为evsig_handler,也就是调用_evsig_set_handler的第三个参数。当信号sig达到时,就会自动调用evsig_handler函数。

       evsig_handler函数后面再说,先回到evsig_add函数中。设置好信号回调函数之后,就会将ev_signal通过event_add函数进行添加,前面说过,ev_signal的监听事件类型为EV_READ|EV_PERSIST,它用来监听socket pair中的读端套接字是否有可读事件发生。

       也就是说,通过evsig_add函数,不仅设置了信号回调函数,还设置监听读端套接字是否有可读事件发生。

       接下来再来看看信号回调函数到底做了什么事。

信号回调函数

       evsig_handler函数定义如下所示:

static void __cdecl
evsig_handler(int sig)//信号处理函数,当信号发生时调用该函数
{
	int save_errno = errno;
#ifdef WIN32
	int socket_errno = EVUTIL_SOCKET_ERROR();
#endif
	ev_uint8_t msg;

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

#ifndef _EVENT_HAVE_SIGACTION
	signal(sig, evsig_handler);
#endif

	/* Wake up our notification mechanism */
	msg = sig;
	send(evsig_base_fd, (char*)&msg, 1, 0);//向写端pair[0]写入信号值,那么读端pair[1]就会收到该信号值
	errno = save_errno;
#ifdef WIN32
	EVUTIL_SET_SOCKET_ERROR(socket_errno);
#endif
}

        这个函数的功能很简单,就是向socket pair中的写端写入一个字节的数据,而这一字节就是信号的值。当这个字节通过socket pair的写端套接字写入之后,socket pair的读端就会读到该字节,读端套接字可读事件发生,监听该事件的ev_signal就会被激活。激活后调用ev_signal的回调函数evsig_cb,该函数定义如下:

static void
evsig_cb(evutil_socket_t fd, short what, void *arg)
{
	static char signals[1024];
	ev_ssize_t n;
	int i;
	int ncaught[NSIG];
	struct event_base *base;

	base = arg;

	memset(&ncaught, 0, sizeof(ncaught));

	while (1) {
		n = recv(fd, signals, sizeof(signals), 0);//从读端pair[1]读取数据,fd是非阻塞的,如果
		if (n == -1) {//说明没有数据可读,或者出错了
			int err = evutil_socket_geterror(fd);
			if (! EVUTIL_ERR_RW_RETRIABLE(err))
				event_sock_err(1, fd, "%s: recv", __func__);
			break;
		} else if (n == 0) {//说明对端关闭
			/* XXX warn? */
			break;
		}
		for (i = 0; i < n; ++i) {//读到了多少个字节,说明收到了多少个信号
			ev_uint8_t sig = signals[i];
			if (sig < NSIG)
				ncaught[sig]++;//记录该信号被捕获的次数,捕获多少次就应该激活多少次
		}
	}

	EVBASE_ACQUIRE_LOCK(base, th_base_lock);
	for (i = 0; i < NSIG; ++i) {//遍历所有被捕获的信号,将其对应的event激活
		if (ncaught[i])
			evmap_signal_active(base, i, ncaught[i]);
	}
	EVBASE_RELEASE_LOCK(base, th_base_lock);
}

        evsig_cb会从读端套接字中读取数据到signals数组中,读出的每一个字节都代表一个发生的信号值,用ncaught数组来存储每个信号发生的次数,并且对于每个发生的信号,都调用evmap_signal_active进行处理,可以参考event_signal_map的激活。

信号event的激活

       evmap_signal_active主要是找到event_signal_map中,监听信号值为sig的那一个evmap_signal,在这个evmap_signal中,包含了所有监听信号值为sig的event组成的双向链表,然后直接遍历这个双向链表,把每个元素都按照EV_SIGNAL的激活方式调用event_active_nolock函数。

void
evmap_signal_active(struct event_base *base, evutil_socket_t sig, int ncalls)
{
	struct event_signal_map *map = &base->sigmap;
	struct evmap_signal *ctx;
	struct event *ev;

	EVUTIL_ASSERT(sig < map->nentries);
	GET_SIGNAL_SLOT(ctx, map, sig, evmap_signal);   //ctx保存信号值为sig的evmap_signal,也就是一个event的双向链表

	TAILQ_FOREACH(ev, &ctx->events, ev_signal_next)
		event_active_nolock(ev, EV_SIGNAL, ncalls);//遍历信号值为Sig的event双向链表,将每个event以EV_SIGNAL方式激活
}

       而对于event_active_nolock函数来说,做的事情很少,就是把传入的event添加到激活队列中,如下所示:

void
event_active_nolock(struct event *ev, int res, short ncalls)
{
	......
	event_queue_insert(base, ev, EVLIST_ACTIVE); //将event插入到激活队列中
    ......
}

       到这里,用来监听用户指定的信号值的那个信号event就被添加到了激活队列中,接下来,就等待主循环处理激活队列时去处理那个信号event。

int
event_base_loop(struct event_base *base, int flags)  
{
	    ......
		if (N_ACTIVE_CALLBACKS(base)) {  //如果激活队列中有事件
			int n = event_process_active(base); //执行激活队列中的event相应的回调函数,返回的n是成功执行的非内部事件数目
		......
}

static int
event_process_active(struct event_base *base)//遍历base的激活队列中所有event,调用其回调函数
{
    ......
	for (i = 0; i < base->nactivequeues; ++i) {  //遍历激活队列中的事件
		if (TAILQ_FIRST(&base->activequeues[i]) != NULL) {  //同一个优先级下可以有多个事件
			base->event_running_priority = i;   //设置当前的优先级
			activeq = &base->activequeues[i];   //获取优先级i下的所有event组成的链表
			c = event_process_active_single_queue(base, activeq); //遍历activeq链表,调用其中每个event的回调函数
			......
		}
	}
}

static int
event_process_active_single_queue(struct event_base *base,
    struct event_list *activeq)
{
	    ......
		switch (ev->ev_closure) { //在调用回调函数是否进行其他行为
		case EV_CLOSURE_SIGNAL: //信号事件回调处理方式
			event_signal_closure(base, ev);
			break;
		case EV_CLOSURE_PERSIST:   //对于永久事件,在调用回调函数之前会重新调用event_add来添加该事件到对应队列中
			event_persist_closure(base, ev);
			break;
		default:
		case EV_CLOSURE_NONE:   //对于一般事件,直接调用回调函数
			EVBASE_RELEASE_LOCK(base, th_base_lock);//释放锁
			(*ev->ev_callback)(
				ev->ev_fd, ev->ev_res, ev->ev_arg);  //调用回调函数
			break;
		}
        ......
}

      激活处理的过程就是event_base_loop——event_process_active——event_process_active_single_queue,在event_process_active_single_queue函数中,会判断激活处理事件的回调关闭方式ev_closure,而对于信号event来说,在evsignal_new时由于传入的参数为EV_SIGNAL|EV_PERSIST,因此evsignal_new内部调用的event_assign会直接设置信号event的ev_closure为EV_CLOSURE_SIGNAL,也就是说,处理激活的信号event最终是通过event_signal_closure函数实现的,该函数定义如下:

static inline void
event_signal_closure(struct event_base *base, struct event *ev)
{
	......
	while (ncalls) {//信号发生了多少次就调用多少次回调函数
		ncalls--;
		ev->ev_ncalls = ncalls;
		if (ncalls == 0)
			ev->ev_pncalls = NULL;
		(*ev->ev_callback)(ev->ev_fd, ev->ev_res, ev->ev_arg);

		......
	}
}

       这里的ncalls,实际上就是evsig_cb函数中记录的信号值sig的发生次数,信号event激活时只处理一次,但是在这一次中会根据信号发生的次数来决定调用多少次回调函数,而这里的回调函数就是用户最开始设定的“当信号发生时应当调用的函数了”。

你可能感兴趣的:(libevent源码学习)