理解还不深刻,简单梳理一下,还需要深入学习实践。
NIO又称为New-IO.主要涉及到以下几个知识点
- 同步不阻塞
- 事件模型
- IO多路复用
- DirectByteBuffer
传统的BIO模型
BIO操作一般分为两个阶段,以从磁盘read为例:
-
等待可读:将文件读到内核态,即为A过程
-
等待可读是从应用程序的角度来看的,这个阶段,应用程序只能阻塞,直到操作系统将数据从硬盘读到内核缓冲区方可。
-
读:从内核态copy到用户态,即为B过程
在等待可读的阶段,CPU是阻塞空等的;第二个阶段,CPU也是阻塞的去读,但是纯内存copy,速度非常快。
同步不阻塞
而对于NIO,第一阶段,CPU不会在这阻塞。如果可读,执行第二步读的操作;如果不可读,直接返回0.第一步,我们称为不阻塞,第二步我们称为同步。
NIO一般需要结合事件模型来使用。比如linux的select,poll,epoll等,或者java nio下边的Selector。select或者poll或者epoll本身也是阻塞的,直到有新的事件到来,比如可读事件,或者可写事件。此时CPU即可去同步的执行读写操作。
伪代码如下:
while(true){
int i = selector.select(); //阻塞的等待事件的到来
if(i == Event.READ){
//do read...执行读操作
}
}
三种事件函数的区别
- select 有数量限制,max为1024;且只能知道有读写事件到来,但不知道该读写哪个文件?
- poll 同上,只能知道有读写事件到来,但不知道读写谁,只能通过遍历的方式去获取要读写的文件。没有数量限制。
- epoll 知道要读写谁,文件数大时效率高
IO多路复用
IO多路复用指的是在一个线程内处理多个IO请求。
优点:
- 节省了线程创建,销毁的成本
- 节省了线程切换的成本(上线文的保存和恢复)
- 单线程效率更高
DirectByteBuffer
JVM在处理IO时需要将堆内数据copy到堆外(JVM进程外,但仍属于用户态),然后才能向内核态copy。通过使用DirectByteBuffer,可以省去由堆内到堆外的copy操作,而直接使用堆外空间。
Kafka的零拷贝
linux的sendfile函数的意思是:将一个fd从内核态直接copy到另一个fd的内核态,而不经过用户态。这样可以省去一次copy操作和两次操作系统的切换。甚至通过带有收集特性的DMA直接访问,可以省略又内核缓冲区到socket buffer的copy操作。
kafka的零拷贝正式通过该原理,通过将磁盘文件写入到内核缓冲区,然后直接发送到网卡,减少了copy次数,提高了吞吐量。
普通:磁盘 -> 内核缓冲区(内核态) -> 用户态 -> socker buffer(内核态) -> 网卡
零拷贝(带DMA收集拷贝功能):磁盘 -> 内核缓冲区(内核态) -> 网卡
Redis IO多路复用
参考: