libevent使用

简介

libevent是一个开源的高性能的网络库

优点:

事件驱动(event-driven),高性能

轻量级,专注于网络

源代码相当精炼,易读

夸平台,支持windows,linux等多个平台

支持I/0,定时器和信号等事件

注册事件优先级

Reactor

处理机制:
 普通函数的调用机制是:程序调用某函数->函数执行,程序等待->函数将返回结果和控制板返回给程序->程序继续处理
Reactor的处理机制是:应用程序不主动调用某个API完成处理,它逆置了事件处理流程,应用程序需要将提供相应的接口并注册到Reacthr上,如果相应的事件发生,Reactor将主动调用程序注册接口,这些接口有称为回调函数。
优点:
响应快:不必要为单个同步事件所阻塞。
编程简单:可以最大程度的避免多线程同步的问题,并且避免了多进程/线程的切换开销。
可扩展性。可以方便的通过增加Reactor实力个数来充分利用CPU。
可复用性,rector框架本身与具体事件处理逻辑无关,具有很高的复用性
Rector模式框架
事件源:linux上是文件描述符,windows上就是socket或者Handle。这里统称为“句柄集”;程序在指定的句柄上注册了关心的事件。
event demultiplexer——事件多路分发机制:由操作系统提供的I/O多路复用机制,比如select和epoll
程序首先将句柄(事件源)及其事件注册到event demultiplexer上;当事件到达时,event demultiplexer会发通知“在已经注册的句柄中,一个或多个句柄事件已经准备就绪”。
程序收到通知后,就可以在非阻塞的情况下对事件进行处理了。
Reactor—反应器:事件管理接口,内核使用event demultiplexer注册、注销事件。并运行事件循环、当有事件进入就绪状态时,调用注册事件的回调函数处理事件。
对应到libevent中就是event_base结构体。
Event Handler—事件处理程序:事件处理程序提供了一组接口,每个接口对应一种类型的事件,供Reactor在相应的事件中发生时调用,执行相应的事件处理。通常会绑定一个有效的句柄。对应到libevent中就是event结构体。

基本使用场景和事件流程

1、初始化libevent,并保存返回的指针:struct event_base *base = event_init() ;

2、初始化事件event,设置回调函数和关注事件:void event_set(struct event *ev, int fd, short event, void (*cb)(evutil_socket_t, short, void *), void *arg);
ev:执行要初始化的event对象;
fd:该event绑定的“句柄”,对于信号事件,它就是关注的信号;
event:在该fd上关注的事件类型,它可以是EV_READ,EV_WRITER,EV_SIGNAL;
cb:这是个函数指针,当fd上的事件event发生时,调用该函数执行处理,它有三个参数,调用时有event_base负责传入,按顺序,实际上就是event_set时的fd,event,arg;
arg:传给cb函数的指针参数;
3、设置event从属的event_base:int event_base_set(struct event_base *base, struct event *ev);
4、添加事件 int event_add(struct event *ev, const struct timeval *timeout);
5、程序进入无限循环,等待就绪时间并等待事件处理:int event_base_dispatch(struct event_base *);
实例代码:

#include <event.h>
#include <stdio.h>

struct event ev;
struct timeval tv;
struct  event ev1 ;
struct timeval tv1;


void time_cb(int fd, short event, void *argc)
{
	printf("timer1 wakeup fd = %d\n",fd);
	event_add(&ev, &tv); // reschedule timer
}

void time_cb2(int fd,short event ,void *argc)
{
	printf("timer wakeup fd = %d\n",fd );
	event_add(&ev1, &tv1);

}

int main()
{
	struct event_base *base = event_init();
	tv.tv_sec = 1; 
	tv.tv_usec = 0;
	tv1.tv_sec = 2; 
	tv1.tv_usec = 0;
	event_set(&ev, -1, 0, timer_cb, NULL);
	evtimer_set(&ev1,time_cb2,NULL);  //宏等价于evnet_set(&ev1,-1,0,time_cb2,NULL);
	event_add(&ev, &tv);
	event_add(&ev1,&tv1);
	event_base_dispatch(base);
	return 0 ;
}

libevent源代码组织

