zoukankan      html  css  js  c++  java
  • libev学习(一)

    一.libev简介

      Libev是一个事件循环:你注册感兴趣的特定事件(比如一个文件可以读取时或者发生超时时),它将管理这些事件源,将这些事件反馈给你的程序。为了实现这些,至少要在你的进程(或线程)中执行事件循环句柄控制,然后就能通过回调机制进行事件通信。你通过所谓的watchers注册感兴趣的特定事件,这些watchers都是相对较小的C语言结构体,它们通过初始化具体的事件得到,然后交由libev启动那个watcher

         Libev支持 select,poll,Linux特有的epoll,BSD特有的kqueue以及Solaris特有的文件描述符事件端口机制(ev_io),Linux信息通知接口(ev_stat),Linux事件文件/信号文件(为了更快更完整的唤醒沉睡线程(ev_async/信号捕捉(ev_signal))相对定时器(ev_timer),用户自定义的绝对定时器(ev_periodic),同步信号(ev_signal),进程状态改变事件(ev_child),和通过事件循环机制实现的事件观察者管理本身(ev_idle,ev_embed,ev_prepareev_check监控)也和文件监控(ev_stat)和有限支持的派生子进程事件(ev_fork)一样。

     

    二.libev特点

      1.关于时间:时间类型为ev_tstampLibev描述时间采用一个浮点数,它来自于距离(POSIX)时期的一个(带小数)的秒数)。

      2.错误处理Libev 知道三类错误:操作系统错误、用法错误和内部错误(bugs) 

    当 libev 捕捉到无法处理的操作系统错误时(如一个系统调用返回了libev不能识别的返回值),它通过调用 ev_set_syserr_cb设定的回调函数,它假设这个回调函数可修正这个问题或者是退出。默认行为是打印一个摘要信息并调用 abort()

    当 libev 检测到用法错误(如负的时间),它会打印一条摘要信息并退出(使用 assert 机制,因此宏 NDEBUG 会关闭这种检测); 表明这里有libev调用端的编程错误需要修正。

    Libev也有一些内部错误检查,也有大多数的代码错误检查。这些在正常情况下不会触发,他们表明在libev存在一个bug或者更大的错误。

     

    3.全局函数(这些函数可以在任何时候被调用,甚至可以在初始化libev库之前调用

      ev_tstamp ev_time ()             //返回libev使用它时的当前时间。请注意,ev_now函数通常更快,也经常用来返回你想知道的时间戳。ev_now_update和ev_now组合在一起使用更好

      ev_sleep (ev_tstamp interval)   //休眠指定的时间间隔:当前线程将被阻塞直到它被中断或者给定的时间间隔已经过去。当interval <= 0.时将立即返回.基本上这是一个精度稍低的sleep()函数.Interval的值是有限制的-libev只保证休眠时间最长为一天(interval <= 86400 

           int ev_version_major ()

           int ev_version_minor ()  //通过调用ev_version_major 和ev_version_minor函数,你可以找出主要的和次要的ABI链接库版本号。如果需要,你可以比较全局字段EV_VERSION_MAJOR 和 EV_VERSION_MINOR,它们表明了你的程序编译完成后的库版本

      ev_set_allocator (void *(*cb)(void *ptr, long size))  //这是申请或释放内存的,如果申请内存是返回0,当内存需要被分配(大小!= 0),库可能中止或采取一些潜在的破坏性的行动

         例:

      static void *
       persistent_realloc (void *ptr, size_t size)
       {
         for (;;)
           {
             void *newptr = realloc (ptr, size);
    
             if (newptr)
               return newptr;
    
             sleep (60);
           }
       }
    
       ...
       ev_set_allocator (persistent_realloc);
    View Code


         ev_set_syserr_cb (void (*cb)(const char *msg))   //设置发生回系统错误时的调函数

      例:    

     static void
       fatal_error (const char *msg)
       {
         perror (msg);
         abort ();
       }
    
       ...
       ev_set_syserr_cb (fatal_error);
    View Code

      ev_feed_signal (int signum) //这个函数可以用来“模拟”一个信号接收。它是完全安全的调用这个函数在任何时间,从任何上下文,包括信号处理程序或随机线程

       

      4.该库知道两种类型的循环,默认的循环(支持子进程事件)和动态创建事件循环

     

             struct ev_loop *ev_default_loop (unsigned int flags)

     常用:ev_default_loop (EVBACKEND_POLL | EVBACKEND_SELECT | EVFLAG_NOENV); //限制测试程序的select、poll的后端,不允许环境设置要考虑

      struct ev_loop *ev_loop_new (unsigned int flags)   //这个函数是线程安全的,常用方式:使用ev_loop_new创建一个时间循环在每个线程中,在主线程中使用默认事件循环。   The flags argumentis usually specified as 0 (or EVFLAG_AUTO).

      flags argumentis

    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 后端

             ev_loop_destroy (loop)    //Destroys an event loop object

         ev_loop_fork (loop)

             int  ev_is_default_loop (loop)   //判断是否是默认事件循环,是返回ture

            unsigned int ev_iteration (loop)      //返回当前的 loop 的迭代数。等于 libev pool 新事件的数量(?)。这个值对应ev_prepareev_check调用,并在 prepare 和 check 之间增一

           unsigned int ev_depth (loop)   //返回ev_run()进入减去退出次数的差值

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

      ps:  来自libev手册

    https://my.oschina.net/haopengstack/blog/511585

  • 相关阅读:
    oracle中 char,varchar,varchar2的区别
    .net + oracle + win2003部署遇到的问题集合
    Oracle下如何获得随机数,如何保留小数,如何取整数
    Oracle 函数大全(字符串函数,数学函数,日期函数,逻辑运算函数,其他函数)
    Service和广播联合更新UI的例子
    [python] 类常用的内置方法
    android textview 换行 "\r\n"
    java操作excel
    项目指导原则
    SVN与bugzilla整合
  • 原文地址:https://www.cnblogs.com/qigaohua/p/5883400.html
Copyright © 2011-2022 走看看