NodeJS 异步 IO



异步 I/O 是最理想的 I/O 模型,然而可惜的是真正的异步 I/O 并不存在。 Linux 上的 AIO 通过信号和回调来传递数据,但是存在缺陷。现有的 libeio 以及 Windows 上的 IOCP,本质上都是利用线程池与阻塞 I/O 来模拟异步 I/O。
事件循环遵循相当常见的单线程异步 I/O 方法:所有(网络) I/O 都在非阻塞套接字上执行,这些套接字使用给定平台上可用的最佳机制进行轮询:Linux 上的 epoll,OSX 上的 kqueue 和其他 BSD,SunOS 上的 event ports 和Windows上的 IOCP。 作为循环迭代的一部分,循环将阻塞等待已添加到轮询器的套接字上的 I/O 活动,并且将触发回调指示套接字条件(可读,可写挂断),因此句柄可以读取,写入或执行所需的 I/O 操作。

libuv 使用线程池来实现异步 File I/O (阻塞)操作,但网络 I/O 总是在单个线程中执行

// 操作系统读取文件:进程 P 获取到文件 /home/user/test.txt 的描述符以后,还需要再发起系统调用 read,传入文件描述符来读取文件内容,同样读取操作需要切换到内核态由内核代为完成。切换到内核态以后,操作系统通过文件描述符找到对应的 inode ,通过 inode 来确定文件存储在磁盘哪些扇区中,然后向磁盘发送指令来读取这些扇区,把内容读取到内核的地址空间里面。一般来说操作系统会通过 memory mapped IO 技术把键盘、磁盘等硬件上的寄存器连接到 IO 总线,再通过 IO 控制器连接到内存总线,这样硬件上的寄存器也被映射到了一段内存地址上,CPU 可以直接通过读写内存的指令来读写硬件寄存器中的数据。同时还会通过 DMA 技术来让硬件不通过 CPU,直接读写内存的内容,这样磁盘在传输文件的同时 CPU 可以去执行其他线程。