libevent的源代码都在一层文件夹下面,主要分为头文件、内部使用的头文件、辅助功能函数、日志、libevent框架、对系统I/O多路复用机制的封装、信号管理、定时器管理、基本数据结构和基于libevent的两个实用库等几个部分。
1、头文件: 主要是event.h 事件宏定义、接口函数的声明,主要结构体event的声明;
2、内部头文件  *_intemal.h。内部结构和函数,对外不可见,以达到信息隐藏的目的;
3、libevent框架:event.c event整体框架代码的实现;
4、对系统I/O多路复用机制的封装
epoll.c   对epoll的封装
select.c 对select的封装
devpoll.c 对dev、poll的封装
kqueue.c 对kqueue的封装
5、定时事件管理 min_heap.h 其实就是一个以时间作为key的小根堆结构;
6、信号管理 signal.c 对信号事件的处理;
7、辅助功能函数、evutil.h和evutil.c,一些辅助功能函数,包括创建socket pair和一些时间操作函数:加、减和比较等;
8、日志、log.h和log.c: log日志函数
9、缓冲区管理、evbuffer.c和buffer.c:libevent对缓冲区的封装
10、基本数据结构、compat\sys下的两个源码文件
compat\sys下的两个源文件:queue.h是libevent基本数据结构的实现,包括链表、双向链表。libevent_time.h:一些用于时间操作的定义;
11、实用网络库:http和evdns:是基于libevent实现的http服务器和异步dns查询库;
lebevent的核心:event
event结构体
truct event 
{
	TAILQ_ENTRY(event) ev_active_next;
	TAILQ_ENTRY(event) ev_next;
	union 
	{
		TAILQ_ENTRY(event) ev_next_with_common_timeout;
		int min_heap_idx;
	} ev_timeout_pos;
	evutil_socket_t ev_fd; //对应I/0事件是文件描述符,对于signal事件是绑定的信号
	struct event_base *ev_base; //该事件所属反应堆实例;
	union
	{
		//IO事件
		struct 
		{
			TAILQ_ENTRY(event) ev_io_next;
			struct timeval ev_timeout;
		} ev_io;

		//signal events事件 
		struct 
		{
			TAILQ_ENTRY(event) ev_signal_next;
			short ev_ncalls;	//事件就绪是调用ev_callback的次数,通常为1
			short *ev_pncalls; //指针,指向 ev_ncalls或NULL
		} ev_signal;
	} _ev;

	short ev_events;	//event关注的事件类型
	short ev_res;		//记录当前激活事件的类型
	short ev_flags;	//标记event信息的字段,表明其当前的状态
	ev_uint8_t ev_pri;	// smaller numbers are higher priority 
	ev_uint8_t ev_closure;
	struct timeval ev_timeout;
	
	void (*ev_callback)(evutil_socket_t, short, void *arg);//回调函数指针
	void *ev_arg; //回调函数arg参数;
};

1、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/0事件不能同时设置
2、eb_flags字段的值
#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 已被初始化
事件设置接口函数
void event_set(struct event *ev, int fd, short events,void (*callback)(int, short, void *), void *arg);
/*	
	1.设置事件 ev 绑定的文件描述符或者信号,对于定时事件,设为-1 即可;
	2.设置事件类型,比如 EV_READ|EV_PERSIST, EV_WRITE, EV_SIGNAL 等;
	3.设置事件的回调函数以及参数 arg;
	4.初始化其它字段,比如缺省的 event_base 和优先级;
*/

int event_base_set(struct event_base *base, struct event *ev);
/*
	设置 event ev 将要注册到的 event_base;
	libevent 有一个全局 event_base 指针 current_base,默认情况下事件 ev
	将被注册到 current_base 上,使用该函数可以指定不同的 event_base;
	如果一个进程中存在多个 libevent 实例,则必须要调用该函数为 event 设置不同的 event_base;
*/
int event_priority_set(struct event *ev, int pri)
//设置event ev的优先级,没什么可说的,注意的一点就是:当ev正处于就绪状态时,不能设置,返回-1。

初见事件处理框架

event_base结构体
struct event_base 
{
	const struct eventop *evsel;
	void *evbase; //是一个eventop实例对象
	struct event_changelist changelist;
	const struct eventop *evsigsel;
	struct evsig_info sig;   //管理信号的结构体
	int virtual_event_count;
	int event_count;
	int event_count_active;
	int event_gotterm;
	int event_break;
	int event_continue;
	int event_running_priority;
	int running_loop;
	struct event_list *activequeues;
	int nactivequeues;
	struct common_timeout_list **common_timeout_queues;
	int n_common_timeouts;
	int n_common_timeouts_allocated;
	struct deferred_cb_queue defer_queue;
	struct event_io_map io;
	struct event_signal_map sigmap;
	struct event_list eventqueue;   //保存了所有注册事件event的指针;
	struct timeval event_tv;
	struct min_heap timeheap;
	struct timeval tv_cache;

#if defined(_EVENT_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
	struct timeval tv_clock_diff;
	time_t last_updated_clock_diff;
#endif

#ifndef _EVENT_DISABLE_THREAD_SUPPORT
	unsigned long th_owner_id;
	void *th_base_lock;
	struct event *current_event;
	void *current_event_cond;
	int current_event_waiters;
#endif

#ifdef WIN32
	struct event_iocp_port *iocp;
#endif
	enum event_base_config_flag flags;
	int is_notify_pending;
	evutil_socket_t th_notify_fd[2];
	struct event th_notify;
	int (*th_notify_fn)(struct event_base *base);
};


struct eventop 
{
	const char *name;
	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);//删除事件;
	int (*dispatch)(struct event_base *, struct timeval *);//事件分发
	void (*dealloc)(struct event_base *);//注销,释放资源
	int need_reinit;
	enum event_method_feature features;
	size_t fdinfo_len;
};
每个I/0 demultiplex机制的实现都必须提供这5个函数接口来完成自身初始化、销毁、释放;对事件的注册、注销、和发布。
函数接口:
/*
功能:注册事件	
参数:ev:要注册的事件, timeout :超时时间
返回 :失败返回-1
*/	
int event_add(struct event *ev, const struct timeval *timeout);  

/*
功能:删除事件		int event_del(struct event *ev);
参数:ev:要删除的事件
返回: 失败返回-1;
*/
int event_del(struct event *ev);

int event_base_loop(struct event_base *base, int loops);
void event_active(struct event *event, int res, short events);
void event_process_active(struct event_base *base);

事件住循环

Libevent的事件主循环是通过event_base_loop()函数完成的。
libevent将Timer和signal事件都统一到了系统的I/0的demultiplex机制中。
I/0和timer事件的统一:timer事件是利用slect() epoll_wait()的超时时间返回来激活所有就绪的timer事件。
I/O和signal事件的统一:Signal事件的出现对于进程来说是完全随机的,进程不能只是靠一个变量来判断是否发送了信号,而是必须告诉内核。在signal发生时,并不立即调用event的callback函数处理信号,而是设法通过系统的I/0机制以及Timer一起处理。

集成信号处理

Socket pair是一个socket对,包含读socket和写socket。
Libevent提供了辅助函数evutil_socketpair()来创建一个socket pair。
int evutil_socketpair(int d, int type, int protocol, evutil_socket_t sv[2]);
为socket pair的读socket在libevent的event_base实例上注册一个persist的读事件。当写socket写入数据时,读socket就会得到通知,从而event_base就能相应的得到通知了。

Signal事件的管理通过结构体evsignal_info完成的,结构体的定义如下

struct evsignal_info 
{
	struct event ev_signal;//为socket pair的读socket向event_base注册读事件时使用的event结构体;
	int ev_signal_pair[2];//socket pair 对
	int ev_signal_added;//记录ev_signal事件是否已经注册
	volatile sig_atomic_t evsignal_caught;//是否有信号发射的标记
	struct event_list evsigevents[NSIG];//表示注册信号的链表
	sig_atomic_t evsigcaught[NSIG];//记录每个信号的发射次数
	#ifdef HAVE_SIGACTION
		struct sigaction **sh_old; //记录原来signal函数的指针
	#else
		ev_sighandler_t **sh_old;
	#endif
	int sh_old_max;
};
注册、注销signal事件:

注册signal事件是通过evsignal_add(struct event* ev)函数完成的,libe对所有的信号注册同一个处理函数evsignal_handler()。

注册的过程:

1、取得ev要注册到的信号signo;

2、如果信号signo未被注册,那么就为signore注册信号处理函数evsignal_handler();

3、如果事件ev_signal还没有祖册,就注册ev_signal事件;

4、将事件ev添加到signo的event链表中

从signo中注销一个已经注册的signal事件,直接从已经注册事件的链表中移除即可。如果链表已经为NULL,那么就恢复旧的处理函数。

集成定时器事件

系统I/0的机制允许程序制定一个最大等待时间(也称为最大超时时间)timeout,即使没有事件发生它们能保证在timeout时间内返回。

跟距所有timer事件的最小超时时间来设置系统I/O的timeout时间;当系统I/O返回时,再激活所有就绪的timer事件就可以将Timer事件融合到系统的I/O机制中。

Libevent 使用堆来管理Timer事件,其key值就是事件的超时时间。


推荐网站:libevent源码深度剖析

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值