(十一)信号事件的管理

xiaoxiao2021-02-27  104

前言

在上一小节中我们主要讲解了信号事件是如何合并到多路I/O复用机制中的以及信号事件的初始化。在本小节中,我们将看到有关信号事件的主要操作。

信号事件的注册

前面我们看到了信号事件是在何时何地如何被初始化的,一个事件无非就是初始化、注册、激活、回调、注销这几个重要的操作。接下来我们看看信号事件注册相关的,即evsignal_add函数,它在signal.c文件中定义。

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__); /* * 这里是个宏函数,在event.h中定义如下 * #define EVENT_SIGNAL(ev) (int)(ev)->ev_fd * 作用就是取得信号值 */ evsignal = EVENT_SIGNAL(ev); assert(evsignal >= 0 && evsignal < NSIG); //没有事件注册到该evsignal信号上 if (TAILQ_EMPTY(&sig->evsigevents[evsignal])) { event_debug(("%s: %p: changing signal handler", __func__, ev)); /* 如有必要,扩大给sh_old分配的内存 * 给该信号设置新的信号捕捉函数 */ 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 */ //将信号事件添加到信号evsignal对应的事件链表中 TAILQ_INSERT_TAIL(&sig->evsigevents[evsignal], ev, ev_signal_next); return (0); }

该函数主要逻辑就是:

判断信号有无对应的注册事件链表 无,判断是否需要重新分配内存,接着便注册该事件有,直接将该事件添加到信号对应的事件链表中

这里初看的话可能你会觉得有点迷糊,因为有两个注册事件的操作,而且不一样。 - event_add注册的事件,是到时候信号事件被激活了,真正执行的操作。 - TAILQ_INSERT_TAIL这个执行的操作虽然也是将信号事件加入链表中,但是是不同的链表。它是收到信号之后,会执行的操作,可以看到_evsignal_set_handler设置的都是evsignal_handler捕捉函数,不管是哪个信号。等下我们会讲这个函数。

总的来说,evsignal_handler,主要是执行写socket这边写数据触发读socket事件,然后多路I/O机制就可以成功的知道信号发生了,从而将event_add注册的信号事件激活。 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; }

里面完成的就主要是注册信号捕捉函数以及给读socket发送数据。这样就可以理解前面那个可能导致你迷惑的知识了。

激活信号事件

我们最后再来看看是如何激活一个信号事件的:

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; /* * NSIG是个宏定义,代码支持的信号最大值 * 遍历该信号的事件链表 */ for (i = 1; i < NSIG; ++i) { ncalls = sig->evsigcaught[i]; //ncalls代表信号接收到的次数,为0则不需要进行激活了 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); } } }

小结

关于信号事件管理部分,还有注销等操作,这些和注册大致相同,就不一一讲解了。主要是要理解socket pair以及它们各自对应的事件是怎样的。关于写socket这边,信号发生之后,会调用send函数给读socket发数据从而触发读事件,导致该事件被激活,最后被调度。 接下来,我们会取看一看关于定时事件方面的管理。

转载请注明原文地址: https://www.6miu.com/read-16806.html

最新回复(0)