Libevent---数据结构

1. 事件处理对象--event

    Libevent中事件处理对象是event结构类型。event结构体封装了句柄、事件类型、回调函数,以及其他必要的标志和数据。

struct event {
    // 事件回调结构
    struct event_callback ev_evcallback;
    /* for managing timeouts */
    //如果是超时事件,则表明是小根堆还是通用超时队列
    union {
        TAILQ_ENTRY(event) ev_next_with_common_timeout;
        int min_heap_idx;
    } ev_timeout_pos;
    // 对于I/O事件处理器,它是文件描述符值;对于信号事件处理器,它是信号值
    evutil_socket_t ev_fd;
    //该事件处理器从属的event_base实例
    struct event_base *ev_base;
    //联合体,表示从属的是信号队列还是I/O队列
    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;
            / Allows deletes in callback */
            short *ev_pncalls;
        } ev_signal;
    } ev_;
    //事件标志
    short ev_events;
    //当前激活事件类型
    short ev_res;       /* result passed to event callback */
    //超时时间值
    struct timeval ev_timeout;
};



事件标志为:
    #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 事件处理器已经被初始化

 

2. 队列

在linux下,Libevent中注册的信号事件和I/O事件,都分别由队列来管理;

  • 队列泛型
//用c宏命令的方式实现了泛型
//这里的name是指结构体叫什么名字,然后type是指该结构体是什么类型

define TAILQ_HEAD(name, type)                      \
struct name {    
    //tqh_first指的是该队列的首结构体
    //tqh_last指的是该队列最后一个元素的地址                      \
    struct type *tqh_first; /* first element */         \
    struct type **tqh_last; /* addr of last next element */     \
}

//TAILQ_ENTRY并没有给结构体起名字,所以只能是一个匿名结构体,
//它一般是另外一个结构体或者共用体的成员
#define TAILQ_ENTRY(type)                       \
struct {                                \
    struct type *tqe_next;  /* next element */          \
    struct type **tqe_prev; /* address of previous next element */  \
}


//由这两个结构体组成,就可以实现一个队列

//队列中的元素结构体。它有一个值,并且有前向指针和后向指针
//通过前后像指针,把队列中的节点(元素)连接起来
struct queue_entry_t
{
    int value;
 
    //从TAILQ_ENTRY的定义可知,它只能是结构体或者共用体的成员变量
    TAILQ_ENTRY(queue_entry_t)entry;
};
 
//定义一个结构体,结构体名为queue_head_t,成员变量类型为queue_entry_t
//就像有头节点的链表那样,这个是队列头。它有两个指针,分别指向队列的头和尾
TAILQ_HEAD(queue_head_t, queue_entry_t);

在libevent中一些插入、删除都是由宏操作执行的

  • event队列
//event_struct.h
    TAILQ_HEAD (event_list, event);
     
    //所以event_list的定义展开后如下:
    struct event_list
    {
        struct event *tqh_first;
        struct event **tqh_last;
    };
  • I/O事件或信号事件队列
struct event_signal_map{
    void **entries; //用于存放evmap_io 或evmap_signal 的数组 */
    int nentries; //entries数组的大小
}

//I/O事件队列
struct evmap_io{
    struct event_list events;
    ev_uint16_t nread;
    ev_uint16_t nwrite;
    ev_uint16_t nclose; //已关闭次数
}

//信号事件队列    evmap_signal是event_signal_map数组中的一个元素,这个元素是一个链表,存储相同signal/IO的事件
struct evmap_signal {
    struct event_list events;
}

       这里可以看出,对于I/O事件或者信号事件是用一个数组来存储的,而每一个数组元素是一个队列,里面存储的是同一个文件描述符的事件队列。也就是说对于同一个文件描述符可以有多个event来进行处理。

3. event_base

它是libevent的Reactor,它存在于主线程,用来管控那些需要处理的各种事件。

struct event_base {
    /** 初始化Reactor的时候选择一种后端I/O复用机制
     **/
    const struct eventop *evsel;   // 一些操作函数如:init、add、del
    /** 指向I/O复用机制真正存储的数据,它通过evsel成员的init函数来初始化 */
    void *evbase;    //真正的epoll、poll

    /** 事件变化队列,其用途是:如果一个文件描述符上注册的事件被多次修改,则
     *  可以使用缓冲来避免重复的系统调用(比如epoll_ctl),它仅能用于时间
     *  复杂度为O(1)的I/O复用技术*/
    struct event_changelist changelist;   //暂时没看到它的用处

