libevent源码分析(细节版)1

来源:互联网 发布:手机淘宝怎么退出 编辑:程序博客网 时间:2024/06/05 11:04

从sample/hello-world.c来剖析libevent源码

我准备从libevent源码中最简单的sample开始剖析源码。目前思路是dfs的方法,即碰到一个不够底层的封装,就去深究它,直到彻底搞懂它,然后返回继续。

先上hello-world.c的源码

#include <string.h>#include <errno.h>#include <stdio.h>#include <signal.h>#ifndef _WIN32#include <netinet/in.h># ifdef _XOPEN_SOURCE_EXTENDED#  include <arpa/inet.h># endif#include <sys/socket.h>#endif#include <event2/bufferevent.h>#include <event2/buffer.h>#include <event2/listener.h>#include <event2/util.h>#include <event2/event.h>static const char MESSAGE[] = "Hello, World!\n";static const int PORT = 9995;static void listener_cb(struct evconnlistener *, evutil_socket_t,    struct sockaddr *, int socklen, void *);static void conn_writecb(struct bufferevent *, void *);static void conn_eventcb(struct bufferevent *, short, void *);static void signal_cb(evutil_socket_t, short, void *);intmain(int argc, char **argv){    struct event_base *base;    struct evconnlistener *listener;    struct event *signal_event;    struct sockaddr_in sin;#ifdef _WIN32    WSADATA wsa_data;    WSAStartup(0x0201, &wsa_data);#endif    base = event_base_new();    if (!base) {        fprintf(stderr, "Could not initialize libevent!\n");        return 1;    }    memset(&sin, 0, sizeof(sin));    sin.sin_family = AF_INET;    sin.sin_port = htons(PORT);    listener = evconnlistener_new_bind(base, listener_cb, (void *)base,        LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,        (struct sockaddr*)&sin,        sizeof(sin));    if (!listener) {        fprintf(stderr, "Could not create a listener!\n");        return 1;    }    signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);    if (!signal_event || event_add(signal_event, NULL)<0) {        fprintf(stderr, "Could not create/add a signal event!\n");        return 1;    }    event_base_dispatch(base);    evconnlistener_free(listener);    event_free(signal_event);    event_base_free(base);    printf("done\n");    return 0;}static voidlistener_cb(struct evconnlistener *listener, evutil_socket_t fd,    struct sockaddr *sa, int socklen, void *user_data){    struct event_base *base = user_data;    struct bufferevent *bev;    bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);    if (!bev) {        fprintf(stderr, "Error constructing bufferevent!");        event_base_loopbreak(base);        return;    }    bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);    bufferevent_enable(bev, EV_WRITE);    bufferevent_disable(bev, EV_READ);    bufferevent_write(bev, MESSAGE, strlen(MESSAGE));}static voidconn_writecb(struct bufferevent *bev, void *user_data){    struct evbuffer *output = bufferevent_get_output(bev);    if (evbuffer_get_length(output) == 0) {        printf("flushed answer\n");        bufferevent_free(bev);    }}static voidconn_eventcb(struct bufferevent *bev, short events, void *user_data){    if (events & BEV_EVENT_EOF) {        printf("Connection closed.\n");    } else if (events & BEV_EVENT_ERROR) {        printf("Got an error on the connection: %s\n",            strerror(errno));/*XXX win32*/    }    /* None of the other events can happen here, since we haven't enabled     * timeouts */    bufferevent_free(bev);}static voidsignal_cb(evutil_socket_t sig, short events, void *user_data){    struct event_base *base = user_data;    struct timeval delay = { 2, 0 };    printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");    event_base_loopexit(base, &delay);}

event_base

第一句是一个event_base结构体

来看下这个结构体的定义

struct event_base {   const struct eventop *evsel;   void *evbase;    int event_count;  /* counts number of total events */   int event_count_active; /* counts number of active events */   int event_gotterm;  /* Set to terminate loop */   int event_break;  /* Set to terminate loop immediately */   /* active event management */   struct event_list **activequeues;   int nactivequeues;   /* signal handling info */   struct evsignal_info sig;   struct event_list eventqueue;   struct timeval event_tv;   struct min_heap timeheap;   struct timeval tv_cache;  };  

struct event_list:

这个的定义在event_struct.h中 TAILQ_HEAD (event_list, event); 。其中TAILQ_HEAD是一个FreeBSD提供的一个宏,或者说一种尾队列。所以event_list就是一种队列类型,其中的元素的的类型是event。更详细就是

struct event_list {  struct event *tqh_first;  struct event **tqh_last;};

提示:libevent作者此处是用了FREEBSD提供的队列,具体实现在/usr/include/sys/queue.h里面,但是作者copy了一份到libevent/compat/sys/queue.h里面

