关于NIO与OS的思考
起因
最近在学习分布式课程的时候 , 涉及到ZK实现可扩展锁的章节 , 提到了 事件驱动 与 多线程 , 很自然的联想到了NIO有关的知识。
想起来之前在纠结的一个问题 :
NIO以为这 No Blocking I/O , 也就是我们当前的线程不会继续等待本次的I/O事件 , 那么究竟是谁在执行这里的I/O? 这里I/O是否会占用线程/进程的时间片?
首先我们假设,NIO不会占用线程/进程的时间片, 并且这里的I/O操作是由于OS中的组件完成的,
在此之前简单回顾NIO 与 OS 相关的知识。
NIO基础
更详细的内容请参考 : https://blog.dhx.icu/2023/05/netty/Netty网络编程(1)JavaNIO-1[Netty]/
Java NIO(New I/O)是从Java 1.4版本开始引入的一个新的I/O API,可以用于替代标准的Java I/O API。
NIO与原来的I/O有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的、基于通道的I/O操作。
NIO | I/O |
---|---|
面向流(Stream Oriented) | 面向缓冲区(Buffer Oriented) |
阻塞I/O(Blocking I/O) | 非阻塞I/O(Non Blocking I/O) |
选择器(Selectors |
NIO的设计还是为了数据传输。对于channel,如果说之前的文件流类似于水流,那么channel就类似于铁路。对于铁路本身,他是不能直接传输数据的,需要我们配备上火车车厢,而channel的作用就是连接初始地点以及目标地点。因此注意通道本身不能传输数据,要想传输数据必须要有缓冲区。如果你想要把数据写入到文件中,那么就可以先把部分的数据写入到缓冲区中, 通过通道把这部分数据运输到目标的文件,反之亦然。因此说现在的面向缓冲区的传输方式是双向的。
Talk is cheap , show me the code
下面是Java的NIO API简单示例
1 | public static void main(String[] args) throws I/OExceptI/On { |
OS的I/O控制方式
程序直接控制方式
- 在程序直接控制方式中,CPU会直接处理 I/O 操作,数据传输等都由 CPU 负责。在这种方式下,CPU 必须不断地轮询 I/O 设备状态,这会导致大量的 CPU 时间用于轮询。
程序直接控制方式虽然简单且易于实现,但其缺点也显而易见,由于CPU 和I/0 设备只能串行工作,导致CPU 的利用率相当低。
中断驱动方式
中断驱动方式通过 CPU 的中断机制来实现。当 I/O 设备完成一个操作时,会发送一个中断信号给 CPU,中断处理程序会被调用来处理这个事件。
这种方式避免了程序不断地轮询设备状态,提高了 CPU 的利用率。
中断驱动方式比程序直接控制方式有效,但由于数据中的每个字在存储器与I/0 控制器之间的传输都必须经过CPU, 这就导致了中断驱动方式仍然会消耗较多的CPU 时间。
DMA
Direct Memory Access : 直接内存访问
在 DMA 方式中,数据传输无需 CPU 的直接干预,而是由专门的 DMA 控制器来完成,这意味着 CPU 可以继续执行其他任务。
DMA 控制器可以直接和设备和内存进行数据传输,减轻了 CPU 的负担,提高了系统的效率。
通道控制方式
通道控制方式是在主控制器和 I/O 设备之间引入了专门的通道控制器,它可以独立地执行 I/O 操作,从而将 I/O 操作和 CPU 的执行分开,提高了系统的 I/O 处理能力。这种方式多用于一些高速、高性能的设备上。
I/O通道方式是DMA方式的发展,它可以进一步 减少CPU的干预,即把对一个数据块的读(或写)为单位的干预,减少为对一组数据块的读(或写)及有关控制和管理为单位的干预。同时,又可以实现CPU、通道和I/0 设备三者的并行操作,从而更有效地提高整个系统的资源利用率。
两者的关系
结合上面的OS的四种I/O控制方式
方式 | 是否需要CPU干预 |
---|---|
程序直接控制 | 需要 |
中断驱动控制 | 需要 |
DMA | 较少 |
I/O通道 | 极少 |
Java 的 NIO 主要基于非阻塞 I/O 和 I/O 多路复用机制来实现,同于与操作系统的 I/O 控制方式存在一些联系:
- DMA 方式:Java 的 NIO 技术使用了直接内存访问(Direct Memory Access,DMA)的方式来进行数据传输。通过 ByteBuffer 类的 allocateDirect() 方法可以直接在内存中分配直接缓冲区,这种缓冲区的数据不受垃圾回收器的管理,可以直接通过 DMA 传输数据,这提供了在非阻塞 I/O 操作中更快速的数据传输。
- 通道控制方式:NIO 中的 Channel 可以看作是这些 I/O 控制方式的一种抽象。Channel 的引入使得可以在一个线程中管理多个 I/O 操作,并且 Selector 实现了通道控制(Channel control)方式,能够通过单一的线程处理多个通道(Channel)的 I/O 操作,实现了 I/O 多路复用。
到这里, 通过NIO来提高CPU利用率的原理也就显而易见了, 至于AI/O , I/O多路复用等技术 , 实际上也是依赖于DMA和通道控制方式。
对于最开始的问题, 我们也可以做出基本的解答: 执行I/O操作的是 DMA 等组件 , 并且不会占用进程/线程的时间片(实际上时间片是CPU占用时间 , 这里基本上没有使用CPU, 也不存在占用时间片的说法了)
时刻记住计算机的分层:
reference
- Netty网络编程(1):JavaNIO基础-1[Netty] : https://blog.dhx.icu/2023/05/netty/Netty网络编程(1)JavaNIO-1[Netty]/
- I/O控制方式(全网最细) : https://zhuanlan.zhihu.com/p/139505392