    /** 指向信号的后端处理机制,目前仅在signal.h文件中定义了一种处理方法
     **/
    const struct eventop *evsigsel;
    /** 信号事件处理器使用的数据结构,其中封装了一个由socketpair创建的管道。它用于信号处理函数和事件多路分发器之间的通信 */
    struct evsig_info sig;

    /** 添加到event_base所有事件的数量 */
    int virtual_event_count;
    /** M最大可激活事件的数量 */
    int virtual_event_count_max;
    /** 所有注册事件的数量 */
    int event_count;
    /** 可注册事件的最大值 */
    int event_count_max;
    /** 注册在其中的激活事件数量 */
    int event_count_active;
    /** 最大存在的激活事件数量 */
    int event_count_active_max;

    /** 是否执行完活动事件队列上剩余的任务之后就退出事件循环
     **/
    int event_gotterm;
    /** 是否立即退出循环 */
    int event_break;
    /** 是否应该启动一个新的事件循环 */
    int event_continue;

    /** 目前正在处理的活动事件队列的优先级 */
    int event_running_priority;

    /** 事件循环是否已经启动 */
    int running_loop;

    /** 设置为我们在循环中设置为“活动”的延迟的cbs数。
     *  这是一个防止饥饿的黑客;使用event_config_set_max_dispatch_interval
     *  的max_callbacks功能会更聪明 
     */
    int n_deferreds_queued;

    /* 活动事件管理 */
    /** 活动事件队列数组。索引值越小的队列,优先级越高。
     *  高优先级的活动事件队列中的事件处理器将被优先处理
     */
    struct evcallback_list *activequeues;
    /** 活动事件队列数组的大小 */
    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 */

    /** 下面3个成员用于管理通用定时器队列 */
    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;

    /** 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;

    /** 时间堆 */
    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
    /* 多线程支持 */
    /** 当前运行该event_base的事件循环的线程 */
    unsigned long th_owner_id;
    /** 对event_base的独占锁 */
    void *th_base_lock;
    /** 条件变量,用于唤醒正在等待某个事件处理完毕的线程 */
    void *current_event_cond;
    /** 等待current_event_cond的线程数 */
    int current_event_waiters;
#endif
    /** 当前事件循环正在执行哪个事件处理器的回调函数 */
    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;
    //为了避免优先级反转,导致太多低优先级的一直在run,而高优先级的得不到处理
    int max_dispatch_callbacks;
    int limit_callbacks_after_prio;   //优先级阈值,超过此优先级的,那么在两个
                                      //epoll_wait之间执行的次数不超过max_dispatch_callbacks

    /* 下面这组成员变量给工作线程唤醒主线程提供了方法(使用socketpair创建的管道) */
    /** 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;

};

 

4. eventop结构体

其封装了I/O复用机制必要的一些操作,比如注册事件、等待事件等。它为event_base支持的所有后端I/O复用机制提供了一个统一的接口。

struct eventop {
    /** I/O复用机制的名字 */
    const char *name;
    /** 函数设置事件库以使用此后端。它应该创建一个新的结构
     *  来保存运行后端所需的任何信息,并返回它。
     *  返回的指针将由event_init存储到event_base.evbase字段中。
     *  失败时,此函数应返回NULL。 
     *  比如,epoll中的epoll_create,用来初始化
     */
    void *(*init)(struct event_base *);
    /** 添加事件处理器
     */
    int (*add)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);
    /** 删除事件处理器 */
    int (*del)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);
    /** 这个函数就是循环等待,相当于epoll_wait
     */
    int (*dispatch)(struct event_base *, struct timeval *);
    /** 释放event_base所分配的资源 */
    void (*dealloc)(struct event_base *);
    /** Flag: set if we need to reinitialize the event base after we fork.
     */
    int need_reinit;
    /** I/O复用支持的一些技术,如边缘触发,水平触发等
     * provide. */
    enum event_method_feature features;
    /** 有的I/O复用机制需要为每个I/O事件队列和信号事件队列分配额外的内存,以避免同一个
     *  文件描述符被重复插入I/O复用机制的事件表中。evmap_io_add(或evmap_io_del)函数
     *  在调用eventop的add(或del)方法时,将这段内存的起始地址作为第5个参数
     *  传递给add(或del)方法。下面这个成员则指定了这段内存的长度
     * */
    size_t fdinfo_len;
};

 

上一篇:网站部署GEOTRUST EVSSL证书有哪些好处?


下一篇:前端Vue+Element使用xlsx上传excel格式文件