从网上可以搜搜这个队列的实现,几十年前的上古C代码,大概就是怎么用C实现C++的模板类。

void *evbase:

evconnlistener_new_bind

这个函数实际包括event_new()和event_add(),先说event_add(struct event *ev,const struct timeval *tv)

intevent_add(struct event *ev, const struct timeval *tv){    int res;    if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) {        event_warnx("%s: event has no event_base set.", __func__);        return -1;    }    EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock);    res = event_add_nolock_(ev, tv, 0);    EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);    return (res);}

EVUTIL_FAILURE_CHECK(!ev->ev_base) 这个debug用的宏,不用管

EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);展开后是 EVLOCK(ev->ev_base->th_base_lock,0);再展开是

if(ev->ev_base->th_base_lock)    evthread_lock_fns_.lock(0,ev->ev_base->th_base_lock)

其中evthread_lock_fns_是一个全局变量,在evthread.c里面声明的,其类型是

struct evthread_lock_callbacks {    /** The current version of the locking API.  Set this to     * EVTHREAD_LOCK_API_VERSION */    int lock_api_version;    /** Which kinds of locks does this version of the locking API     * support?  A bitfield of EVTHREAD_LOCKTYPE_RECURSIVE and     * EVTHREAD_LOCKTYPE_READWRITE.     *     * (Note that RECURSIVE locks are currently mandatory, and     * READWRITE locks are not currently used.)     **/    unsigned supported_locktypes;    /** Function to allocate and initialize new lock of type 'locktype'.     * Returns NULL on failure. */    void *(*alloc)(unsigned locktype);    /** Funtion to release all storage held in 'lock', which was created     * with type 'locktype'. */    void (*free)(void *lock, unsigned locktype);    /** Acquire an already-allocated lock at 'lock' with mode 'mode'.     * Returns 0 on success, and nonzero on failure. */    int (*lock)(unsigned mode, void *lock);    /** Release a lock at 'lock' using mode 'mode'.  Returns 0 on success,     * and nonzero on failure. */    int (*unlock)(unsigned mode, void *lock);};

好吧,其实发现深究到这,这就是一个坑,先跳过这个锁吧,下次再来看。这个地方应该就是上锁解锁,保持队列的线程安全。主要还是看res = event_add_nolock_(ev, tv, 0);这个函数的源码在event.c里面,但是我这个版本的这个函数的实现,已经不适合阅读了。这里只简单的注释一下(因为我也没完全看懂)

