libevent
Posted tianzeng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了libevent相关的知识,希望对你有一定的参考价值。
- 句柄:I/O框架库处理的对象,即I/O事件,信号和定时事件称为统一事件源,一个事件源通常和一个句柄绑定在一起,句柄的作用是:当内核检测到就绪事件时,他通过句柄来通知应用程序这一事件,I/O事件对应的句柄是文件描述符,信号事件对应的句柄是信号值.
- 事件多路分发器:事件的到来是随机的,异步的,无法预测何时到来一个客户连接或收到一个信号,所以应用程序需要循环等待并处理这件事,这就是事件循环,时间循环中一般采用I/O复用.
- 事件处理器和具体事件处理器:事件处理器执行对应的逻辑业务.通常包含一个或多个hand_event回调函数,这些回调函数放在事件循环中被执行,I/O框架库中事件处理器通常是一个接口,用户继承它并实现,即成为具体事件处理器,因此事件处理器的回调函数一般声明为虚函数;事件处理器还包含一个get_handle它返回与事件处理器关联的句柄,当事件多路分发器检测到有事件发生时,他通过句柄来通知应用程序,必须将事件处理器和句柄绑定,才能在事件发生时获得正确的事件处理器.
- handle——事件源:Linux上是文件描述符,Windows上就是Socket或者Handle了,这里统一称为“句柄集”;程序在指定的句柄上注册关心的事件,在libevent中有三种类型的事件:定时器事件(time event)、信号事件(signal event)和I/O事件。
- event demultiplexer——事件多路分发机制:由操作系统提供的I/O多路复用机制,比如select和epoll。程序首先将其关心的句柄(事件源)及其事件注册到event demultiplexer上;当有事件到达时,event demultiplexer会发出通知事件处理程序“在已经注册的句柄集中,一个或多个句柄的事件已经就绪”;程序收到通知后,就可以在非阻塞的情况下对事件进行处理了。对应到libevent中,依然是select、poll、epoll等,但是libevent使用结构体eventop进行了封装,以统一的接口来支持这些I/O多路复用机制,达到了对外隐藏底层系统机制的目的
- Reactor——反应器:Reactor,是事件管理的接口,内部使用event demultiplexer注册、注销事件;并运行事件循环,当有事件进入“就绪”状态时,调用注册事件的回调函数处理事件。对应到libevent中,就是event_base结构体。
- Event Handler——事件处理程序:事件处理程序提供了一组接口,每个接口对应了一种类型的事件,供Reactor在相应的事件发生时调用,执行相应的事件处理。通常它会绑定一个有效的句柄。对应到libevent中,就是event结构体。
event
struct event { TAILQ_ENTRY(event) ev_active_next; TAILQ_ENTRY(event) ev_next; /* for managing timeouts */ union { TAILQ_ENTRY(event) ev_next_with_common_timeout; int min_heap_idx; } ev_timeout_pos; evutil_socket_t ev_fd; struct event_base *ev_base; union { // I/O事件和信号事件不能同时设置 /* used for io events */ struct { TAILQ_ENTRY(event) ev_io_next; struct timeval ev_timeout; } ev_io; /* used by signal events */ struct { TAILQ_ENTRY(event) ev_signal_next; short ev_ncalls; /* Allows deletes in callback */ short *ev_pncalls; } ev_signal; } _ev; short ev_events; short ev_res; /* result passed to event callback */ short ev_flags; ev_uint8_t ev_pri; /* smaller numbers are higher priority */ ev_uint8_t ev_closure; struct timeval ev_timeout; /* allows us to adopt for different types of events */ void (*ev_callback)(evutil_socket_t, short, void *arg); void *ev_arg; };
- ev_events:所支持的事件类型,可以用所示的标志按位与(互斥的标志不行,如读写事件和信号不能同时设置)
- ev_next:所有已注册过得事件处理器(包括I/O事件处理器和信号事件处理器)通过该成员串成一个尾队列,称之注册事件队列,
- ev_active_next:所有被激活的事件处理器通过该成员串成一个尾队列成为活动事件队列,活动队列不止一个,不同优先级插入到不同活动队列中,再循环事件中,Reactor按优先级从高到低遍历所有活动事件队列,依次处理其中的事件处理器
- ev_times_pos:仅用于定时事件处理器(定时器),这些定时器不存储在时间堆中而是在尾队列中,称为通用定时器队列,通用定时器ev_timeout_pos联合体的ev_next_with_common_timeout成员指出了该定时器在通用定时器队列中的位置,对于其他定时器而言ev_timeout_pos的min_heap_index成员指定了定时器在堆中的位置,一个定时器是否是通用定时器取决于其超时值大小.
- _ev:所有相同描述符值的I/O事件处理器通过ev.ev_io.ev_io_next成员串成一个尾队列,称为I/O事件队列,所有相同信号值得事件处理器通过ev.ev_signal.ev_signal_next成员串成一个尾队列(信号事件队列),ev_ev_signal.ev_ncalls成员指定信号发生时,Reactor需要执行多少次该事该事件对应的件处理器中的回调函数,ev_ev_signal.ev_pncalls要么是NULL要么是指向ev_ev_signal.ev_ncalls.
- 在程序中可能针对一个socket上的可读可写事件创建多个事件处理器(他们拥有不同的回调函数),当事件发生时,所有的事件处理器都被执行,所以事件队列将具有相同文件描述符值得事件处理器你放在一起,当一个文件描述符上的事件发生时,多路事件分发器能很快地把相关的事件处理器添加到活动事件队列中(信号事件队列同因).ev_fd:对于I/O是文件描述符,对于信号是信号; ev_base:该事件处理器从属的ev_base; ev_res:当前活动事件的类型; ev_flags:一些事件可选的标志; ev_pri:事件处理器优先级,值越小优先级越高; ev_closure:指定ev_base指定事件处理器的回调函数时行为; ev_timeout:仅对定时器有效,指定定时器超时值; ev_callback:事件处理器的回调函数,由event_base回调,当会调函数执行时,他的三个参数分别传入事件处理器的如下三个成员:ev_fd,ev_res,ev_flags; ev_arg:回调函数的参数.
源文档解释
/** * @struct event * * Structure to represent a single event. * * An event can have some underlying condition it represents: a socket * becoming readable or writeable (or both), or a signal becoming raised. * (An event that represents no underlying condition is still useful: you * can use one to implement a timer, or to communicate between threads.) * * Generally, you can create events with event_new(), then make them * pending with event_add(). As your event_base runs, it will run the * callbacks of an events whose conditions are triggered. When you * longer want the event, free it with event_free(). * * In more depth: * * An event may be "pending" (one whose condition we are watching), * "active" (one whose condition has triggered and whose callback is about * to run), neither, or both. Events come into existence via * event_assign() or event_new(), and are then neither active nor pending. * * To make an event pending, pass it to event_add(). When doing so, you * can also set a timeout for the event. * * Events become active during an event_base_loop() call when either their * condition has triggered, or when their timeout has elapsed. You can * also activate an event manually using event_active(). The even_base * loop will run the callbacks of active events; after it has done so, it * marks them as no longer active. * * You can make an event non-pending by passing it to event_del(). This * also makes the event non-active. * * Events can be "persistent" or "non-persistent". A non-persistent event * becomes non-pending as soon as it is triggered: thus, it only runs at * most once per call to event_add(). A persistent event remains pending * even when it becomes active: you‘ll need to event_del() it manually in * order to make it non-pending. When a persistent event with a timeout * becomes active, its timeout is reset: this means you can use persistent * events to implement periodic timeouts. * * This should be treated as an opaque structure; you should never read or * write any of its fields directly. For backward compatibility with old * code, it is defined in the event2/event_struct.h header; including this * header may make your code incompatible with other versions of Libevent. * * @see event_new(), event_free(), event_assign(), event_get_assignment(), * event_add(), event_del(), event_active(), event_pending(), * event_get_fd(), event_get_base(), event_get_events(), * event_get_callback(), event_get_callback_arg(), * event_priority_set() */
以上是关于libevent的主要内容,如果未能解决你的问题,请参考以下文章