温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Libuv事件循环实现的逻辑是什么

发布时间:2021-12-17 09:27:50 来源:亿速云 阅读:306 作者:iii 栏目:大数据

这篇文章主要介绍“Libuv事件循环实现的逻辑是什么”,在日常操作中,相信很多人在Libuv事件循环实现的逻辑是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Libuv事件循环实现的逻辑是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

Libuv是一个跨平台的的基于事件驱动的异步io库。但是他提供的功能不仅仅是io,包括进程、线程、信号、定时器、进程间通信等。

  • Libuv使用各平台提供的事件驱动模块实现异步(epoll, kqueue, IOCP, event
    ports)。他用来支持上层非文件io的模块。libuv把上层的事件和回调封装成io观察者(uv__io_t)放到底层的事件驱动模块。当事件触发的时候,libuv会执行io观察者中的回调。

  • Libuv实现一个线程池用来支持上层文件io、dns以及用户层耗cpu的任务。

    Libuv的整体执行架构

Libuv事件循环实现的逻辑是什么  


从上图中我们大致了解到,Libuv分为几个阶段,然后在一个循环里不断执行每个阶段里的任务。下面我们具体看一下每个阶段。

1 更新当前事件,在每次事件循环开始的时候,libuv会更新当前事件到变量中,这一轮循环的剩下操作可能使用这个变量获取当前事件,避免过多的系统调用影响性能。

2 如果时间循环是处于alive状态,则开始处理事件循环的每个阶段。否则退出这个事件循环。alive状态是什么意思呢?如果有active和ref状态的handle,active状态的request或者closing状态的handle则认为事件循环是alive的(具体实现后续会分析)。

3 timer阶段:判断最小堆中的节点哪个节点超时了,执行他的回调。

4 pending阶段:执行pending回调。一般来说,所有的io回调(网络,文件,dns)都会在poll io阶段执行。但是有的情况下,poll io阶段的回调会延迟到下一次循环执行,那么这种回调就是在pending阶段执行的。

5 idle阶段:如果节点处理avtive状态,每次事件循环都会被执行(idle不是说事件循环空闲的时候才执行)。

6 prepare阶段:和idle阶段一样。

7 poll io阶段:计算最长等待时间timeout,计算规则:  
       如果时间循环是以UV_RUN_NOWAIT模式运行的,则timeout是0。  
       如果时间循环即将退出(调用了uv_stop),则timeout是0。  
       如果没有active状态的handle或者request,timeout是0。  
       如果有dile阶段的队列里有节点,则timeout是0。  
       如果有handle等待被关闭的(即调了uv_close),timeout是0。  
       如果上面的都不满足,则取timer阶段中最快超时的节点作为timeout,如果没有则timeout等于-1,即永远阻塞,直到满足条件。  
 

8 poll io阶段:调用各平台提供的io多路复用接口,最多等待timeout时间。返回的时候,执行对应的回调。(比如linux下就是epoll模式)

9 check阶段:和idle prepare一样。

10 closing阶段:处理调用了uv_close函数的handle的回调。

11 如果libuv是以UV_RUN_ONCE模式运行的,那事件循环即将退出。但是有一种情况是,poll io阶段的timeout的值是timer阶段的节点的值。并且poll io阶段是因为超时返回的,即没有任何事件发生,也没有执行任何io回调。这时候需要在执行一次timer阶段。因为有节点超时了。

12 一轮事件循环结束,如果libuv以UV_RUN_NOWAIT 或 UV_RUN_ONCE模式运行的,则退出事件循环。如果是以UV_RUN_DEFAULT模式运行的并且状态是alive,则开始下一轮循环。否则退出事件循环。

下面是Libuv事件循环实现的逻辑。

int uv_run(uv_loop_t* loop, uv_run_mode mode) {
  int timeout;
  int r;
  int ran_pending;
  // 在uv_run之前要先提交任务到loop
  r = uv__loop_alive(loop);
  // 事件循环没有任务执行,即将退出,设置一下当前循环的时间
  if (!r)
    uv__update_time(loop);
  // 没有任务需要处理或者调用了uv_stop 
  while (r != 0 && loop->stop_flag == 0) {
    // 更新loop的time字段
    uv__update_time(loop);
    // 执行超时回调
    uv__run_timers(loop);
    // 执行pending回调,ran_pending代表pending队列是否为空,即没有节点可以执行
    ran_pending = uv__run_pending(loop);
    // 继续执行各种队列
    uv__run_idle(loop);
    uv__run_prepare(loop);

    timeout = 0;
    // 执行模式是UV_RUN_ONCE时,如果没有pending节点,才会阻塞式poll io,默认模式也是
    if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
      timeout = uv_backend_timeout(loop);
    // poll io timeout是epoll_wait的超时时间
    uv__io_poll(loop, timeout);
    uv__run_check(loop);
    uv__run_closing_handles(loop);
    // 还有一次执行超时回调的机会,因为poll io阶段可能是因为定时器超时返回的。
    if (mode == UV_RUN_ONCE) {
      uv__update_time(loop);
      uv__run_timers(loop);
    }

    r = uv__loop_alive(loop);
    // 只执行一次,退出循环,UV_RUN_NOWAIT表示在poll io阶段不会阻塞并且循环只执行一次
    if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
      break;
  }
  // 是因为调用了uv_stop退出的,重置flag
  if (loop->stop_flag != 0)
    loop->stop_flag = 0;
  // 返回是否还有活跃的任务(handle或request),业务代表可以再次执行uv_run
  return r;

到此,关于“Libuv事件循环实现的逻辑是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI