event 结构

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Function_Dou/article/details/87891307

libevent是基于事件驱动的, 事件驱动主要是由event_base负责, 它是负责事件的各种处理(注册, 删除等); 而event则是libevent的核心, 管理事件驱动.

event结构

其实说的一些东西只有看过了libevent大部分源码后才容易理解, 所以最开始的还都是从源码入手, 理解清楚了整个框架才能明白其中的意义.

struct event {
	// 三种队列
	
	// 注册的事件队列
	TAILQ_ENTRY (event) ev_next;
	// 就绪队列
	TAILQ_ENTRY (event) ev_active_next;
	// 信号队列
	TAILQ_ENTRY (event) ev_signal_next;
	unsigned int min_heap_idx;	// 保存定时的小根堆/* for managing timeouts */

	// 反应堆事件
	struct event_base *ev_base;

	// IO : 事件的文件描述符
	// 信号 : 以绑定的信号
	int ev_fd;
	// 监听的类型
	short ev_events;
	// 调用回调函数 ev_callback 的次数
	short ev_ncalls;
	// 删除回调, 通常指向 ev_ncalls 或者为 NULL
	short *ev_pncalls;	/* Allows deletes in callback */

	// 设置定时时间
	struct timeval ev_timeout;

	int ev_pri;	// 队列优先级, 与event_base *ev-base 中的优先级队列联用/* smaller numbers are higher priority */

	// 设置的回调函数, 
	// int : ev_fd
	// short : ev_event
	// arg : ev_arg. 通过 event_set 设置第三个参数
	void (*ev_callback)(int, short, void *arg);
	void *ev_arg;

	// 回调时保存返回的事件状态.(像select中的fd_set, poll中的revent, epoll中的epoll_events结构)
	int ev_res;		/* result passed to event callback */
	// 标志位: 标志当前event事件的状态, 以及所在哪个链表中
	int ev_flags;
};

大部分的解释都在注释中了, 但是可能还有部分不太明了, 这里我具体对可能不太明了的进行解释.

  • TAILQ_ENTRY : 一个宏定义, 定义的是链表. 使用宏定义来实现链表很像c++中的模板. 如果对宏定义定义链表感兴趣可以在compat/sys/queue.h看一下.

    #define TAILQ_ENTRY(type)						\
    struct {								\
    	struct type *tqe_next;	/* next element */			\
    	struct type **tqe_prev;/* 前一个节点的next节点的地址. 这样主要是方便删除操作*/ \
    }
    
  • ev_callback : event的回调函数, 被event_base调用. 执行事件处理程序.

    • ev_fd : 如果是 IO 类型,则ev_fd是文件描述符; 如果是 信号, 则ev_fd是绑定的具体信号.
    • ev_event : 监听事件的类型.
    • ev_arg : 是ev_callback的第三个参数.
    • ev_res : ev_callback的返回事件的状态.

    其中ev_events是libevent内部定义的类型. 针对IO, 信号, 定时都有一个细致的划分, 还有一个永久事件我们以后碰到了再来分析.

    // 定时事件
    #define EV_TIMEOUT	0x01
    // 读事件
    #define EV_READ		0x02
    // 写事件
    #define EV_WRITE	0x04
    // 信号事件
    #define EV_SIGNAL	0x08
    // 表事件为永久
    #define EV_PERSIST	0x10	/* Persistant event */
    
  • ev_flags : 标志位. 表示当前event的状态是什么(还没有被初始化, 还是已经就绪等等状态).

    /**************** int ev_flags 当前事件的类型*******************/ 
    #define EVLIST_TIMEOUT	0x01
    // 在已注册链表中
    #define EVLIST_INSERTED	0x02
    // 信号
    #define EVLIST_SIGNAL	0x04
    // 就绪链表中
    #define EVLIST_ACTIVE	0x08
    // 内部使用
    #define EVLIST_INTERNAL	0x10
    // 已被初始化
    #define EVLIST_INIT	0x80
    
    /* EVLIST_X_ Private space: 0x1000-0xf000 */
    #define EVLIST_ALL	(0xf000 | 0x9f)
    

说了这么多参数的意义, 如果是刚开始接触肯定是懵逼的, 这很正常. 现在我们来理一下整个event结构, 一定要认真看一下, 不然很可能还是很懵逼.

  1. 三个队列.

    event中定义的三个链表队列就是用来区分注册事件的状态. 比如事件还没有被注册则三个队列中都没有信息; 如果事件刚被注册了那么事件肯定在注册链表中并且ev_flag标志位设置为EVLIST_INSERTED(标志好处就是我们不用在链表中遍历判断事件在哪一个链表中, 只需要判断标志位就可以); 如果注册的事件是IO事件, 那么还要ev_event标志;如果注册的事件是信号, 那么还要将事件加入信号链表中并设置ev_flags标志同时ev_event标志也要设置为EV_SIGNAL; 如果事件已经被触发了, 则要加事件加入就绪链表并设置标志还要从其他两个链表中删除.

    记住 : 在设置事件的时候需要设置ev_event标志位来标志事件的类型, 加入链表时需要设置ev_flags标志来标志所处的哪个链表和状态.

    扫描二维码关注公众号,回复: 5294271 查看本文章
  2. 定时.

    当事件设置了时间, 那么肯定要初始化ev_timeout标志, 同时将该事件加入到min_heap_idx小根堆中, 时间越短越靠前也就越快被触发.

  3. 回调

    当事件从就绪链表中被取出时, 那就表示事件被触发, 通过查看ev_ncalls来执行对少次回调函数.

如果看完了上面的三个过程, 能够让你减少懵逼的话我也很高兴自己理的思路能够帮助你. 剩下的过程我们以后遇到再来分析, 毕竟event可是核心, 怎么会这么简单.

总结

分析了event结构可以看出来不管事件是IO, 信号还是定时事件都可以通过event进行统一的封装, 这就是Reactor接口实现, 很像下面的事件收集器(虽然不太准确). 下面我们接着分析event_base结构.

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Function_Dou/article/details/87891307