Libevent是用c语言编写的一个轻量级的开源高性能I/O框架库。
使用它的案例有:高性能的分布式内存对象缓存软件memcached,Netchat,Vomit
Libevent是一个典型的Reactor(反应器)模型。
Reactor,是一种高效的事件处理模式。和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的事件发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”。使用Libevent也是向Libevent框架注册相应的事件和回调函数;当这些时间发生时,Libevent会调用这些回调函数处理相应的事件。
Reactor模式,它要求主线程只负责监听文件描述符上是否有事件发生,有的话就立即将该事件通知工作线程,除此之外,主线程不作任何其他实质性的工作。读写数据,接受新的连接,以及处理客户请求均在工作线程中完成。
Reactor模式的I/O框架库包含:句柄(Handle),事件多路分发器(event demultiplexer),事件处理器(EventHandler)和具体的时间处理器(ConcreteEventHandler),Reactor。
1.句柄
即I/O事件、信号和定时事件。Linux下, I/O事件对应的是文件描述符,信号事件对应信号值。
Windows上就是Socket。
2.事件多路分发器
有注册事件和删除事件的方法。还有等待事件的方法demultiplex,内部调用select,poll,epoll
3.事件处理器和具体事件处理器
事件处理器执行事件对应的业务逻辑。他通常包含一个或多个handle_event回调函数。
I/O框架库提供的事件处理器通常是一个接口,用户需要继承他来实现自己的事件处理器,即具体事件处理器。因此事件处理器中的回调函数一般声明为虚函数。以支持用户的扩展。
此外,事件处理器还提供一个get_handle方法,它返回与该事件处理器关联的句柄。 当事件多路分发器检测到有事件发生时,它是通过句柄来通知应用程序的。因此,我们必须将事件处理器和句柄绑定,才能在事件发生时获取到正确的事件处理器。
4.Reactor
它是事件管理的接口,对应到libevent中,就是event_base结构体。
它提供几个主要方法:
1. handle_events。该方法执行事件循环。等待事件,然后依次处理所有就绪事件对应的事件处理器
2.register_handler。该方法调用事件多路分发器的register_event方法往事件多路分发器中注册一个事件。
3.remove_handler。该方法调用事件多路分发器的remove_event方法从事件多路分发器中删除一个事件。
模块之间交互:
1)我们注册具体事件处理器到Reactor中。
2)Reactor调用每个事件处理器的get_handle接口获取其绑定的句柄。
3)Reactor调用handle_events开始事件处理循环。在这里,Reactor会将步骤2获取的所有句柄都收集起来,使用事件多路分发器来等待这些句柄的事件发生。
4)当某个(或某几个)句柄的事件发生时,事件多路分发器通知Reactor
5)Reactor根据发生事件的句柄找出所对应的事件处理器。
6)Reactor调用事件处理器中的handle_event(回调函数)处理事件。
Proactor: 它将所有的I/O操作都交给主线程和内核处理,工作线程仅负责处理业务逻辑。
组成:
1.句柄
2.异步操作处理器
3.异步操作
4.完成事件队列
5.Proactor
6.完成事件接口
7.具体完成事件接口
流程:
1.应用程序启动,调用异步操作处理器提供的异步操作接口函数,调用之后应用程序和异步操作处理就独立运行;应用程序可以调用新的异步操作,而其它操作可以并发进行;
2.应用程序启动Proactor主动器,进行无限的事件循环,等待完成事件到来;
3.异步操作处理器执行异步操作,完成后将结果放入到完成事件队列;
4.Proactor从完成事件队列中取出结果,分发到相应的具体完成事件接口中;
以同步I/O模型(epoll_wait)实现的Reactor模式的工作流程:
1.主线程往epoll_wait内核事件表中注册socket上的读就绪事件
2.主线程调用epoll_wait等待socket上有数据可读
3.当有数据可读时,epoll_wait通知主线程,主线程将socket可读事件放入请求队列
4.睡眠在请求队列上的某个工作线程被唤醒,他从socket读取数据,并处理客户请求,然后往epoll内核事件表中注册该socket上的写就绪事件
5.主线程调用epoll_wait等待socket可写
6.当可写时,epoll_wait通知主线程,主线程将socket可写事件放入请求队列
7.睡眠在请求队列上的某个工作线程被唤醒,他往socket上写入服务器处理客户请求的结果
Proactor:它将所有的I/O操作都交给主线程和内核来处理,工作线程仅仅负责业务逻辑。
使用异步I/O模型(aio_read和aio_write)实现的Proactor模式:
1.主线程调用aio_read函数向内核注册socket上的读完成事件,并告诉内核用户读缓冲区的位置,以及读操作完成时如何通知应用程序(以信号为例)
2.主线程继续处理其他逻辑
3.当socket上的数据被读入用户缓冲区后,内核向应用程序发送一个信号,通知应用程序数据已经可用
4.应用程序预先定义好的信号处理函数选择一个工作线程来处理客户请求,处理完后,调用aio_write函数向内核注册socket上的写完成事件,并告诉内核用户写缓冲区的位置,以及写操作完成时如何通知应用程序
5.主线程继续处理其他逻辑
6.当用户缓冲区的数据被写入socket之后,内核向应用程序发送一个信号,通知应用程序数据已经发送完毕
7.应用程序预先定义好的信号处理函数选择一个工作线程来做善后处理,比如决定是否关闭socket
libevent使用流程:
1)调用event_init创建event_base对象,一个event_base相当于一个Reactor实例。
struct event_base *lib_base = event_base_new()
2) 调用event_new创建事件。设置好事件类型和回调函数。
struct event* listen_event = event_new(base,fd,events,callback,arg)
**base:事件对象
**fd:事件对应的文件描述符或信号,对于定时器设为-1
**events:事件类型,比如 EV_READ,EV_PERSIST, EV_WRITE, EV_SIGNAL
EV_PERSIST作用:事件被触发后,自动重新对这个event调用event_add函数
**callback:事件的回调函数
**arg:回调函数参数
3)调用event_add向libevent添加该事件event。
event_add(listen_event,NULL);
相当于Reactor中的register_handler方法
4)程序调用event_base_dispatch()系列函数进入无线循环,等待事件.
5)事件循环结束后,使用*_free系列函数来释放系统资源。