zoukankan      html  css  js  c++  java
  • libeventsignal(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 %d\n", __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数组上

    具体代码如下

      

    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
    }

     

    //下面另一篇再写 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 }
  • 相关阅读:
    ElasticSearch已经配置好ik分词和mmseg分词(转)
    Java:Cookie实现记住用户名、密码
    (转)10大H5前端框架
    msysGit在GitHub代码托管
    mongodb 语句和SQL语句对应(SQL to Aggregation Mapping Chart)
    centos 7 安装redis
    mac下idea卡顿问题解决
    在linux系统中,使用tomcat的shutdown.sh脚本停止应用,但是进程还在的解决办法
    centOS 7.4 安装配置jdk1.8
    CentOS6 在线安装PostgreSQL10
  • 原文地址:https://www.cnblogs.com/wangshaowei/p/8552664.html
Copyright © 2011-2022 走看看