(十一)信号事件的管理

本文深入探讨信号事件的管理,包括信号事件的注册和激活过程。在注册部分,详细解析了如何将信号事件添加到事件链表中,并解释了两个不同注册操作的区别。激活信号事件的部分阐述了信号如何触发读socket事件,进而激活相应的信号处理。文章以一个小结收尾,为后续的定时事件管理铺垫。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

在上一小节中我们主要讲解了信号事件是如何合并到多路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);
}

该函数主要逻辑就是:

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

这里初看的话可能你会觉得有点迷糊,因为有两个注册事件的操作,而且不一样。
- 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发数据从而触发读事件,导致该事件被激活,最后被调度。
接下来,我们会取看一看关于定时事件方面的管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值