• libevent(七)信号事件监听


    libevent通过socketpair实现对信号事件的监听。

    还记得event_base吗?

    struct event_base {
        struct evsig_info sig;
        //
    };

    evsig_info结构如下:

    struct evsig_info {
        evutil_socket_t ev_signal_pair[2];  /* Socketpair used to send notifications from the signal handler */
        struct event ev_signal;             /* Event watching ev_signal_pair[1] */
        
        int ev_signal_added;                /* True if we've added the ev_signal event yet. */
        int ev_n_signals_added;             /* Count of the number of signals we're currently watching. */
        
        struct sigaction **sh_old;          /* sigaction* 指针数组,用于存放信号处理函数 */
        int sh_old_max;                     /* Size of sh_old. */
    };

    evsig_init

    在event_base初始化阶段会完成socketpair的创建。

    int
    evsig_init(struct event_base *base)
    {
        /*
         * Our signal handler is going to write to one end of the socket
         * pair to wake up our event loop.  The event loop then scans for
         * signals that got delivered.
         */
        if (evutil_socketpair(
                AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1) {
    #ifdef WIN32
            /* Make this nonfatal on win32, where sometimes people
               have localhost firewalled. */
            event_sock_warn(-1, "%s: socketpair", __func__);
    #else
            event_sock_err(1, -1, "%s: socketpair", __func__);
    #endif
            return -1;
        }
    
        evutil_make_socket_closeonexec(base->sig.ev_signal_pair[0]);
        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]);
    
        event_assign(&base->sig.ev_signal, base, base->sig.ev_signal_pair[1],
            EV_READ | EV_PERSIST, evsig_cb, base);
    
        base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL;
        event_priority_set(&base->sig.ev_signal, 0);
    
        base->evsigsel = &evsigops;
    
        return 0;
    }

    可以看到,sig.ev_signal关联了sig.ev_signal_pair[1]的读事件,并且回调函数为evsig_cb

    evsig_add

    假设我们现在向event_base添加一个信号事件signal_int

    struct event signal_int;
    event_assign(&signal_int, base, SIGINT, EV_SIGNAL|EV_PERSIST, signal_cb, &signal_int);
    event_add(&signal_int, NULL);

    event_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];
        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) {
            if (event_add(&sig->ev_signal, NULL))
                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做了两件事:

    1. 设置信号的回调函数为evsig_handler

      回调函数存放于sig.sh_old中。

    2. 将I/O事件sig.ev_signal添加到epoll中。

    整篇文章的两个主角出现了: evsig_handler、evsig_cb。

    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);
        errno = save_errno;
    #ifdef WIN32
        EVUTIL_SET_SOCKET_ERROR(socket_errno);
    #endif
    }

    这里evsig_base_fd = base->sig.ev_signal_pair[0];

    evsig_cb

    /* Callback for when the signal handler write a byte to our signaling socket */
    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);
            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) {
            if (ncaught[i])
                evmap_signal_active(base, i, ncaught[i]);
        }
        EVBASE_RELEASE_LOCK(base, th_base_lock);
    }

    这里的recv置于while中,说明每次evsig_cb运行时,会将sig.ev_signal_pair[0]上的数据全部读完。

    总结

    信号事件流程

    signal_int、sig.ev_signal

    每个事件都有个事件类型。

    signal_int的事件类型为EV_SIGNAL,说明它是一个信号事件。

    经过上述的流程之后,signal_int会被加入到信号事件队列sigmap,以及总事件队列eventqueue。

    sig.ev_signal的事件类型为EV_READ,说明它是一个I/O事件。

    经过上述的流程之后,sig.ev_signal会被加入到I/O事件队列io,以及总事件队列eventqueue。

    同时,与sig.ev_signal关联的fd会被加入epoll中。

  • 相关阅读:
    C++ 类的本质 札记
    eclipse makefile
    boost 简介
    telecom 产品分析js
    javascript 得到页面参数
    ajax 接口统一模式
    备份
    jquery 元素插入详解
    使用WebClient制作一下简单的采集器
    数据库锁机制
  • 原文地址:https://www.cnblogs.com/gattaca/p/7698661.html
Copyright © 2020-2023  润新知