intevent_add_nolock_(struct event *ev, const struct timeval *tv,    int tv_is_absolute){    struct event_base *base = ev->ev_base;    int res = 0;    int notify = 0;    EVENT_BASE_ASSERT_LOCKED(base);    event_debug_assert_is_setup_(ev);    event_debug((         "event_add: event: %p (fd "EV_SOCK_FMT"), %s%s%s%scall %p",         ev,         EV_SOCK_ARG(ev->ev_fd),         ev->ev_events & EV_READ ? "EV_READ " : " ",         ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",         ev->ev_events & EV_CLOSED ? "EV_CLOSED " : " ",         tv ? "EV_TIMEOUT " : " ",         ev->ev_callback));    EVUTIL_ASSERT(!(ev->ev_flags & ~EVLIST_ALL));    if (ev->ev_flags & EVLIST_FINALIZING) {        /* XXXX debug */        return (-1);    }    /*     * prepare for timeout insertion further below, if we get a     * failure on any step, we should not change any state.     */   //如果添加的新事件是定时器,则为其在时间堆上预留一个位子    if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {        if (min_heap_reserve_(&base->timeheap,            1 + min_heap_size_(&base->timeheap)) == -1)            return (-1);  /* ENOMEM == errno */    }    /* If the main thread is currently executing a signal event's     * callback, and we are not the main thread, then we want to wait     * until the callback is done before we mess with the event, or else     * we can race on ev_ncalls and ev_pncalls below. */#ifndef EVENT__DISABLE_THREAD_SUPPORT    if (base->current_event == event_to_event_callback(ev) &&        (ev->ev_events & EV_SIGNAL)        && !EVBASE_IN_THREAD(base)) {        ++base->current_event_waiters;        EVTHREAD_COND_WAIT(base->current_event_cond, base->th_base_lock);    }#endif//这个判断条件大致意思是 如果是读写已经信号事件并且这个事件没有入队过    if ((ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL)) &&        !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE|EVLIST_ACTIVE_LATER)))    {   //此处是将文件描述符或者信号之类的句柄统一化,统一映射为一种int型的数据,这个映射函数接下来会解析        if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED))            res = evmap_io_add_(base, ev->ev_fd, ev);        else if (ev->ev_events & EV_SIGNAL)            res = evmap_signal_add_(base, (int)ev->ev_fd, ev);        if (res != -1)            event_queue_insert_inserted(base, ev);//将新事件加入队列        if (res == 1)         {            /* evmap says we need to notify the main thread. */            notify = 1;            res = 0;        }    }    /*     * we should change the timeout state only if the previous event     * addition succeeded.     */    if (res != -1 && tv != NULL) {        struct timeval now;        int common_timeout;#ifdef USE_REINSERT_TIMEOUT        int was_common;        int old_timeout_idx;#endif        /*         * for persistent timeout events, we remember the         * timeout value and re-add the event.         *         * If tv_is_absolute, this was already set.         */        if (ev->ev_closure == EV_CLOSURE_EVENT_PERSIST && !tv_is_absolute)            ev->ev_io_timeout = *tv;#ifndef USE_REINSERT_TIMEOUT        if (ev->ev_flags & EVLIST_TIMEOUT) {            event_queue_remove_timeout(base, ev);        }//以上是对定时事件的处理#endif        //读到此处其实我有一个地方一直没搞懂,就是所谓的三个事件队列到底对应什么,i/o,signal,active队列。其中有EVLIST_INSERTED,EVLIST_ACTIVE.        //事件何时进入inserted队列,何时进入active队列,什么是active状态,这个状态说明什么,在何时到达这个状态???????        /* Check if it is active due to a timeout.  Rescheduling         * this timeout before the callback can be executed         * removes it from the active list. */        if ((ev->ev_flags & EVLIST_ACTIVE) &&            (ev->ev_res & EV_TIMEOUT)) {            if (ev->ev_events & EV_SIGNAL) {                /* See if we are just active executing                 * this event in a loop                 */                if (ev->ev_ncalls && ev->ev_pncalls) {                    /* Abort loop */                    *ev->ev_pncalls = 0;                }            }            event_queue_remove_active(base, event_to_event_callback(ev));        }        gettime(base, &now);        common_timeout = is_common_timeout(tv, base);#ifdef USE_REINSERT_TIMEOUT        was_common = is_common_timeout(&ev->ev_timeout, base);        old_timeout_idx = COMMON_TIMEOUT_IDX(&ev->ev_timeout);#endif        if (tv_is_absolute) {//根据定时器类型,判断加入通用定时器队列还是插入时间堆            ev->ev_timeout = *tv;        } else if (common_timeout) {            struct timeval tmp = *tv;            tmp.tv_usec &= MICROSECONDS_MASK;            evutil_timeradd(&now, &tmp, &ev->ev_timeout);            ev->ev_timeout.tv_usec |=                (tv->tv_usec & ~MICROSECONDS_MASK);        } else {            evutil_timeradd(&now, tv, &ev->ev_timeout);        }        event_debug((             "event_add: event %p, timeout in %d seconds %d useconds, call %p",             ev, (int)tv->tv_sec, (int)tv->tv_usec, ev->ev_callback));#ifdef USE_REINSERT_TIMEOUT        event_queue_reinsert_timeout(base, ev, was_common, common_timeout, old_timeout_idx);#else        event_queue_insert_timeout(base, ev);#endif        if (common_timeout) {            struct common_timeout_list *ctl =                get_common_timeout_list(base, &ev->ev_timeout);            if (ev == TAILQ_FIRST(&ctl->events)) {                common_timeout_schedule(ctl, &now, ev);            }        } else {            struct event* top = NULL;            /* See if the earliest timeout is now earlier than it             * was before: if so, we will need to tell the main             * thread to wake up earlier than it would otherwise.             * We double check the timeout of the top element to             * handle time distortions due to system suspension.             */            if (min_heap_elt_is_top_(ev))                notify = 1;            else if ((top = min_heap_top_(&base->timeheap)) != NULL &&                     evutil_timercmp(&top->ev_timeout, &now, <))                notify = 1;        }    }    /* if we are not in the right thread, we need to wake up the loop */    if (res != -1 && notify && EVBASE_NEED_NOTIFY(base))        evthread_notify_base(base);    event_debug_note_add_(ev);    return (res);}

以上event_add_nolock_主要有三个函数,
evmap_io_add 该函数将IO事件添加到多路分发器中,并将对应的事件处理器添加到IO队列中,同时建立IO事件和IO事件处理器的映射关系
evmap_signal_add 同上
event_queue_insert 该函数将事件处理器添加到各种事件队列中
(《Linux高性能服务器编程》游双 著)

先写到这里吧,马上开学了,回学校了继续研究。

2017/8/25

原创粉丝点击