Quantcast
Channel: Sam的技术Blog
Viewing all articles
Browse latest Browse all 158

libevent使用记录一

$
0
0
作者: Sam  (甄峰)  sam_code@hotmail.com

之前使用epoll+threadpool来做高并发Server, 后决定使用libevent来做替换。现记录如下:

0. 背景知识
libevent是用于编写高速可移植非阻塞IO应用的库。它对select,poll,epoll等接口进行封装。通过设置回调函数的方式,监听文件描述符和socket, 还兼任定时器和信号接收的管理工作。

其设计目的是:

可移植性:使用libevent编写的程序应该可以在libevent支持的所有平台上工作。即使没有好的方式进行非阻塞IO,libevent也应该支持一般的方式,让程序可以在受限的环境中运行。

速度快:libevent尝试使用每个平台上最高速的非阻塞IO实现,并且不引入太多的额外开销。

可扩展性:libevent被设计为程序即使需要上万个活动套接字的时候也可以良好工作。

方便:无论何时,最自然的使用libevent编写程序的方式应该是稳定的、可移植的。


libevent由下列组件构成:

evutil:用于抽象不同平台网络实现差异的通用功能。

event和event_base:libevent的核心,为各种平台特定的、基于事件的非阻塞IO后端提供抽象API,让程序可以知道套接字何时已经准备好,可以读或者写,并且处理基本的超时功能,检测OS信号。

bufferevent:为libevent基于事件的核心提供使用更方便的封装。除了通知程序套接字已经准备好读写之外,还让程序可以请求缓冲的读写操作,可以知道何时IO已经真正发生。(bufferevent接口有多个后端,可以采用系统能够提供的更快的非阻塞IO方式,如Windows中的IOCP。)

evbuffer:在bufferevent层之下实现了缓冲功能,并且提供了方便有效的访问函数。

evhttp:一个简单的HTTP客户端/服务器实现。

evdns:一个简单的DNS客户端/服务器实现。

 

evrpc:一个简单的RPC实现。



编译Libevent代码后,会产生以下5个库:

libevent_core:所有核心的事件和缓冲功能,包含了所有的event_base、evbuffer、bufferevent和工具函数。

libevent_extra:定义了程序可能需要,也可能不需要的协议特定功能,包括HTTP、DNS和RPC。

libevent:这个库因为历史原因而存在,它包含libevent_core和libevent_extra的内容。不应该使用这个库,未来版本的libevent可能去掉这个库。

某些平台上可能安装下列库:

libevent_pthreads:添加基于pthread可移植线程库的线程和锁定实现。它独立于libevent_core,这样程序使用libevent时就不需要链接到pthread,除非是以多线程方式使用libevent。

libevent_openssl:这个库为使用bufferevent和OpenSSL进行加密的通信提供支持。它独立于libevent_core,这样程序使用libevent时就不需要链接到OpenSSL,除非是进行加密通信。



头文件

libevent公用头文件都安装在event2目录中,分为三类:

API头文件:定义libevent公用接口。这类头文件没有特定后缀。

兼容头文件:为已废弃的函数提供兼容的头部包含定义。不应该使用这类头文件,除非是在移植使用较老版本libevent的程序时。

结构头文件:这类头文件以相对不稳定的布局定义各种结构体。这些结构体中的一些是为了提供快速访问而暴露;一些是因为历史原因而暴露。直接依赖这类头文件中的任何结构体都会破坏程序对其他版本libevent的二进制兼容性,有时候是以非常难以调试的方式出现。这类头文件具有后缀“_struct.h”。

(还存在不在event2目录中的较老版本libevent的头文件,请参考下节:如果需要使用老版本libevent)




1. 基础知识

libevent有自己的一套概念体系,要使用libevent,就必须首先理解这套概念体系。


1.1: event_base:

event_base是libevent最基础,最重要的对象。在一个event_base中,可以配置设置,监听事件。


1.1.1:创建和销毁

创建和返回一个event_base. 使用缺省的配置。

struct event_base *event_base_new(void);


创建和返回一个event_base, 使用定制的config.

struct event_base *event_base_new_with_config(const struct event_config *);


销毁一个event_base:

void event_base_free(struct event_base *);

它仅仅负责释放event_base_new()所分配的资源。并不关闭任何文件描述符,或释放任何event所占资源。


1.1.2:运行loop,等待关注的事件发生:

int event_base_loop(struct event_base *base, int flags)

是个loop. 等待event_base中关注的event变成active,并调用event的callback函数。

这个loop会一直运行,直到发生以下情况:

A. event_base 中,已经没有active或pending的Event了。

B. 有人调用了event_base_loopbreak() or event_base_loopexit()


参数flags:

#define EVLOOP_ONCE 0x01

 

#define EVLOOP_NONBLOCK 0x02

#define EVLOOP_NO_EXIT_ON_EMPTY 0x04


