请注意这是 libev 而不是 libevent 的文章!html
自从接触到 libev 以后,就深深赞同做者精简的设计理念,因而就爱上了 libev 这样简单的I/O库。此外,libev 的大小也比 libevent 小得多而且自由得多。虽然我在公司的项目用的异步 I/O 库仍是以 libevent 和 libubox 为主,可是我的业余的工程中,每每用的是 libev 而不是 libevent。编程
惋惜的是,貌似是由于 libev 是单人维护,并且不支持 Windows 等缘由,并不如 libevent 甚至是 libuv 等受欢迎,国内的研究资料也并很少。segmentfault
可是呢,老子是 Linux / BSD 开发者,我就喜欢!后端
阅读本文最好有 libevent 基础,由于基本概念和 libevent 是相似的安全
库 事件循环 具体事件 ---------------------------------- libevent event_loop event libev ev_loop watcher
其余的在这里我就不重复了。服务器
关于 libevent 请参见个人文章:Libevent官方文档学习笔记网络
本文地址:http://www.javashuo.com/article/p-qlmywexr-dw.html异步
ev_io
:支持 Linux 的select
、poll
、epoll
;BSD 的kqueue
;Solaris 的event port mechanisms
ev_signal
:支持各类信号处理、同步信号处理ev_timer
:相对事件处理ev_periodic
:排程时间表ev_child
:进程状态变化事件ev_start
:监视文件状态ev_fork
:有限的fork事件支持Libev 使用一个ev_tstamp
数据类型来表示1970年以来的秒数,实际类型是 C 里面的double
类型。函数
Libev 使用三种层级的错误:oop
ev_set_syserr_cb
所设置的回调。默认行为是调用abort()
assert
assert
如下函数能够在任意时间调用,用于配置 libev 库:
ev_tstamp ev_time ();
返回当前的时间。
void ev_sleep (ev_tstamp interval);
休眠一段指定的时间。若是interval
小于等于0,则马上返回。最大支持一天,也就是86400秒
int ev_version_major (); int ev_version_minor ();
能够调用这两个函数,而且与系统与定义的EV_VERSION_MAJOR
和EV_VERSION_MINOR
做对比,判断是否应该支持该库
unsigned int ev_supported_backends (); unsigned int ev_recommand_backends (); unsigned int ev_embeddable_backends ();
返回该 libev 库支持的和建议的后端列表
void ev_set_allocator ( void *(*cb)(void *ptr, long size)throw() );
从新设置realloc
函数。对于一些系统(至少包括 BSD 和 Darwin)的 realloc 函数可能不正确,libev 已经给了替代方案。
void ev_set_syserr_cb ( void (*cb)(const char *msg)throw() );
设置系统错误的 callback。默认调用perror()
并abort()
void ev_feed_signal (int signum)
模拟一个signal
事件出来
Event loop 用一个结构体struct ev_loop *
描述。Libev 支持两类 loop,一是 default loop,支持 child process event;动态建立的 event loops 就不支持这个功能
struct ev_loop *ev_default_loop (unsigned int flags);
初始化 default loops。若是已经初始化了,那么直接返回而且忽略 flags。注意这个函数并非线程安全的。只有这个 loop 能够处理ev_child
事件。
struct ev_loop *ev_loop_new (unsigned int flags);
这个函数是线程安全的。通常而言,每一个 thread 使用一个 loop。如下说明 flag 项的各个值:
EVFLAG_AUTO
:默认值,经常使用EVFLAG_NOENV
:指定 libev 不使用LIBEV_FLAGS
环境变量。经常使用于调试和测试EVFLAG_FORKCHECK
:与ev_loop_fork()
相关,本文暂略EVFLAG_NOINOTIFY
:在ev_stat
监听中不使用inotify
APIEVFLAG_SIGNALFD
:在ev_signal
监听中使用signalfd
APIEVFLAG_NOSIGMASK
:使 libev 避免修改 signal mask。这样的话,你要使 signal 是非阻塞的。在将来的 libev 中,这个 mask 将会是默认值。EVBACKEND_SELECT
:通用后端EVBACKEND_POLL
:除了 Windows 以外的全部后端均可以用EVBACKEND_EPOLL
:Linux 后端EVBACKEND_KQUEUE
:大多数 BSD 的后端EVBACKEND_DEVPOLL
:Solaris 8 后端EVBACKEND_PORT
:Solaris 10 后端void ev_loop_destroy (struct ev_loop *loop);
销毁ev_loop
。注意这里要将全部的 IO 清除光以后再调用,由于这个函数并不停止全部活跃(active)的 IO。部分 IO 不会被清除,好比 signal。这些须要手动清除。这个函数通常和ev_loop_new
一块儿出如今同一个线程中。
void ev_loop_fork (struct ev_loop *loop);
这个函数致使ev_run
的子过程重设已有的 backend 的 kernel state。重用父进程建立的 loop。能够和pthread_atfork()
配合使用。
须要在每个须要在 fork 以后重用的 loop 中调用这个函数。必须在恢复以前或者调用ev_run()
以前调用。若是是在fork
以后建立的 loop,不须要调用。
使用 pthread 的代码例以下:
static void post_fork_chuild (void) { ev_loop_fork (EV_DEFAULT); } ... pthread_atfork (NULL, NULL, post_fork_child);
int ev_is_default_loop (struct ev_loop *loop);
判断当前 loop 是否是 default loop。
unsigned int ev_iteration (struct ev_loop *loop);
返回当前的 loop 的迭代数。等于 libev pool 新事件的数量(?)。这个值对应ev_prepare
和ev_check
调用,并在 prepare 和 check 之间增一。
unsigned int ev_depth (struct ev_loop *loop);
返回ev_run()
进入减去退出次数的差值。
注意,致使ev_run
异常退出的调用(setjmp / longjmp, pthread_cancel, 抛出异常等)均不会致使该值减一。
unsigned int ev_backend (struct ev_loop *loop);
返回EVBACKEND_*
值
ev_tstamp ev_now (loop)
获得当前的“event loop time”。在 callback 调用期间,这个值是不变的。
void ev_new_update (loop)
更新从ev_now()
中返回的时间。没必要要的话,不要使用,由于这个函数的开销相对是比较大的。
void ev_suspend (struct ev_loop *loop); void ev_resume (struct ev_loop *loop);
暂停当前的 loop,使其刮起当前的全部工做。同时其 timeout 也会暂停。若是恢复后,timer 会从上一次暂停状态继续及时——这一点对于实现一些要连同时间也一块儿冻结的功能时,很是有用。
注意已经 resume 的loop不能再 resume,反之已经 suspend 的 loop 不能再 suspend。
bool ev_run (struct ev_loop *loop, int flags);
初始化 loop 结束后,调用这个函数开始 loop。若是 flags == 0,直至 loop 没有活跃的时间或者是调用了 ev_bread 以后中止。
Loop 能够是异常使能的,你能够在 callback 中调用longjmp
来终端回调而且跳出 ev_run,或者经过抛出 C++ 异常。这些不会致使 ev_depth 值减小。
EVRUN_NOWAIT
会检查而且执行全部未解决的 events,但若是没有就绪的时间,ev_run 会马上返回。EVRUN_ONCE
会检查全部的 events,在至少每个 event 都执行了一次事件迭代以后才返回。但有时候,使用ev_prepare
/ev_check
更好。
如下是ev_run
的大体工做流程:
ev_break
状态LOOP:
EVFLAG_FORKCHECK
,则检查 fork,若是检测到 fork,则排队并调用全部的 fork watchersev_break
被调用了,则直接跳转至 FINISHev_now
的值ev_now
的值,执行 time jump 调整ev_break
被调用了,或者使用了EVRUN_ONCE
或者EVRUN_NOWAIT
,则若是没有活跃的 watchers,则 FINISH,不然 continueFINISH:
EVBREAK_ONE
,则重设 ev_break 状态void ev_break (struct ev_loop *loop, how);
中断 loop。参数能够是 EVBREAK_ONE
(执行完一个内部调用后返回)或EVBREAK_ALL
(执行完全部)。
下一次调用 ev_run 的时候,相应的标志会清除
void ev_ref (struct ev_loop *loop); void ev_unref (struct ev_loop *loop);
相似于 Objective-C 中的引用计数,只要 reference count 不为0,ev_run 函数就不会返回。
在作 start 以后要 unref;stop 以前要 ref。
void ev_set_io_collect_interval (struct ev_loop *loop, ev_tstamp interval); void ev_set_timeout_collect_interval (struct ev_loop *loop, ev_tstamp interval);
两个值均默认为0,表示尽可能以最小的延迟调用 callback。但这是理想的状况,实际上,好比 select 这样低效的系统调用,因为能够一次性读取不少,因此能够适当地进行延时。经过使用比较高的延迟,可是增长每次处理的数据量,以提升 CPU 效率。
void ev_invoke_pending (struct ev_loop *loop);
调用全部的 pending 的 watchers。这个除了能够在 callback 中调用(少见)以外,更多的是在重载的函数中使用。参见下一个函数
void ev_set_invoke_pending_cb (struct ev_loop *loop, void (*invoke_pending_cb(EV_P)));
重载 ev_loop 调用 watchers 的函数。新的回调应调用 ev_invoke_pending
。若是要恢复默认值,则置喙 ev_invoke_pending 便可。
int ev_pending_count (struct ev_loop *loop);
返回当前有多少个 pending 的 watchers。
void ev_set_loop_release_cb (struct ev_loop *loop, void (*release)(EV_P)throw(), void (*acquire)(EV_P)throw());
这是一个 lock 操做,你能够自定义 lock。其中 release 是 unlock,acquire 是 lock。release 是在 loop 挂起以等待events 以前调用,而且在开始回调以前调用 acquire。
void ev_set_userdata (struct ev_loop *loop, void *data); void *ev_userdata (struct ev_loop *loop);
设置 / 读取 loop 中的用户 data。这一点和 libevent 很不一样,libevent 的参数 / 用户数据是以 event 为单位的,而 libev 的原生用户数据是以 loop 为单位的。
void ev_verify (struct ev_loop *loop);
验证当前 loop 的设置。若是发现问题,则打印 error msg 并 abort()
。
Libev 官方文档学习笔记(1)——概述和 ev_loop(本文)
Libev 官方文档学习笔记(2)——watcher 基础
Libev 官方文档学习笔记(3)——经常使用 watcher 接口
使用 libev 构建 TCP 响应服务器的简单流程