Chinaunix首页 | 论坛 | 博客
  • 博客访问: 543777
  • 博文数量: 51
  • 博客积分: 345
  • 博客等级: 民兵
  • 技术积分: 534
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-21 12:02
个人简介

文章分类

全部博文(51)

文章存档

2023年(2)

2022年(1)

2021年(7)

2020年(10)

2019年(2)

2016年(20)

2015年(5)

2014年(1)

2011年(3)

我的朋友

分类: C/C++

2016-05-17 11:23:29

Libevent 提供了一组API,使用者可以很方便的调用文件描述符对应的回调函数,另外还支持对signal的回调;下面来看一下对signal回调的实现;
源码版本:libevent-2.0.21-stable
先来看个例子:
signal_test.c
static void  signal_cb(evutil_socket_t fd, short event, void *arg)
{
    struct event *signal = arg;
    printf("%s: got signal %d\n", __func__, EVENT_SIGNAL(signal));
    if (called >= 2)
        event_del(signal);
    called++;
}

int  main(int argc, char **argv)
{
    struct event signal_int;
    struct event_base* base;

    /* Initalize the event library */
    base = event_base_new();    //运行期间总的管理实体;

    /* Initalize one event */
    event_assign(&signal_int, base, SIGINT, EV_SIGNAL|EV_PERSIST, signal_cb,
        &signal_int);    //libevent的管理结构为struct event, 这里初始化一个signal对应的管理结构,主要内容为信号SIGINT,及回调signal_cb

    event_add(&signal_int, NULL);    //将signal管理结构加入到base;
    event_base_dispatch(base);    //进入监听转台,若期间接收到信号,则调用signal_cb;
    event_base_free(base);
    return (0);
}


libevent内部对signal处理的初始化:

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) {    //这里会创建一对socket,其用处为当发生信号时,发送通知消息;
#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_pair[1]的处理函数evsig_cb

    base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL;
    event_priority_set(&base->sig.ev_signal, 0);

    base->evsigsel = &evsigops;

    return 0;
}

初始化关键是evutil_socketpair,它创建了一对unix socket:

int  evutil_ersatz_socketpair(int family, int type, int protocol,
    evutil_socket_t fd[2])
{
    /* This code is originally from Tor.  Used with permission. */

    /* This socketpair does not work when localhost is down. So
     * it's really not the same thing at all. But it's close enough
     * for now, and really, when localhost is down sometimes, we
     * have other problems too.
     */
#ifdef WIN32
#define ERR(e) WSA##e
#else
#define ERR(e) e
#endif
    evutil_socket_t listener = -1;
    evutil_socket_t connector = -1;
    evutil_socket_t acceptor = -1;
    struct sockaddr_in listen_addr;
    struct sockaddr_in connect_addr;
    ev_socklen_t size;
    int saved_errno = -1;

    if (protocol
        || (family != AF_INET
#ifdef AF_UNIX
            && family != AF_UNIX
#endif
        )) {
        EVUTIL_SET_SOCKET_ERROR(ERR(EAFNOSUPPORT));
        return -1;
    }
    if (!fd) {
        EVUTIL_SET_SOCKET_ERROR(ERR(EINVAL));
        return -1;
    }

    listener = socket(AF_INET, type, 0);
    if (listener < 0)
        return -1;
    memset(&listen_addr, 0, sizeof(listen_addr));
    listen_addr.sin_family = AF_INET;
    listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);    //绑定一个回环地址
    listen_addr.sin_port = 0;    /* kernel chooses port.     */
    if (bind(listener, (struct sockaddr *) &listen_addr, sizeof (listen_addr))
        == -1)
        goto tidy_up_and_fail;
    if (listen(listener, 1) == -1)
        goto tidy_up_and_fail;

    connector = socket(AF_INET, type, 0);    //创建连接socket
    if (connector < 0)
        goto tidy_up_and_fail;
    /* We want to find out the port number to connect to.  */
    size = sizeof(connect_addr);
    if (getsockname(listener, (struct sockaddr *) &connect_addr, &size) == -1)
        goto tidy_up_and_fail;
    if (size != sizeof (connect_addr))
        goto abort_tidy_up_and_fail;
    if (connect(connector, (struct sockaddr *) &connect_addr,    //连接到回环地址&端口
                sizeof(connect_addr)) == -1)
        goto tidy_up_and_fail;

    size = sizeof(listen_addr);
    acceptor = accept(listener, (struct sockaddr *) &listen_addr, &size);  // 获取连接成功的socket,
    if (acceptor < 0)
        goto tidy_up_and_fail;
    if (size != sizeof(listen_addr))
        goto abort_tidy_up_and_fail;
    evutil_closesocket(listener);
    /* Now check we are talking to ourself by matching port and host on the
       two sockets.     */
    if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -1)
        goto tidy_up_and_fail;
    if (size != sizeof (connect_addr)
        || listen_addr.sin_family != connect_addr.sin_family
        || listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr
        || listen_addr.sin_port != connect_addr.sin_port)
        goto abort_tidy_up_and_fail;
    fd[0] = connector;    //作为写socket
    fd[1] = acceptor;    //作为读socket;

    return 0;

 abort_tidy_up_and_fail:
    saved_errno = ERR(ECONNABORTED);
 tidy_up_and_fail:
    if (saved_errno < 0)
        saved_errno = EVUTIL_SOCKET_ERROR();
    if (listener != -1)
        evutil_closesocket(listener);
    if (connector != -1)
        evutil_closesocket(connector);
    if (acceptor != -1)
        evutil_closesocket(acceptor);

    EVUTIL_SET_SOCKET_ERROR(saved_errno);
    return -1;
#undef ERR
}

自此创建一对socket,fd[0] 用于写;fd[1] 用于读;只要通过fd[0]写入数据,就会触发fd[1]的回调,最终调用函数evsig_cb;
那么合适触发fd[0]的写动作呐,要看信号事件的添加了,由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];    //用于写的fd;
    EVSIGBASE_UNLOCK();

    event_debug(("%s: %d: changing signal handler", __func__, (int)evsignal));
    if (_evsig_set_handler(base, (int)evsignal, evsig_handler) == -1) {    //设置信号处理函数,当信号发生时调用evsig_handler
        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_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);     //向evsig_base_fd写入了信号值,这样就会触发fd[1]的读动作,回调evsig_cb,将该信号对应的struct event实例添加到base->activequeues队列;文件描述法由select,poll,epoll机制监控;
    errno = save_errno;
#ifdef WIN32
    EVUTIL_SET_SOCKET_ERROR(socket_errno);
#endif
}

阅读(1638) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~