EVLOOP_ONCE: event_base_loop()阻塞,直到一个event变成active. 在Active状态的Event的Callback函数执行后,就退出。

EVLOOP_NONBLOCK:loop不会阻塞,它仅仅是查看是否已经有event ready. 有则运行其callback.然后退出

EVLOOP_NO_EXIT_ON_EMPTY 

:哪怕event_base中没有active或者pending的Event了。也不退出。直到调用event_base_loopbreak() or event_base_loopexit().


当flags什么都不设置时,则loop一直运行,检查到event变为active时,运行其callback函数。当没有active或pending的Event后,退出loop。有人调用了event_base_loopbreak() or event_base_loopexit(),也退出loop.



返回值:

0: success. ()

-1: 出错了。

1: 没有Active或pending event了。



int event_base_dispatch(struct event_base *event_base)

它就是简化版的event_base_loop();

int event_base_dispatch(struct event_base *event_base)

{

return (event_base_loop(event_base, 0));

 

}



1.1.3: 停止loop:

若想在event_base中的所有event被移除前,退出loop.可以调用:

int event_base_loopbreak(struct event_base *);

int event_base_loopexit(struct event_base *event_base, const struct timeval *tv)


event_base_loopbreak()会尝试立刻退出loop。 如果正在运行active event的callback(),则等待callback()函数返回后退出loop.

event_base_loopexit()会在tv时间后尝试退出loop.如果正在运行active event的callback(),则等待callback()函数返回后退出loop.


若调用此两个函数时,loop还未运行。则行为不同。

后面详细看看。



若想知道event_base是正常退出loop的,还是调用函数退出的。可以看:

int event_base_got_exit(struct event_base *);

int event_base_got_break(struct event_base *);



int event_base_loopcontinue(struct event_base *);



1.2: event:

event是libevent操作的基本单元。

一个event代表的是一组条件的集和,它包含:文件描述符,这个文件描述符上所感兴趣的事件,回调函数。

1.2.1: event的状态

一个event有三种状态:

A. 已初始化(Initialized)

B. Pending.

C. 激活(Active)

当event被创建并被关联到event_base后,它就是initialized状态。

将event添加入event_base的Pending event列表。此时,它被event_base的loop所监控。

event在Pending状态下,若发生触发条件。则event进入active状态。用户提供的事件回调函数将被运行。

此时,event的状态。要看创建时的设置,如果设置为Persistent.则事件event保持为Pending状态。 否则,则不是Pending状态。 可以用删除将它变回Initialized状态。也可以继续添加为Pending状态。




1.2.2:创建event:

 struct event *event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg)

创建一个event.并与event_base关联。参数fd和events告知触发event的条件(哪些fd发生哪些event), 参数cb和arg告知libevent,当event变成active后该怎么做。


此时,event是initilized状态。

如果出错,返回NULL。




event flags:

#define EV_TIMEOUT 0x01

#define EV_READ 0x02

#define EV_WRITE         0x04

#define EV_SIGNAL         0x08

#define EV_PERSIST 0x10

#define EV_ET         0x20

#define EV_FINALIZE      0x40

#define EV_CLOSED 0x80

  The EV_PERSIST flag can also be passed in the events argument: it makes

  event_add() persistent until event_del() is called.


EV_TIMEOUT:这个标志表示某超时时间流逝后事件成为激活的.构造事件的时候, EV_TIMEOUT标志是被忽略的: 可以在添加事件的时候设置超时,也可以不设置.超时发生时,回调函数的what参数将带有这个标志.


EV_READ:表示指定的文件描述符已经就绪,可以读取的时候,事件将成为激活的.


EV_WRITE:表示指定的文件描述符已经就绪,可以写入的时候,事件将成为激活的.


EV_SIGNAL:用于实现信号检测,请看下面的"构造信号事件"节.


EV_PERSIST:表示事件是"持久的",请看下面的"关于事件持久性"节.


EV_ET:表示如果底层的event_base后端支持边沿触发事件,则事件应该是边沿触发的.这个标志影响EV_READ和EV_WRITE的语义.




1.2.3:设置event为Pending:

int event_add(struct event *ev, const struct timeval *timeout);

把指定的event状态改变为Pending. 这意味着将这个Event加入event_base Loop所关注的列表中。

返回0:Success。  返回-1:出错。



1.2.4: 设置event为Initilizated状态:

int event_del(struct event *);

当event被event_base_loop() 设置为Active,即意味着有触发条件发生。此时,回调函数被调用。event进入No-Pending状态。这时,可以把它设置回Pending状态(event_add())),也可以设置回Initilizated状态。

event_del()就是把event设置回Initilizated状态。 也就是说它不再被Loop所关注。



1.2.5:释放Event:

void event_free(struct event *);

释放event资源。





 

Viewing all articles
Browse latest Browse all 158

Trending Articles