libev教程一:libev简单入门

1 libev

1.1 Introduction

主页http://software.schmorp.de/pkg/libev.html

 文档http://software.schmorp.de/pkg/libev.html

 libev所实现的功能就是一个强大的reactor,可能notify事件主要包括下面这些:

  • 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之后事件

1.2 About The Code

代码风格相当严谨而且排版也非常工整,并且从域名看出作者是德国人。但是内部使用了大量的宏造成阅读代码并不是非常方便。

并且从代码角度分析,应该是一开始支持有一个默认的event_loop,但是随着多核产生实际应用中可能会使用到多个event_loop, 猜想作者应该是为了方便的话使用了很多宏进行替换。

允许使用多个event_loop的宏是EV_MULTIPLICITY.

比如下面这段代码

void noinline
ev_io_start (EV_P_ ev_io *w)
{
int fd = w->fd;

if (expect_false (ev_is_active (w)))
return;

assert (("libev: ev_io_start called with negative fd", fd >= 0));
assert (("libev: ev_io_start called with illegal event mask", !(w->events & ~(EV__IOFDSET | EV_READ | EV_WRITE))));

EV_FREQUENT_CHECK;

ev_start (EV_A_ (W)w, 1);
array_needsize (ANFD, anfds, anfdmax, fd + 1, array_init_zero);
wlist_add (&anfds[fd].head, (WL)w);

fd_change (EV_A_ fd, w->events & EV__IOFDSET | EV_ANFD_REIFY);
w->events &= ~EV__IOFDSET;

EV_FREQUENT_CHECK;
}


初次阅读这个代码会觉得非常难懂。

说明 定义
EV_P event parameter            struct ev_loop* loop
EV_P_             EV_P,
EV_A event argument           loop
EV_A_             EV_A,

然后很多变量只要是ev_loop成员的话都被封装成为了宏。比如代码里面的anfds,实际上的宏定义是
#define anfds           ((loop)->anfds) 

事实上一个ev_loop里面的字段是相当多的,不过也很正常本身就是一个强大的reactor.但是这造成一个直接后果,就是对于想要了解ev_loop的全貌比较困难,所以想要彻底地了解libev也比较麻烦,所以我们只能够从应用层面来尝试了解它。

1.3 EventLoop

首先我们关注一下reactor本身。在libev下面reactor对象称为event_loop.

event_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处理时候会递归调用。

在backend/epoll底层每次epoll_wait时候,libev提供了接口回调可以在epoll_wait前后调用

void ev_set_loop_release_cb (loop, void (*release)(EV_P), void (*acquire)(EV_P))
static void
epoll_poll (EV_P_ ev_tstamp timeout)
{
/* epoll wait times cannot be larger than (LONG_MAX - 999UL) / HZ msecs, which is below */
/* the default libev max wait time, however. */
EV_RELEASE_CB;
eventcnt = epoll_wait (backend_fd, epoll_events, epoll_eventmax,
epoll_epermcnt ? 0 : ev_timeout_to_ms (timeout));
EV_ACQUIRE_CB;
}


在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);

在ev_run里面有使用这些参数的代码比较麻烦。但是大意是这样,如果我们这是了timeout_interval的话,那么我们每次检查timeout时间的话必须在timeout_interval,使用这段时间ev_sleep.但是这个又会影响到io_interval,所以内部做了一些换算,换算的结果作为epoll_wait超时时间。不过同样在大部分时候我们不需要关心它,默认时候是0.0,系统会使用最快的响应方式来处理。

1.4 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_ev
  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值