您的位置:首页 > 其它

[置顶] (十一)信号事件的管理

2017-08-30 22:12 246 查看

前言

在上一小节中我们主要讲解了信号事件是如何合并到多路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发数据从而触发读事件,导致该事件被激活,最后被调度。

接下来,我们会取看一看关于定时事件方面的管理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: