libevent源码解析(一)核心数据结构

一. 前言

  Libevent是一个轻量级的开源高性能网络库,对于学习网络编程、学习C语言等都有着很大的帮助。Libevent 有几个显著的亮点:事件驱动(event-driven),高性能;轻量级,专注于网络,不如ACE那么臃肿庞大;源代码相当精炼、易读;跨平台,支持Windows、Linux、BSD和Mac Os;支持多种I/O多路复用技术, epoll、poll、dev/poll、select和kqueue等;支持I/O,定时器和信号等事件;注册事件优先级。
  学习libevent库的时候,发现网上有很多优秀的文章,如张亮大神的libevent源码深度剖析系列。然而,他们的文章大多是多年以前的(甚至接近十年前),libevent中数据结构、框架结构、库的功能等等都有了不同程度的改动,所以在此自己边学习记录边解析新版的libevent的源码。我解析的是最新的2.18版本的libevent库。
  关于libevent的使用不用多说,网上一搜一大把。主要就是一个异步网络库,可以注册IO,信号等不同的事件。可以参考libevent源码剖析一到四篇的内容以及其他优秀博客加以了解。本文从核心数据结构入手,首先了解最基础的数据结构相关内容。

二. event

   Libevent是基于事件驱动(event-driven)的,从名字也可以看到event是整个库的核心。event就是Reactor框架中的事件处理程序组件;它提供了函数接口,供Reactor在事件发生时调用,以执行相应的事件处理,通常它会绑定一个有效的句柄。
   event结构在event.h中申明,在event_struct.h中定义。具体结构如下:

struct event {
    struct event_callback ev_evcallback;  /*回调函数结构体*/

    /* for managing timeouts 
     * 如果是timeout事件,它们是event在小根堆中的索引,
     * libevent使用小根堆来管理定时事件
     */
    union {
        TAILQ_ENTRY(event) ev_next_with_common_timeout;
        int min_heap_idx;
    } ev_timeout_pos;
    evutil_socket_t ev_fd;  /*对于I/O事件,是绑定的文件描述符;对于signal事件,是绑定的信号*/

    short ev_events;    /*event关注的事件类型*/
    short ev_res;       /* 记录了当前激活事件的类型result passed to event callback */

    struct event_base *ev_base; /*该事件所属的反应堆实例,这是一个event_base结构体*/

    union {
        /* used for io events */
        struct {
            LIST_ENTRY (event) ev_io_next;
            struct timeval ev_timeout;
        } ev_io;

        /* used by signal events */
        struct {
            LIST_ENTRY (event) ev_signal_next;
            short ev_ncalls;    /*事件就绪执行时,调用ev_callback的次数,通常为1*/
            /* Allows deletes in callback 指针,通常指向ev_ncalls或者为NULL*/
            short *ev_pncalls;
        } ev_signal;
    } ev_;    /*针对不同类型的事件设置链表记录*/


    struct timeval ev_timeout;  /*timeout事件的超市值*/
};

这里用到了event_callback,该结构体如下所示:

struct event_callback {
    TAILQ_ENTRY(event_callback) evcb_active_next;
    short evcb_flags;
    ev_uint8_t evcb_pri;    /* smaller numbers are higher priority */
    ev_uint8_t evcb_closure;
    /* allows us to adopt for different types of events */
        union {
        void (*evcb_callback)(evutil_socket_t, short, void *);
        void (*evcb_selfcb)(struct event_callback *, void *);
        void (*evcb_evfinalize)(struct event *, void *);
        void (*evcb_cbfinalize)(struct event_callback *, void *);
    } evcb_cb_union;
    void *evcb_arg;
};

简单的说,该结构体定义了几种不同的而回调函数联合,用于不同的情况。
由上可见,event事件主要包括IO,信号,以及超时三种,在该头文件中定义了以下几种类型:

#define EVLIST_TIMEOUT      0x01
#define EVLIST_INSERTED     0x02
#define EVLIST_SIGNAL       0x04
#define EVLIST_ACTIVE       0x08
#define EVLIST_INTERNAL     0x10
#define EVLIST_ACTIVE_LATER 0x20
#define EVLIST_FINALIZING   0x40
#define EVLIST_INIT     0x80

#define EVLIST_ALL          0xff

在上文中,我已经对event结构体中每个部分做了简要的说明,在event.h中,有详细的注释说明该结构体的作用以及函数接口,如下所示:

/**
 * @struct event
 *
 * Structure to represent a single event.
 *
 * An event can have some underlying condition it represents: a socket
 * becoming readable or writeable (or both), or a signal becoming raised.
 * (An event that represents no underlying condition is still useful: you
 * can use one to implement a timer, or to communicate between threads.)
 *
 * Generally, you can create events with event_new(), then make them
 * pending with event_add().  As your event_base runs, it will run the
 * callbacks of an events whose conditions are triggered.  When you no
 * longer want the event, free it with event_free().
 *
 * In more depth:
 *
 * An event may be "pending" (one whose condition we are watching),
 * "active" (one whose condition has triggered and whose callback is about
 * to run), neither, or both.  Events come into existence via
 * event_assign() or event_new(), and are then neither active nor pending.
 *
 * To make an event pending, pass it to event_add().  When doing so, you
 * can also set a timeout for the event.
 *
 * Events become active during an event_base_loop() call when either their
 * condition has triggered, or when their timeout has elapsed.  You can
 * also activate an event manually using event_active().  The even_base
 * loop will run the callbacks of active events; after it has done so, it
 * marks them as no longer active.
 *
 * You can make an event non-pending by passing it to event_del().  This
 * also makes the event non-active.
 *
 * Events can be "persistent" or "non-persistent".  A non-persistent event
 * becomes non-pending as soon as it is triggered: thus, it only runs at
 * most once per call to event_add().  A persistent event remains pending
 * even when it becomes active: you'll need to event_del() it manually in
 * order to make it non-pending.  When a persistent event with a timeout
 * becomes active, its timeout is reset: this means you can use persistent
 * events to implement periodic timeouts.
 *
 * This should be treated as an opaque structure; you should never read or
 * write any of its fields directly.  For backward compatibility with old
 * code, it is defined in the event2/event_struct.h header; including this
 * header may make your code incompatible with other versions of Libevent.
 *
 * @see event_new(), event_free(), event_assign(), event_get_assignment(),
 *    event_add(), event_del(), event_active(), event_pending(),
 *    event_get_fd(), event_get_base(), event_get_events(),
 *    event_get_callback(), event_get_callback_arg(),
 *    event_priority_set()
 */

三.event-base

   在event结构中,有一个指向event_base的指针,所以这里我们看看event_base结构体。该结构体定义于event_internal.h中,具体代码如下。

struct event_base {
    /** 事件库对应的函数指针 Function pointers and other data to describe this event_base's
     * backend. */
    const struct eventop *evsel;
    /** Pointer to backend-specific data. */
    void *evbase;

    /** 事件发生的变化存储在这里 List of changes to tell backend about at next dispatch.  Only used
     * by the O(1) backends. */
    struct event_changelist changelist;

    /** 针对信号事件的函数指针存放结构 Function pointers used to describe the backend that this event_base
     * uses for signals */
    const struct eventop *evsigsel;
    /** Data to implement the common signal handler code. */
    struct evsig_info sig;

    /*一些计数数据*/
    /** Number of virtual events */
    int virtual_event_count;
    /** Maximum number of virtual events active */
    int virtual_event_count_max;
    /** Number of total events added to this event_base */
    int event_count;
    /** Maximum number of total events added to this event_base */
    int event_count_max;
    /** Number of total events active in this event_base */
    int event_count_active;
    /** Maximum number of total events active in this event_base */
    int event_count_active_max;

    /*事件循环控制参数*/
    /** Set if we should terminate the loop once we're done processing
     * events. */
    int event_gotterm;
    /** Set if we should terminate the loop immediately */
    int event_break;
    /** Set if we should start a new instance of the loop immediately. */
    int event_continue;

    /** 优先级 The currently running priority of events */
    int event_running_priority;

    /** Set if we're running the event_base_loop function, to prevent
     * reentrant invocation. */
    int running_loop;

    /** Set to the number of deferred_cbs we've made 'active' in the
     * loop.  This is a hack to prevent starvation; it would be smarter
     * to just use event_config_set_max_dispatch_interval's max_callbacks
     * feature */
    int n_deferreds_queued;

    /* 此处用链表管理激活的事件 Active event management. */
    /** An array of nactivequeues queues for active event_callbacks (ones
     * that have triggered, and whose callbacks need to be called).  Low
     * priority numbers are more important, and stall higher ones.
     */
    struct evcallback_list *activequeues;
    /** The length of the activequeues array */
    int nactivequeues;
    /** A list of event_callbacks that should become active the next time
     * we process events, but not this time. */
    struct evcallback_list active_later_queue;

    /* 超时控制 common timeout logic */

    /** An array of common_timeout_list* for all of the common timeout
     * values we know. */
    struct common_timeout_list **common_timeout_queues;
    /** The number of entries used in common_timeout_queues */
    int n_common_timeouts;
    /** The total size of common_timeout_queues. */
    int n_common_timeouts_allocated;

    /** IO事件 Mapping from file descriptors to enabled (added) events */
    struct event_io_map io;

    /** 信号事件 Mapping from signal numbers to enabled (added) events. */
    struct event_signal_map sigmap;

    /** 超时事件 Priority queue of events with timeouts. */
    struct min_heap timeheap;

    /** 时间戳记录 Stored timeval: used to avoid calling gettimeofday/clock_gettime
     * too often. */
    struct timeval tv_cache;

    struct evutil_monotonic_timer monotonic_timer;

    /** Difference between internal time (maybe from clock_gettime) and
     * gettimeofday. */
    struct timeval tv_clock_diff;
    /** Second in which we last updated tv_clock_diff, in monotonic time. */
    time_t last_updated_clock_diff;

#ifndef EVENT__DISABLE_THREAD_SUPPORT
    /* threading support */
    /** The thread currently running the event_loop for this base */
    unsigned long th_owner_id;
    /** A lock to prevent conflicting accesses to this event_base */
    void *th_base_lock;
    /** A condition that gets signalled when we're done processing an
     * event with waiters on it. */
    void *current_event_cond;
    /** Number of threads blocking on current_event_cond. */
    int current_event_waiters;
#endif
    /** The event whose callback is executing right now */
    struct event_callback *current_event;

#ifdef _WIN32
    /** IOCP support structure, if IOCP is enabled. */
    struct event_iocp_port *iocp;
#endif

    /** Flags that this base was configured with */
    enum event_base_config_flag flags;

    struct timeval max_dispatch_time;
    int max_dispatch_callbacks;
    int limit_callbacks_after_prio;

    /* Notify main thread to wake up break, etc. */
    /** True if the base already has a pending notify, and we don't need
     * to add any more. */
    int is_notify_pending;
    /** A socketpair used by some th_notify functions to wake up the main
     * thread. */
    evutil_socket_t th_notify_fd[2];
    /** An event used by some th_notify functions to wake up the main
     * thread. */
    struct event th_notify;
    /** A function used to wake up the main thread from another thread. */
    int (*th_notify_fn)(struct event_base *base);

    /** Saved seed for weak random number generator. Some backends use
     * this to produce fairness among sockets. Protected by th_base_lock. */
    struct evutil_weakrand_state weakrand_seed;

    /** List of event_onces that have not yet fired. */
    LIST_HEAD(once_event_list, event_once) once_events;

};

  evsel和evbase这两个字段的设置可能会让人有些迷惑,这里你可以把evsel和evbase看作是类和静态函数的关系,比如添加事件时的调用行为:evsel->add(evbase, ev),实际执行操作的是evbase;这相当于class::add(instance, ev),instance就是class的一个对象实例。evsel指向了全局变量static const struct eventop *eventops[]中的一个;
前面也说过,libevent将系统提供的I/O demultiplex机制统一封装成了eventop结构;因此eventops[]包含了select、poll、kequeue和epoll等等其中的若干个全局实例对象。evbase实际上是一个eventop实例对象。eventop会根据当前编译环境选择可用的类型添加至数组中以供选择,其实现在编译过程中进行,对用户来说是完全封闭不可见的。具体说明在后文中会详细介绍。这里给出其中用到的event_top结构体如下:

/** Structure to define the backend of a given event_base. */
struct eventop {

    /** 名字 The name of this backend. */
    const char *name;

    /** 初始化 Function to set up an event_base to use this backend.  It should
     * create a new structure holding whatever information is needed to
     * run the backend, and return it.  The returned pointer will get
     * stored by event_init into the event_base.evbase field.  On failure,
     * this function should return NULL. */
    void *(*init)(struct event_base *);

    /** 注册 Enable reading/writing on a given fd or signal.  'events' will be
     * the events that we're trying to enable: one or more of EV_READ,
     * EV_WRITE, EV_SIGNAL, and EV_ET.  'old' will be those events that
     * were enabled on this fd previously.  'fdinfo' will be a structure
     * associated with the fd by the evmap; its size is defined by the
     * fdinfo field below.  It will be set to 0 the first time the fd is
     * added.  The function should return 0 on success and -1 on error.
     */
    int (*add)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);

    /** 删除 As "add", except 'events' contains the events we mean to disable. */
    int (*del)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);

    /** 事件分发 Function to implement the core of an event loop.  It must see which
        added events are ready, and cause event_active to be called for each
        active event (usually via event_io_active or such).  It should
        return 0 on success and -1 on error.
     */
    int (*dispatch)(struct event_base *, struct timeval *);

    /** 释放资源 Function to clean up and free our data from the event_base. */
    void (*dealloc)(struct event_base *);

    /** 标记位 Flag: set if we need to reinitialize the event base after we fork.
     */
    int need_reinit;

    /**支持的特性,根据操作系统不同而不同 Bit-array of supported event_method_features that this backend 
    can provide. */
    enum event_method_feature features;

    /** 额外信息记录 Length of the extra information we should record for each fd that
        has one or more active events.  This information is recorded
        as part of the evmap entry for each fd, and passed as an argument
        to the add and del functions above.
     */
    size_t fdinfo_len;
};

  同样的,在event.h中有对其作用和调用函数的描述:

/**
 * Structure to hold information and state for a Libevent dispatch loop.
 *
 * The event_base lies at the center of Libevent; every application will
 * have one.  It keeps track of all pending and active events, and
 * notifies your application of the active ones.
 *
 * This is an opaque structure; you can allocate one using
 * event_base_new() or event_base_new_with_config().
 *
 * @see event_base_new(), event_base_free(), event_base_loop(),
 *    event_base_new_with_config()
 */

  由此我们可以看出,如果说event是核心数据结构,那么event_base就是在event之后,记录并统筹所有event的核心事件管理结构,即事件的处理框架。创建一个event_base对象也既是创建了一个新的libevent实例,程序需要通过调用event_init()(内部调用event_base_new函数执行具体操作)函数来创建,该函数同时还对新生成的libevent实例进行了初始化。该函数首先为event_base实例申请空间,然后初始化timer mini-heap,选择并初始化合适的系统I/O 的demultiplexer机制,初始化各事件链表;函数还检测了系统的时间设置,为后面的时间管理打下基础。

四.小结

  本文对libevent库的核心数据结构进行了解析,在下一篇中将会对本文中数据结构内提到的各个接口函数进行解析。

猜你喜欢

转载自blog.csdn.net/u013354486/article/details/80758401