码迷,mamicode.com
首页 > 其他好文 > 详细

libevent(七)信号事件监听

时间:2017-10-20 13:22:13      阅读:200      评论:0      收藏:0      [点我收藏+]

标签:watch   where   versions   sigaction   event   关联   from   pen   lease   

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. */
};

在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

 

假设我们现在向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]上的数据全部读完。

 

 

 

libevent(七)信号事件监听

标签:watch   where   versions   sigaction   event   关联   from   pen   lease   

原文地址:http://www.cnblogs.com/gattaca/p/7698661.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!