第八章 working with events

来源:互联网 发布:unity3d游戏案例 编辑:程序博客网 时间:2024/06/05 18:40

第八章 working with events

libevent的基本操作单元是事件(event)。每个事件代表一个条件集合,包括:
- 一个当前可读的或可写的文件描述符
- 一个变化为可读或可写的文件描述符(仅用于边缘触发)
- 定时器时间到达
- 发生一个信号
- 用户引发一个事件

事件都有相似的生命周期。一旦调用一个libevent函数建立一个事件并将它关联一个event_base,事件就已初始化了。你可以add该事件使得它处理未决状态。当事件是未决时,如果条件出现,事件将变为激活状态,并且用户提供的回调函数将会被调用。如果事件被配置为persistent,它将仍然为未决的。如果不是persistent,在调用回调函数期间,它将会停止未决。你可以通过删除事件的方式让一个未决事件成已非未决,也可以通过add一个非未决事件使它再次未决。

8.1 创建事件对象

#define EV_TIMEOUT      0x01#define EV_READ         0x02#define EV_WRITE        0x04#define EV_SIGNAL       0x08#define EV_PERSIST      0x10#define EV_ET           0x20typedef void (*event_callback_fn)(evutil_socket_t,short,void *);struct event* event_new(struct event_base *base,evutil_socket_t fd,short what,event_callback_fn cb,void *arg);void event_free(struct event *event);

event_new()函数用event_base分配和创建一个新的事件。what是一个上面列表的一个标签集合。如果fd是非负的,它是一个可读或可写的文件。当事件激活时,libevent将会引发调用cb函数。

内部错误或非法参数,event_new返回NULL。

所有的新事件是初始化的和non-pending。为使一个事件是未决的,调用event_add。
析构一个事件,调用event_free。当事件是未决的或激活时,调用event_free是安全的。

8.1.1 事件标签

EV_TIMEOUT:时间完事件变为激活状态

  • EV_READ:当文件描述符可读时,事件变为激活状态
  • EV_WRITE:当文件描述符可写时,事件变为激活状态
  • EV_SIGNAL:用于实现信号检测
  • EV_PERSIST:事件是persisent
  • EV_ET: 事件后台方法支持边缘触发,事件是边缘触发的。

8.1.2 事件的persistence

默认情况下,当一个pending事件变为激活状态时,在执行其回调函数前事件将变为non-pending。因此,如果要合一个事件再次pending,可以在回调函数中调用event_add将它加入到event_base中。

如果一个事件设置了EV_PERSIST,事件将会是persistent。这意味着,当一个事件的回调函数调用时,事件仍然是pending。如果要使它为non-pending,可以在回调函数中调用event_del删除它。

当事件回调运行时,一个persitent事件的超时将会重置。因此,如果有一个EV_READ | EV_PERSIST和一个5s超时,事件将会激活:
- socket可读时
- 事件最后一次激活已过5s

8.1.3 Creating an event as its own callback argument

经常可能会创建一个事件,它的参数为事件本身。你不能仅仅用一个指向事件的指针,因为它不存在。可以使用event_self_cbarg()解决这个问题。

void *event_self_cbarg();

event_self_cbarg()返回一个”magic”指针,告诉event_new()创建一个事件,接收它自已做为回调参数。

Example

#include <event2/event.h>void cb_func(evutil_socket_t fd,short what,void *arg);void run(struct event_base *base){    struct timeval one_sec = {1,0};    struct event *ev;    ev = event_new(base,-1,EV_PERSIST,cb_func,event_self_cbarg());    event_add(ev,&one_sec);    event_base_dispatch(base);}

8.1.4 超时事件(Timeout-only events)

为了方便,这里有一个以evtimer_开头的宏定义集合,可以用它们来分配和操作纯超时事件。使用这些宏可以使代码更清楚。

#define evtimer_new(base,callback,arg) \    event_new((base),-1,0,(callback),(arg))#define evtimer_add(ev,tv) \    event_add((ev),(tv))#define evtimer_del(ev) \    event_del(ev)#define evtimer_pending(ev,tv_out) \    event_pending((ev),EV_TIMEOUT,(tv_out))

8.1.5 创建信号事件(signal events)

libevent也可以监视POSIX信号。创建一个处理信号的事件:

#define evsignal_new(base,signum,callback,arg) \    event_new(base,signum,EV_SIGNAL | EV_PERSIST,cb,arg)

Example:

struct event*hup_event;struct_base *base = event_base_new();hup_event = evsignal_new(base,SIGHUP,sighup_function,NULL);

一些方便的信号事件宏

#define evsignal_add(ev,tv) \    event_add((ev),(tv))#define evsignal_del(ev) \    event_del(ev)#define evsignal_pending(ev,what,tv_out) \    event_pending((ev),(what),(tv_out))

信号事件警告:

当前的libevent版本,多数backends,每个进程中只有一个event_base在同一时刻可以监听信号。如果一次将信号事件加入到两个event_base中–即使信号不同–仅仅只有一个event_base可以收到信号。

kqueue没有这个限制

8.1.6 setting up events without heap-allocation

