Libuv简介

Libuv是一个跨平台的的基于事件驱动的异步io库。但是他提供的功能不仅仅是io,包括进程、线程、信号、定时器、进程间通信等。下面是来自官网对Libuv架构的介绍图。

从上图中我们看到

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

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;
} 

文档 http://docs.libuv.org/en/v1.x/design.html

本文分享自微信公众号 - 编程杂技(theanarkh),作者:theanarkh

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-05-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • libuv源码解析之数据结构全景图

    theanarkh
  • libuv之inotify源码分析

    inotify是linux系统提供用于监听文件系统的机制。inotify机制的逻辑大致是 1 init_inotify创建一个inotify机制的实例,返回一个...

    theanarkh
  • libuv之async.c源码解析

    libuv的async.c实现了线程和主线程的通信。在uv_loop_init函数中对async进行初始化。

    theanarkh
  • XISE菜刀+模板制作[源文件]

    支持批量查找可写目录功能(只支持asp php),并在生成内页和批量上传直接调用。

    C4rpeDime
  • Nodejs探秘:深入理解单线程实现高并发原理

    从Node.js进入我们的视野时,我们所知道的它就由这些关键字组成 事件驱动、非阻塞I/O、高效、轻量,它在官网中也是这么描述自己的。 Node.js® is ...

    IMWeb前端团队
  • IDEA 自定义快捷代码块

    设置入口位置 settings -> Editor -> Live Template

    java攻城狮
  • 学会这 18 个工具,你一定能真正理解如何监控网络带宽!

    本文介绍了一些可以用来监控网络使用情况的Linux命令行工具。这些工具可以监控通过网络接口传输的数据,并测量目前哪些数据所传输的速度。入站流量和出站流量分开来显...

    民工哥
  • 分享一个超牛逼的 Java 文件在线预览项目

    基于 OpenOffice 服务将文件 (.doc、.docx、.xls、.ppt) 转化为pdf、html格式,然后直接在浏览器渲染输出。

    Leetcode名企之路
  • 如何写好 5000 行的 SQL 代码

    经历了大大小小的 MIS 系统,小到几人用的协作系统,几十人用的 OA 系统,到上千人用的 MES/ERP 系统,再到百万人用的电商系统,存储过程的影子在半个世...

    Lenis
  • 移动端开发-iPhone、iPad的默认按钮样式等开发经验

    HTML5学堂:从JS的逻辑层来说,移动端远远不如PC平台,但是“恐怖的”兼容问题是移动端最为头疼的地方。本文介绍iPad与iPhone的默认按钮样式的处理方法...

    HTML5学堂

扫码关注云+社区

领取腾讯云代金券