NIO相关

176 阅读3分钟

理解还不深刻,简单梳理一下,还需要深入学习实践。

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多路复用

参考:

   Java NIO浅析

【深入浅出Linux】零拷贝技术sendfile

   操作系统中的IO,及高性能IO模型