(虚线部分代表包含关系)
一、tail_queue
关于tail_queue的解释在上篇文章中很细致。
二、event
//event提供了函数接口,供Reactor在事件发生时调用,以执行相应的事件处理, //通常它会绑定一个有效的句柄(ev_fd)做为回调函数的参数. struct event { //已注册事件队列入口 TAILQ_ENTRY (event) ev_next; //已激活事件队列入口 TAILQ_ENTRY (event) ev_active_next; //信号事件队列入口 不过在以前的分析中可以看到,将来这个队列中的内容会被添加到ev_active_next队列中。 TAILQ_ENTRY (event) ev_signal_next; //表示该event在定时器事件最小堆min_heap的索引 unsigned int min_heap_idx; /* for managing timeouts */ //该事件所属的反应堆实例 struct event_base *ev_base; //对于I/O事件,是绑定的文件描述符; 对于signal事件,是绑定的信号. int ev_fd; //表示事件类型: I/O,定时器或者信号 short ev_events; //事件就绪执行时,将要调用ev_callback 的次数,通常为1 short ev_ncalls; //该事件的超时时间,在定时器最小堆min_heap操作中作为节点值进行比较. struct timeval ev_timeout; //该事件的优先级,越小越优先. int ev_pri; /* smaller numbers are higher priority */ //该事件被激活时的回调函数 void (*ev_callback)(int, short, void *arg); //该事件的标记信息,表示其当前的状态,即它在哪个链表中 int ev_flags; ... //其他成员. };
一个事件是可以插入到多个队列的,当它与一个反应堆实例(event_base)关联时,这个事件被插入到反应堆实例下的已注册事件队列 event_base -> eventqueue ,当它处于就绪状态时,会被插入到反应堆实例下的已激活事件队列 event_base -> activequeues[id], id = event -> ev_pri .同时,如果此事件是信号事件,那么它会被插入到反应堆结构体下的信号事件结构体下的信号队列 event_base -> evsignal_info -> evsigevents[id], id = event -> ev_fd .
需要指出的,每个事件都保持了一个成员 struct event_base *ev_base; ,它表示该事件属于哪个反应堆实例.
还有一个成员需要注意, short ev_events; ,它表明此事件的事件类型,libevent正是基于此实现对I/O,信号,定时 3种事件的封装的.
一般使用情况下,不申请struct event_base.因为在库中有一个全局的event_base,所有的事件都可以添加到这个全局event_base中!!!
三、min_heap
typedef struct min_heap { struct event** p; //p指向一个动态分配的数组,数组元素是event指针. unsigned n, a; // n表示目前保存了多少元素,a表示p指向的内存能够存储event指针的个数. } min_heap_t;
四、evsignal_info
这个结构体是用来管理信号事件的
struct evsignal_info { //是否有信号发生的标记 volatile sig_atomic_t evsignal_caught; //evsigevents[signo]表示注册到信号 signo 的事件链表 struct event_list evsigevents[NSIG]; //具体记录每个信号触发的次数,evsigcaught[signo]是记录信号signo被触发的次数 sig_atomic_t evsigcaught[NSIG]; ... //其他成员 };
还有其他几个更加重要的结构体
event_base