libevent的集成信号处理

本节主要介绍libevent对signal事件的具体处理框架,包括事件注册,删除和socket pair通知机制,以及是如何将Signal事件集成到事件主循环之中的。
1.继承策略–使用socket pair
Socket pair就是一个socket对,包含两个socket,一个读socket,一个写socket。
工作方式:
线程A-> W -> Write socket(经由Socket pair) -> Read socket -> R -> libevent线程
关于socket的实现方法:
(1)开始
(2)创建监听socket
(3)创建一个连接socket——-sock1
(4)调用connect()连接到监听socket监听的端口
(5)调用accept()取得连接成功后返回的socket——-sock2
(6)将sock1作为写socket,sock2作为读socket;返回
(7)结束
关于非Win32环境下的代码,流程可以与上文对应

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);
    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);
    if (acceptor < 0)
        goto tidy_up_and_fail;
    if (size != sizeof(listen_addr))
        goto abort_tidy_up_and_fail;
    evutil_closesocket();
    /* 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;
    fd[1] = acceptor;

    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
}

2.集成到事件主循环—通知event_base
为socket pair的读socket在libevent的event_base实例上注册一个persist的读事件。
这样当socket写入数据时,读socket就会得到通知,触发读事件,从而event_base就能得到相应的通知了。

完整的处理框架如下所示:

这里写图片描述

3.evsignal_info结构体
书上写的是evsignal_info,但是我看源代码找到的是evsig_info这个结构体

struct evsig_info {
    /* Event watching ev_signal_pair[1] */
    struct event ev_signal;
    /* Socketpair used to send notifications from the signal handler */
    evutil_socket_t ev_signal_pair[2];
    /* True iff we've added the ev_signal event yet. */
    int ev_signal_added;
    /* Count of the number of signals we're currently watching. */
    int ev_n_signals_added;

    /* Array of previous signal handler objects before Libevent started
     * messing with them.  Used to restore old signal handlers. */
#ifdef _EVENT_HAVE_SIGACTION
    struct sigaction **sh_old;
#else
    ev_sighandler_t **sh_old;
#endif
    /* Size of sh_old. */
    int sh_old_max;
};

4.注册,注销signal事件
注册signal事件是通过evsignal_add(struct event *ev)函数完成的,libevent对所有的信号注册同一个处理函数evsignal_handle()

注册流程
(1)取得ev要注册到的信号signo
(2)如果信号signo为被注册,那么就为signo注册信号处理函数evsignal_handle();
(3)如果事件ev_signal还没注册,就注册它
(4)将事件ev添加到signo的event链表中

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
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值