libev 简单的I/O库.  a high performance full featured event loop written in c

libev 的大小也比 libevent 小得多并且自由得多. 很多概念和 libevent 是类似的.

貌似是因为 libev 是单人维护,而且不支持 Windows 等原因,并不如 libevent 甚至是 libuv 等受欢迎,国内的研究资料也并不多。

库        事件循环      具体事件
----------------------------------
libevent event_loop event
libev ev_loop watcher

ev_io:支持 Linux 的select、poll、epoll;BSD 的kqueue;Solaris 的event port mechanismsev_signal:支持各种信号处理、同步信号处理ev_timer:相对事件处理ev_periodic:排程时间表ev_child:进程状态变化事件ev_start:监视文件状态ev_fork:有限的fork事件支持

1、ev_loop是libev用来描述事件循环的结构体。在libev中的定义比较绕,这里把它摘抄出来,做下注释,方便学习。libev的定义如下
  ev_tstamp 是double类型,是一个时间事件的计数类型,这里使用define来定义ev_rt_now为((loop)->ev_rt_now),程序里可以直接使用ev_rt_now,后边结构体的定义都是这样,这可能是作者的一些编程技巧。
  这两行表示了一个意思,用type来定义name,就是等于 type name;如 int backend;相当于在结构体中定义了一个变量。具体变量在ev_vars.h头文件中定义。
  ev_warp.h文件的作用和#define ev_rt_now ((loop)->ev_rt_now) 相同。具体两个文件代码如下:

概述

  features:

  • ev_io:支持 Linux 的selectpollepoll;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 使用三种层级的错误:

  1. 操作系统错误:调用ev_set_syserr_cb所设置的回调。默认行为是调用abort()

  2. 参数错误:调用assert

  3. 内部错误(bug):内部调用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_MAJOREV_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 loops 的函数

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 API

  • EVFLAG_SIGNALFD:在ev_signal监听中使用signalfd API

  • EVFLAG_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_prepareev_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的大致工作流程:

  • loop depth ++

  • 重设ev_break状态

  • 在首次迭代之前,调用所有 pending watchers

LOOP:

  • 如果置了EVFLAG_FORKCHECK,则检查 fork,如果检测到 fork,则排队并调用所有的 fork watchers

  • 排队并且调用所有 ready 的watchers

  • 如果ev_break被调用了,则直接跳转至 FINISH

  • 如果检测到了 fork,则分离并且重建 kernel state

  • 使用所有未解决的变化更新 kernel state

  • 更新ev_now的值

  • 计算要 sleep 或 block 多久

  • 如果指定了的话,sleep

  • loop iteration ++

  • 阻塞以等待事件

  • 排队所有未处理的I/O事件

  • 更新ev_now的值,执行 time jump 调整

  • 排队所有超时事件

  • 排队所有定期事件

  • 排队所有优先级高于 pending 事件的 idle watchers

  • 排队所有 check watchers

  • 按照上述顺序的逆序,调用 watchers (check watchers -> idle watchers -> 定期事件 -> 计时器超时事件 -> fd事件)。信号和 child watchers 视为 fd watchers。

  • 如果ev_break被调用了,或者使用了EVRUN_ONCE或者EVRUN_NOWAIT,则如果没有活跃的 watchers,则 FINISH,否则 continue

FINISH:

  • 如果是EVBREAK_ONE,则重设 ev_break 状态

  • loop depth --

  • return

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()

链接:

    1. libev 官网                                              http://libev.schmorp.de/

    1. libev简单使用介绍              http://simohayha.iteye.com/blog/306712

    2. libev 中 ev_loop 结构体中的成员变量       http://www.cnblogs.com/pang1567/p/4069941.html

    3. Libev 官方文档学习笔记(1)——概述和 ev_loop    http://www.zhimengzhe.com/linux/95594.html

libev 学习使用的更多相关文章

  1. libev学习笔记

    转 libev的使用--结合Socket编程 作者:cxy450019566 之前自己学过一些libev编程的基础,这次写压测刚好用上了,才算真正动手写了些东西,在这里做一些总结.写这篇文章是为了用浅 ...

  2. 【转载】使用事件模型 & libev学习

    参考这篇文章: http://www.ibm.com/developerworks/cn/linux/l-cn-edntwk/ 这里面使用的是 libev ,不是libevent Nodejs就是采用 ...

  3. libev学习(一)

    一.libev简介 Libev是一个事件循环:你注册感兴趣的特定事件(比如一个文件可以读取时或者发生超时时),它将管理这些事件源,将这些事件反馈给你的程序.为了实现这些,至少要在你的进程(或线程)中执 ...

  4. Libev学习笔记4

    这一节首先分析Libev的定时器部分,然后分析signal部分. 对定时器的使用主要有两个函数: ev_timer_init (&timeout_watcher, timeout_cb, .) ...

  5. Libev学习笔记3

    设置完需要监听的事件之后,就开始event loop了.在Libev中,该工作由ev_run函数完成.它的大致流程如下: int ev_run (EV_P_ int flags) { do { /* ...

  6. Libev学习笔记2

    这一节根据官方文档给出的简单示例,深入代码内部,了解其实现机制.示例代码如下: int main (void) { struct ev_loop *loop = EV_DEFAULT; ev_io_i ...

  7. Libev学习笔记1

    和Libevent相似,Libev是一个高性事件驱动框架,据说性能比Libevent要高,bug比Libevent要少.Libev只是一个事件驱动框架,不是网络库,因为它的内部并没有任何socket编 ...

  8. libev学习之ev_run

    好吧,神马都init好了,loop毕竟是个环呐,在哪跑起来呢,ok,他是ev_run的工作: int ev_run (EV_P_ int flags) { #if EV_FEATURE_API ++l ...

  9. libev学习代码

随机推荐

  1. 详解python 局部变量与全局变量

    本文将详细分析python的全局变量与局部变量,学过php的人都知道,php里面的全局变量是无法在function里面去使用的,只有超全局变量才可以,那么python会怎么处理全局变量与局部变量呢?下 ...

  2. OJ题归纳

    1.求最大公约数 利用辗转相除法求最大公约数 int gcd(int a,int b) { int c,r; if(a<b){c=a;a=b;b=c;} if(b==0) return a; r ...

  3. Linux e1000e网卡驱动

    目录 识别网卡 命令行参数 附加配置 技术支持 一.识别网卡e1000e驱动支持Intel所有的GbE PCIe网卡,除了82575,82576,基于82580系列的网卡.提示:Intel(R) PR ...

  4. 编译程序提示配置PKG_CONFIG_PATH

    http://blog.csdn.net/langeldep/article/details/6804331 在安装开源软件的过程中, 经常会碰到提示配置PKG_CONFIG_PATH路径, 或者直接 ...

  5. Xampp+Openfire+Spark的简单使用

    Openfire与Spark的简单实用 1.安装Openfire 百度云 提取码:uu11 2.查找路径 /usr/local/openfire 这时候需要将openfire的文件属性都设置为 可读可 ...

  6. Java面试题(下)

    这部分主要是开源Java EE框架方面的内容,包括hibernate.MyBatis.spring.Spring MVC等,由于Struts 2已经是明日黄花,在这里就不讨论Struts 2的面试题, ...

  7. java基础知识(一)- 数据类型

    Java有两大数据类型: 基本数据类型,基本数据类型都可以直接分配到栈中 引用数据类型,引用存放在栈中,对象本身存放在堆中 基本数据类型共有四类八种 第一类:字符型(char) 第二类:逻辑型(boo ...

  8. [Leetcode] Length of last word 最后一个单词的长度

    Given a string s consists of upper/lower-case alphabets and empty space characters' ', return the le ...

  9. react router路由传参

    今天,我们要讨论的是react router中Link传值的三种表现形式.分别为通过通配符传参.query传参和state传参. ps:进入正题前,先说明一下,以下的所有内容都是在react-rout ...

  10. remove computer from join with powershell

    Removes the local computer from its domain. Remove-Computer [-UnjoinDomainCredential] <PSCredenti ...