【问题标题】:How does libuv and Node.js actually schedule timers?libuv 和 Node.js 是如何实际调度计时器的?
【发布时间】:2015-01-07 02:17:52
【问题描述】:

libuv 和操作系统如何在 Node.js 中实际调度 setTimeout 和 setInterval 等定时器?我看到在计时器触发之前,节点进程没有使用 CPU。这是否意味着操作系统会调度定时器,并在定时器触发时唤醒 Node 进程?如果是这样,操作系统如何安排计时器以及硬件如何执行它?

【问题讨论】:

    标签: node.js timer interrupt libuv


    【解决方案1】:

    计时器回调作为 NodeJS 事件循环的一部分执行。当您调用setTimeoutsetInterval 时,libuv(实现 NodeJS 事件循环的 C 库)在称为计时器堆的“最小堆”数据结构中创建一个计时器。在这个数据结构中,它跟踪每个计时器到期的时间戳。

    在事件循环的每次新迭代开始时,libuv 调用uv__update_time,后者又调用系统调用来获取当前时间并将当前循环时间更新到毫秒精度。 (https://github.com/nodejs/node/blob/master/deps/uv/src/unix/core.c#L375)

    紧随该步骤之后是事件循环迭代的第一个主要阶段,即计时器阶段。在这个阶段,libuv 会调用uv__run_timers 来处理所有过期定时器的回调。在这个阶段,libuv 遍历计时器堆,根据它刚刚使用uv__update_time 更新的“循环时间”来识别过期的计时器。然后它调用所有过期计时器的回调。

    以下是来自 NodeJS 中的事件循环实现的经过编辑的 sn-p,以突出我刚刚描述的内容。

    while (r != 0 && loop->stop_flag == 0) {
        uv__update_time(loop);
        uv__run_timers(loop);
        
        // ...redacted for brevity...
    
        r = uv__loop_alive(loop);
        if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
          break;
      }
    

    前段时间我写了一系列关于 NodeJS 事件循环的文章。我希望该系列中的这篇文章会有所帮助。 https://blog.insiderattack.net/timers-immediates-and-process-nexttick-nodejs-event-loop-part-2-2c53fd511bb3

    【讨论】:

      【解决方案2】:

      Node 在下面使用 libuv 来处理这个问题。虽然 setTimeout 有自己的一些内部管理,但它最终使用 libuv 提供的 uv_timer_t 工具。

      让我们假设事件循环唯一在做的事情就是计时器。 libuv 将计算轮询超时,这实际上是计时器的到期时间(在本例中)。然后事件循环将通过使用适当的系统调用(epoll_wait、kevent 等)阻止 i/o。此时由内核决定要做什么,但是当前的执行线程被阻塞,直到内核再次唤醒它,所以这里没有使用 CPU,因为什么都没有发生。

      一旦超时,上述系统调用将返回,libuv 将处理到期的计时器和 i/o。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-12-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-01-18
        • 2010-10-15
        • 2020-07-24
        • 1970-01-01
        相关资源
        最近更新 更多