libevent-signal(1)

现在已经知道,libevent有三种事件类型,分别是时钟事件,信号事件,i/o事件。今天就分析一下信号事件,下面是一个简单的信号事件demo

#include <sys/types.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <sys/stat.h>
#ifndef WIN32
#include <sys/queue.h>
#include <unistd.h>
#include <sys/time.h>
#else
#include <windows.h>
#endif
#include <signal.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <event.h>

int called = 0;

static void
signal_cb(int fd, short event, void *arg)
{
    struct event *signal = arg;

    printf("%s: got signal %dn", __func__, EVENT_SIGNAL(signal));

    if (called >= 2)
        event_del(signal);

    called++;
}

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

    /* Initalize the event library */
    struct event_base* base = event_base_new();

    /* Initalize one event */
    event_set(&signal_int, SIGINT, EV_SIGNAL|EV_PERSIST, signal_cb,
        &signal_int);
    event_base_set(base, &signal_int);

    event_add(&signal_int, NULL);

    event_base_dispatch(base);
    event_base_free(base);

    return (0);
}

  从代码看,这里event_set第二个参数是一个中断类型的信号(ctrl+c可触发),第三个参数代表这是一个信号事件并长存

  event_base_new中会调用base->evsel->init(这里先不放代码,末尾会放流程图),而这个函数会根据当前后端选择初始化函数,这里是win32_init,最终调用 evutil_socketpair(int family, int type, int protocol, int fd[2]),这个函数中用 tcp-socket将 两个socket句柄绑定到fd数组上

libevent-signal(1)

 

具体代码如下

  

int
evutil_socketpair(int family, int type, int protocol, int fd[2])
{
#ifndef WIN32
    return socketpair(family, type, protocol, fd);
#else
    /* 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.
     */
    int listener = -1;
    int connector = -1;
    int acceptor = -1;
    struct sockaddr_in listen_addr;
    struct sockaddr_in connect_addr;
    int size;
    int saved_errno = -1;

    if (protocol
#ifdef AF_UNIX
        || family != AF_UNIX
#endif
        ) {
        EVUTIL_SET_SOCKET_ERROR(WSAEAFNOSUPPORT);
        return -1;
    }
    if (!fd) {
        EVUTIL_SET_SOCKET_ERROR(WSAEINVAL);
        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(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;
    fd[1] = acceptor;

    return 0;

 abort_tidy_up_and_fail:
    saved_errno = WSAECONNABORTED;
 tidy_up_and_fail:
    if (saved_errno < 0)
        saved_errno = WSAGetLastError();
    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;
#endif
}

libevent-signal(1)

 

 

//下面另一篇再写 event_add的代码如下

 1 int
 2 event_add(struct event *ev, const struct timeval *tv)
 3 {
 4     struct event_base *base = ev->ev_base;
 5     const struct eventop *evsel = base->evsel;
 6     void *evbase = base->evbase;
 7     int res = 0;
 8 
 9     event_debug((
10          "event_add: event: %p, %s%s%scall %p",
11          ev,
12          ev->ev_events & EV_READ ? "EV_READ " : " ",
13          ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
14          tv ? "EV_TIMEOUT " : " ",
15          ev->ev_callback));
16 
17     assert(!(ev->ev_flags & ~EVLIST_ALL));
18 
19     /*
20      * prepare for timeout insertion further below, if we get a
21      * failure on any step, we should not change any state.
22      */
23     if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) 
24     {
25         if (min_heap_reserve(&base->timeheap,
26             1 + min_heap_size(&base->timeheap)) == -1)
27             return (-1);  /* ENOMEM == errno */
28     }
29 
30     if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
31         !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
32         res = evsel->add(evbase, ev);
33         if (res != -1)
34             event_queue_insert(base, ev, EVLIST_INSERTED);
35     }
36 
37     /* 
38      * we should change the timout state only if the previous event
39      * addition succeeded.
40      */
41     if (res != -1 && tv != NULL) {
42         struct timeval now;
43 
44         /* 
45          * we already reserved memory above for the case where we
46          * are not replacing an exisiting timeout.
47          */
48         if (ev->ev_flags & EVLIST_TIMEOUT)
49             event_queue_remove(base, ev, EVLIST_TIMEOUT);
50 
51         /* Check if it is active due to a timeout.  Rescheduling
52          * this timeout before the callback can be executed
53          * removes it from the active list. */
54         if ((ev->ev_flags & EVLIST_ACTIVE) &&
55             (ev->ev_res & EV_TIMEOUT)) {
56             /* See if we are just active executing this
57              * event in a loop
58              */
59             if (ev->ev_ncalls && ev->ev_pncalls) {
60                 /* Abort loop */
61                 *ev->ev_pncalls = 0;
62             }
63             
64             event_queue_remove(base, ev, EVLIST_ACTIVE);
65         }
66 
67         gettime(base, &now);
68         evutil_timeradd(&now, tv, &ev->ev_timeout);
69 
70         event_debug((
71              "event_add: timeout in %ld seconds, call %p",
72              tv->tv_sec, ev->ev_callback));
73 
74         event_queue_insert(base, ev, EVLIST_TIMEOUT);
75     }
76 
77     return (res);
78 }

  第五行,代表的是当前系统所支持的后端模式,base->evsel和base->evbase先不要纠结,都是用在一个地方

  第23行关于最小堆的逻辑先跳过

  第30行,当事件ev不在已注册或者激活链表中,则调用evbase注册事件,这里的ev_events,ev_flags分别代表event关注的事件类型与当前的状态

ev_events有四种类型

  I/O事件: EV_WRITE和EV_READ
  定时事件:EV_TIMEOUT
  信号:    EV_SIGNAL
  辅助选项:EV_PERSIST,表明是一个永久事件

ev_flags有以下几种状态

#define EVLIST_TIMEOUT 0x01 // event在time堆中  
#define EVLIST_INSERTED 0x02 // event在已注册事件链表中  
#define EVLIST_SIGNAL 0x04 // 未见使用  
#define EVLIST_ACTIVE 0x08 // event在激活链表中  
#define EVLIST_INTERNAL 0x10 // 内部使用标记  
#define EVLIST_INIT     0x80 // event已被初始化  

  重点分析一下res = evsel->add(evbase, ev);我所在为win32平台,实际调用的是int win32_insert(void *op, struct event *ev)

 1 int
 2 win32_insert(void *op, struct event *ev)
 3 {
 4     struct win32op *win32op = op;
 5     struct event_entry *ent;
 6 
 7     if (ev->ev_events & EV_SIGNAL) {
 8         if (win32op->signals_are_broken)
 9             return (-1);
10         return (evsignal_add(ev));
11     }
12     if (!(ev->ev_events & (EV_READ|EV_WRITE)))
13         return (0);
14     ent = get_event_entry(win32op, ev->ev_fd, 1);
15     if (!ent)
16         return (-1); /* out of memory */
17 
18     event_debug(("%s: adding event for %d", __func__, (int)ev->ev_fd));
19     if (ev->ev_events & EV_READ) {
20         if (do_fd_set(win32op, ent, 1)<0)
21             return (-1);
22         ent->read_event = ev;
23     }
24     if (ev->ev_events & EV_WRITE) {
25         if (do_fd_set(win32op, ent, 0)<0)
26             return (-1);
27         ent->write_event = ev;
28     }
29     return (0);
30 }

原文链接: https://www.cnblogs.com/wangshaowei/p/8552664.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    libevent-signal(1)

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/392036

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月31日 上午10:53
下一篇 2023年3月31日 上午10:53

相关推荐