libevent源码II--event事件处理器

libevent的核心-event

struct event* signal_event = evsignal_new(base, SIGINT, signal_cb, base);
event_add(signal_event, NULL);

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


//在event2/event_struct.h中event的结构描述
struct event {
	TAILQ_ENTRY (event) ev_next;            /*增加下一个事件*/
	TAILQ_ENTRY (event) ev_active_next;     /*增加下一个活动事件*/
	TAILQ_ENTRY (event) ev_signal_next;     /*增加下一个信号*/
	/*ev_next,ev_active_next和ev_signal_next都是双向链表节点指针;它们是libevent对不同事件类型和在不同的时期,对事件的管理时使用到的字段。
	libevent使用双向链表保存所有注册的I/O和Signal事件,ev_next就是该I/O事件在链表中的位置;称此链表为“已注册事件链表”;
	同样ev_signal_next就是signal事件在signal事件链表中的位置;
	ev_active_next:libevent将所有的激活事件放入到链表active list中,然后遍历active list执行调度,ev_active_next就指明了event在active list中的位置;*/
 
	unsigned int min_heap_idx;         /* for managing timeouts 表示该event保存在min_heap数组中的索引*/ 
	struct timeval ev_timeout;   //用来保存事件的超时时间
	/*min_heap_idx和ev_timeout,如果是timeout事件,它们是event在小根堆中的索引和超时值,libevent使用小根堆来管理定时事件*/
 
	struct event_base *ev_base;   /* 该事件所属的反应堆实例,这是一个event_base结构体*/
 
	int ev_fd;               /*对于I/O事件,是绑定的文件描述符;对于signal事件,是绑定的信号*/
	short ev_events;        /*event关注的事件类型,它可以是以下3种类型:
							I/O事件: EV_WRITE和EV_READ
							定时事件:EV_TIMEOUT
							信号:    EV_SIGNAL
							辅助选项:EV_PERSIST,表明是一个永久事件
							Libevent中的定义为:
							#define EV_TIMEOUT 0x01   
							#define EV_READ  0x02   
							#define EV_WRITE 0x04   
							#define EV_SIGNAL 0x08   
							#define EV_PERSIST 0x10 
							可以看出事件类型可以使用“|”运算符进行组合,需要说明的是,信号和I/O事件不能同时设置;
							还可以看出libevent使用event结构体将这3种事件的处理统一起来;*/ 
	void *ev_arg; /*回调函数的参数,void*,表明可以是任意类型的数据,在设置event时指定*/
	void (*ev_callback)(int fd, short events, void *arg);   
	/*event的回调函数,被ev_base调用,执行事件处理程序,这是一个函数指针,
	其中参数fd对应于ev_fd;events对应于ev_events;arg对应于ev_arg*/
 
	int ev_pri;  /* smaller numbers are higher priority */
 
	short ev_ncalls;        /*事件就绪执行时,调用ev_callback的次数,通常为1*/ 
	short *ev_pncalls; /* Allows deletes in callback ,指针,通常指向ev_ncalls或者为NULL*/
	int ev_res;  /* result passed to event callback ,记录了当前激活事件的类型*/
	int ev_flags; /*libevent用于标记event信息的字段,表明其当前的状态,可能的值有:
				  #define EVLIST_TIMEOUT 0x01 // event在time堆中   
				  #define EVLIST_INSERTED 0x02 // event在已注册事件链表中   
				  #define EVLIST_SIGNAL 0x04 // 未见使用   
				  #define EVLIST_ACTIVE 0x08 // event在激活链表中   
				  #define EVLIST_INTERNAL 0x10 // 内部使用标记   
				  #define EVLIST_INIT     0x80 // event已被初始化  
				  #define EVLIST_TIMEOUT 0x01 // event在time堆中
				  #define EVLIST_INSERTED 0x02 // event在已注册事件链表中
				  #define EVLIST_SIGNAL 0x04 // 未见使用
				  #define EVLIST_ACTIVE 0x08 // event在激活链表中
				  #define EVLIST_INTERNAL 0x10 // 内部使用标记
				  #define EVLIST_INIT     0x80 // event已被初始化 */          
};

注册事件

函数原型:int event_add(struct event *ev, const struct timeval *tv) 参数:ev:指向要注册的事件; tv:超时时间; 

函数将ev注册到ev->ev_base上,事件类型由ev->ev_events指明,如果注册成功,ev将被插入到已注册链表中;如果tv不是NULL,则会同时注册定时事件,将ev添加到timer堆上;

如果其中有一步操作失败,那么函数保证没有事件会被注册,可以讲这相当于一个原子操作。这个函数也体现了libevent细节之处的巧妙设计,且仔细看程序代码,部分有省略,注释直接附在代码中。 
 


int event_add(struct event *ev, const struct timeval *tv)
{
	struct event_base *base = ev->ev_base; // 要注册到的event_base
	const struct eventop *evsel = base->evsel;
	void *evbase = base->evbase; // base使用的系统I/O策略
	// 新的timer事件,调用timer heap接口在堆上预留一个位置
	// 注:这样能保证该操作的原子性:
	// 向系统I/O机制注册可能会失败,而当在堆上预留成功后,
	// 定时事件的添加将肯定不会失败;
	// 而预留位置的可能结果是堆扩充,但是内部元素并不会改变
	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 */
	}
	// 如果事件ev不在已注册或者激活链表中,则调用evbase注册事件
	if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
		!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
			res = evsel->add(evbase, ev);
			if (res != -1) // 注册成功,插入event到已注册链表中
				event_queue_insert(base, ev, EVLIST_INSERTED);
	}
	// 准备添加定时事件
	if (res != -1 && tv != NULL) {
		struct timeval now;
		// EVLIST_TIMEOUT表明event已经在定时器堆中了,删除旧的
		if (ev->ev_flags & EVLIST_TIMEOUT)
			event_queue_remove(base, ev, EVLIST_TIMEOUT);
		// 如果事件已经是就绪状态则从激活链表中删除
		if ((ev->ev_flags & EVLIST_ACTIVE) &&
			(ev->ev_res & EV_TIMEOUT)) {
				// 将ev_callback调用次数设置为0
				if (ev->ev_ncalls && ev->ev_pncalls) {
					*ev->ev_pncalls = 0;
				}
				event_queue_remove(base, ev, EVLIST_ACTIVE);
		}
		// 计算时间,并插入到timer小根堆中
		gettime(base, &now);
		evutil_timeradd(&now, tv, &ev->ev_timeout);
		event_queue_insert(base, ev, EVLIST_TIMEOUT);
	}
	return (res);
}

void event_queue_insert(struct event_base *base, struct event *ev, int queue)
{
	// ev可能已经在激活列表中了,避免重复插入
	if (ev->ev_flags & queue) {
		if (queue & EVLIST_ACTIVE)
			return;
	}
	// ...
	ev->ev_flags |= queue; // 记录queue标记
	switch (queue) {
	case EVLIST_INSERTED: // I/O或Signal事件,加入已注册事件链表
		TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);
		break;
	case EVLIST_ACTIVE: // 就绪事件,加入激活链表
		base->event_count_active++;
		TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri], ev, ev_active_next);
		break;
	case EVLIST_TIMEOUT: // 定时事件,加入堆
		min_heap_push(&base->timeheap, ev);
		break;
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值