1、libev所实现的功能就是一个强大的reactor,可能notity事件主要包括下面这些:
- ev_io // IO可读可写
- ev_stat // 文件属性变化
- ev_async // 激活线程
- ev_signal // 信号处理
- ev_timer // 定时器
- ev_periodic // 周期任务
- ev_child // 子进程状态变化
- ev_fork // 开辟进程
- ev_cleanup // event loop退出触发事件
- ev_idle // 每次event loop空闲触发事件
- ev_embed // TODO(zhangyan04):I have no idea.
- ev_prepare // 每次event loop之前事件
- ev_check // 每次event loop之后事件
宏 | 说明 | 定义 |
---|---|---|
EV_P | event parameter | struct ev_loop* loop |
EV_P_ | EV_P, | |
EV_A | event argument | loop |
EV_A_ | EV_A, |
2、struct ev_loop结构体
event_loop允许动态创建和销毁,并且允许绑定自定义数据
struct ev_loop
struct ev_loop * ev_loop_new (unsigned int flags);
void ev_loop_destroy (EV_P);
void ev_set_userdata (EV_P_ void *data);
void *ev_userdata (EV_P);
我们这里主要关注一下flags.这里面主要是选择使用什么backend来进行poll操作,可以选择的有:
EVBACKEND_SELECT
EVBACKEND_POLL
EVBACKEND_EPOLL // 通常我们选择这个
EVBACKEND_KQUEUE
EVBACKEND_DEVPOLL
EVBACKEND_PORT
但是还有三个比较重要选项:
EVFLAG_NOINOTIFY // 不适用inofity调用来使用ev_stat.这样可以减少fd使用。
EVFLAG_SIGNALFD // 使用signalfd来检测信号是否发生,同样这样可以减少fd使用。
大部分时候我们使用EVFLAG_AUTO(0)一般就足够满足需求了,
从代码角度来看如果支持epoll的话那么首先会选择epoll.
因为在watcher的回调函数里面是可以知道当前event_loop的,
这样就可以获得自定义数据。然后我们看看这个event_loop如何运行和停止的
void ev_run (EV_P_ int flags);
void ev_break (EV_P_ int how);
同样我们这里比较关注flags和how这两个参数。flags有下面这几个:
- 0.通常这是我们想要的,每次轮询在poll都会等待一段时间然后处理pending事件。
- EVRUN_NOWAIT.运行一次,在poll时候不会等待。这样效果相当于只是处理pending事件。
- EVRUN_ONCE.运行一次,但是在poll时候会等待,然后处理pending事件。
而how有下面这几个:
- EVBREAK_ONE.只是退出一次ev_run这个调用。通常来说使用这个就可以了。
- EVBREAK_ALL.退出所有的ev_run调用。这种情况存在于ev_run在pengding处理时候会递归调用。
在event_loop里面我们还关心一件事情,就是每次event_loop轮询的时间长短。
通常来说这个不会是太大问题,但是在高性能情况下面我们需要设置
void ev_set_io_collect_interval (EV_P_ ev_tstamp interval);
void ev_set_timeout_collect_interval (EV_P_ ev_tstamp interval);
3、Watcher
然后我们关心一下EventHandler.在libev下面watcher相当于EventHandler这么一个概念,通常里面会绑定fd回调函数以及我们需要关注的事件。然后一旦触发事件之后会触发我们使用的回调函数,回调函数参数通常有reactor,watcher以及触发的事件。这里不打算重复文档里面的watcher 相关的内容和对应的API,但是对于某些内容的话可能会提到并且附带一些注释。之前我们还是看看通用过程,这里使用TYPE区分不同类型watcher.
typedef void (*)(struct ev_loop *loop, ev_TYPE *watcher, int revents) callback; // callback都是这种类型
ev_init (ev_TYPE *watcher, callback); // 初始化watcher
ev_TYPE_set (ev_TYPE *watcher, [args]); // 设置watcher
ev_TYPE_init (ev_TYPE *watcher, callback, [args]); // 通常使用这个函数最方便,初始化和设置都在这里
ev_TYPE_start (loop, ev_TYPE *watcher); // 注册watcher
ev_TYPE_stop (loop, ev_TYPE *watcher); // 注销watcher
ev_set_priority (ev_TYPE *watcher, int priority); // 设置优先级
ev_feed_event (loop, ev_TYPE *watcher, int revents); // 这个做跨线程通知非常有用,相当于触发了某个事件。
bool ev_is_active (ev_TYPE *watcher); // watcher是否active.
bool ev_is_pending (ev_TYPE *watcher); // watcher是否pending.
int ev_clear_pending (loop, ev_TYPE *watcher); // 清除watcher pending状态并且返回事件
wacther的状态有下面这么几种:
- initialiased. 调用init函数初始化
- active. 调用start进行注册
- pending. 已经触发事件但是没有处理
- inactive. 调用stop注销。这个状态等同于initialised这个状态。
其实关于每个watcher具体是怎么实现的没有太多意思,因为大部分现有代码都差不多。会在下一节说说内部数据结构是怎么安排的,了解内部数据结构以及过程之后很多问题就可以避免了,比如"The special problem of disappearing file descriptors"这类问题。
标签:EV,libev,watcher,使用,ev,TYPE,event,loop From: https://www.cnblogs.com/ink-white/p/16806949.html