为了提高性能或其他原因,一些人喜欢将事件作为一个更大的结构体中的一部分。对于每个事件的使用,将会节省:
- 在堆上分配一个小对象的内存开销
- 关联指针到事件的时间开销
- 如果事件已经不在缓存中,可能存在额外的缓存未命中的时间开销

使用该方法的可以产生与其他版本libevent二进制不兼容的风险,因为存在事件结构体大小不一致的问题。如果确实需要使用event_assign来提高堆分配事件的性能,但是可能造成非常能诊断的问题。

int event_assign(struct event *event,struct event_base *base,    evutil_socket_t fd,short what,    void (*callback)(evutil_socket_t,short,void *),void *arg);event指向未初始化的对象。成功返回0,失败返回-1

Example:

#include <event2/event.h>#include <event2/event_struct.h>/*include event_struct.h表示将不会与将来的libevent版本兼容*/#include <stdio.h>struct event_pair{    evutil_socket_t fd;    struct event read_event;    struct event write_event;};void readcb(evutil_socket_t short,void *);void writecb(evutil_socket_t short,void *);struct event_pair*event_pair_new(struct event_base *base,evutil_socket_t fd){    struct event_pair *p = malloc(sizeof(struct event_pair));    if(!p) return NULL;    p->fd = fd;    event_assign(&p->read_event,base,fd,EV_READ|EV_PERSIST,readcb,p);    event_assign(&p->write_event,base,fd,EV_WRITE|EV_PERSIST,writecb,p);}

注:可以使用event_assign()初始化栈分配和静态分配的事件。

注:绝不要在已经pending的事件上调用event_assign(),这样做将导致非常严重的错误。在调用event_assign()之间应该先谳用event_del()使事件non-pending。

8.2 making events pending and non-pending

一旦构建一个事件,在通过将事件add进一个event_base而成为pending,该事件绝不会执行。

  • non-pending:初始化后的事件状态
  • pending:事件通过event_add()进行event_base状态,此时,没有条件发生。
  • active:在event_base中的non-pending状态的事件在其关心的条件发生时,事件将会进入active状态,此时回调函数将会执行。
int event_add(struct event *ev,const struct timeval *tv);

注:在一个non-pending的事件调用event_add,将使事件成功pending状态。成功返回0,失败返回-1.如果tv是NULL,事件将没有超时。否则在tv时间过程会超时。

如果在一个已经pending状态的事件上调用event_add(),事件将会重新安排(以新指定的tv)。如果事件已经pending,重新以tv为NULL调用event_add,event_add()将不会起任何作用。

int event_del(struct event *tv);//删除一个事件使它为non-pending和non-active.如果事件不是pending或active,该函数不会起作用。成功返回0,失败返回-1.

注:如果在事件变为active之后但在它的回高函数执行前删除事件,那么回调孙数将不会执行。

int event_remove_timer(struct event *ev);//

8.3 优先级事件(Events with priorities)

当多个事件同时发生,libevent没有定义一个执行它们回调函数的顺序。可以使用优先级使一些事件比其他事件更重要。有高优先级的事件的回调函数将会被优先执行。

在初始化事件后,加入事件前,可心设置事件优先级。

int event_priority_set(struct event *event,int priority);//事件优先级是0到事件优先级数-1之间的一个数。成功返回0,失败返回-1.

当多个优先级的多个事件active时,低优先级事件不会运行,而是运行高优先级事件,然后再检查事件状态。仅仅当没有高优先级事件是active时,才运行低先级active事件。
注:可以会饿死低优先级事件。

8.4 检查事件状态(Inspecting event status)

“`
int event_pending(const struct event *ev,short what,struct timeval *tv_out);

define event_get_signal(ev)

evutil_socket_t event_get_fd(const struct event *ev);
struct event_base *event_get_base(const struct event *ev);
short event_get_events(const struct event *tv);
event_callback_fn event_get_callback(const struct event *ev);
void *event_get_callback_arg(const struct event *ev);
int event_get_priority(const struct event *ev);

void event_get_assignment(const struct event *event,
struct event_base **base_out,
evutil_socket_t *fd_out,
short *events_out,
event_callback_fn *callback_out,
void **arg_out);

“`

event_pending函数判断给定的事件是pending或者active.如果是并且waht设置了EV_READ,EV_WRITE,EV_SIGNAL和EV_TIMEOUT中的任何一个,函数将返回当前是pending或active的标签(flags)。

## 8.5 查找当前运行事件(Finding the currently running event)

为了debug或其它目的,可以获得当前运行事件的指针。

“`
struct event *event_base_get_running_event(struct event_base *base);

“`

## 8.6 Configuring one-off events

“`
int event_base_once(struct event_base ,evutil_socket_t short,void ()(evutil_socket_t,short,void ),void ,const struct timeval*);

“`
该函数与event_new()一样,但不支持EV_SIGNAL和EV_PERSIST。当回调完成时,libevent释放它自已的事件结构。成功返回0,失败返回-1.

使用event_base_once插入的事件不能删除和手动激活。

## 8.7 Manually activating an event

可以手动激活一个事件,即使事件条件没有发生。

“`
void event_active(struct event *ev,int what short ncalls);

“`
该函数可以使waht标记的事件激活。事件不必是pending,激活事件也不会使事件pending.

0 0
原创粉丝点击