zoukankan      html  css  js  c++  java
  • libev 学习使用

     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

  • 相关阅读:
    poj 3068 Bridge Across Islands
    XidianOJ 1086 Flappy v8
    XidianOJ 1036 分配宝藏
    XidianOJ 1090 爬树的V8
    XidianOJ 1088 AK后的V8
    XidianOJ 1062 Black King Bar
    XidianOJ 1091 看Dota视频的V8
    XidianOJ 1098 突击数论前的xry111
    XidianOJ 1019 自然数的秘密
    XidianOJ 1109 Too Naive
  • 原文地址:https://www.cnblogs.com/doscho/p/7729247.html
Copyright © 2011-2022 走看看