libuv和操作系统如何在Node.js中实际调度setTimeout和setInterval等定时器?我发现在计时器触发之前,节点进程没有使用CPU。这是否意味着操作系统会调度计时器,并在计时器被触发时唤醒节点进程?如果是这样,操作系统如何安排计时器以及硬件如何执行它?
答案 0 :(得分:4)
Node使用下面的libuv来处理这个问题。虽然setTimeout有自己的内部管理,但它最终使用libuv提供的uv_timer_t工具。
让我们假设事件循环唯一做的是计时器。 libuv将计算轮询超时,这实际上是计时器的到期时间(在此示例中)。然后事件循环将通过使用适当的系统调用(epoll_wait,kevent等)来阻止i / o。在那时,由内核来决定做什么,但是当前的执行线程被阻塞,直到内核再次唤醒它,所以这里没有使用过的CPU,因为什么也没发生。
一旦超时到期,上述系统调用将返回,libuv将处理到期时间和i / o。
答案 1 :(得分:2)
计时器回调是作为NodeJS事件循环的一部分执行的。调用setTimeout
或setInterval
时,libuv(实现NodeJS事件循环的C库)在“最小堆”数据结构中创建一个计时器,称为计时器堆。在此数据结构中,它跟踪每个计时器到期的时间戳。
在事件循环的每个新迭代的开始,libuv调用uv__update_time
,后者依次调用syscall以获取当前时间并以毫秒为单位更新当前循环时间。 (https://github.com/nodejs/node/blob/master/deps/uv/src/unix/core.c#L375)
紧接在此步骤之后的是事件循环迭代的第一个主要阶段,即计时器阶段。在此阶段,libuv调用uv__run_timers
来处理所有过期计时器的回调。在此阶段,libuv遍历计时器堆,以根据刚刚使用uv__update_time
更新的“循环时间”来识别过期的计时器。然后,它将调用所有这些过期计时器的回调。
以下是NodeJS中事件循环实现中的摘要片段,以突出显示我刚才描述的内容。
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