• 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

  • 相关阅读:
    关于在MAC上进行 LARAVEL 环境 Homestead 安装过程记录
    js 贷款计算器
    js 实现阶乘
    js 两点间距离函数
    composer Your requirements could not be resolved to an installable set of packages
    vue 项目优化记录 持续更新...
    vue 项目打包
    vue 真机调试页面出现空白
    vue 真机调试
    谈谈-Android状态栏的编辑
  • 原文地址:https://www.cnblogs.com/qigaohua/p/5883400.html
Copyright © 2020-2023  润新知