【问题标题】:How does the Nodejs do i/o under the hood?Nodejs 如何在后台进行 i/o 操作?
【发布时间】:2022-01-16 15:57:40
【问题描述】:

我知道nodejs使用libuv提供的线程池来做并发作业。我读到 nodejs 只需要其中一个线程来执行所有 i/o 绑定活动

那么非阻塞文件操作是如何发生的呢?

如果我遍历一个包含 100 个文件的文件夹,使用 readFile() 打开所有文件,那么在节点、磁盘和操作系统之间实际上会发生什么?

如果在其他一些多线程和阻塞语言中执行相同的操作会导致线程等待 i/o 完成,那么谁在 nodejs 中等待 i/o 完成? ,尤其是同时发生多个文件操作时?

谢谢!

【问题讨论】:

    标签: javascript node.js concurrency io operating-system


    【解决方案1】:

    nodejs 中的文件 IO 使用线程池。默认情况下,池中有 4 个线程。

    当一些 Javascript 在 nodejs 中调用诸如fs.open() 之类的文件操作时,调用会被路由到 nodejs 实现中 libuv 库中的一些本机代码。该代码将特定文件操作添加到等待操作系统线程的项目队列中。如果一个 OS 线程立即可用,则该 OS 线程被赋予执行文件操作的任务,然后原始 libuv 函数返回,该函数返回原始 Javascript,nodejs 继续运行其他 Javascript。

    然后,稍后线程完成文件操作并得到结果。它使用完成的结果和原始文件操作传递的原始回调将事件插入到 nodejs 事件队列中。

    当 nodejs 完成执行它正在运行的任何其他 Javascript 时,它会从事件队列中提取下一个事件并调用与该事件关联的 Javascript 回调并将与该事件关联的数据传递给它。这会触发最初传递给 fs.open() 的 Javascript 回调被调用。

    如果在上面的第二步中,线程池中没有可用的线程,则任务被添加到队列中,libuv 立即将控制权返回给 nodejs,以便它可以运行其他 Javascript。稍后,当池中的一个现有线程完成并完成将事件添加到事件队列的工作时,它将把下一个项目从线程池队列中拉出,给它现在可用的线程并让它开始运行。

    通过这种方式,所有文件操作在 Javascript 方面看起来都是非阻塞和异步的。如果线程池中有可用线程,它们要么立即启动,要么在线程池队列中等待线程可用。这部分对于 Javascript 方面是不可见的。无论哪种方式,操作都已启动或排队,并且控制立即返回给 nodejs 以继续运行其他 Javascript。而且,无论哪种方式,操作都会在稍后的某个时间完成,并且与文件操作关联的 Javascript 回调通过 Javascript 事件队列被调用。这提供了 nodejs 用于所有文件 I/O 的异步、非阻塞、事件驱动机制。

    【讨论】:

    • 感谢您的回复!我还是有些疑惑
    • @akashkhamkar - 有什么疑问?
    • 文件操作是同时发生还是在节点中顺序发生?
    • 我的意思是一旦他们在 libuv 中排队,那之后呢
    • 最多可以在不同的操作系统线程中同时运行 4 个文件操作。如果 nodejs 尝试运行超过 4 个,则其他的将排队,直到现有的 4 个完成。从 nodejs Javascript 的角度来看,所有请求都是非阻塞和异步的——只是有些请求可能会在队列中等待一段时间,然后才能真正开始运行。请注意,对于旋转磁盘,同样类型的队列也发生在磁盘控制器级别,因为磁盘读/写磁头一次只能放置在一个磁道上。
    猜你喜欢
    • 1970-01-01
    • 2015-05-09
    • 1970-01-01
    • 2013-04-02
    • 1970-01-01
    • 2018-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多