BIO、NIO、多路复用和AIO

小飞棍来喽~~~ 今天和大家一起来学习一下IO相关的知识内容。这里推荐一个公众号:小林coding。该大佬的图解系列相当好,关于IO的相关知识XiXi也是从这位大佬的文章中学习的。 写这篇文章的目的也是为了检验自己对IO相关知识的掌握,以及用Java代码向大家说明何为BIO?何为NIO?何为多路复用?

BIO、NIO和AIO

何为IO

IOI是输入(Input),O是输出(Output) 一言蔽之:从IO设备读取数据或写入数据到应用程序 从IO设备读取数据,需要先将数据拷贝到内核态,再将数据从内核态拷贝到用户态。写数据反之。

BIO、NIO、多路复用和AIO
image.png

BIO、NIO和AIO的区别就是在于等待这两次拷贝的不同

BIO

BIO:阻塞式IO,需要等待数据从IO设备->内核态内核态->用户态两次拷贝。 一旦调用read(),则线程将会阻塞

BIO、NIO、多路复用和AIO
image.png

上图来自:小林coding

NIO

NIO:非阻塞式IO,不用等待数据从IO设备->内核态的过程,但是要等待内核态->用户态的数据拷贝 调用read()方法后,方法不会阻塞,但是需要主动不停地询问是否完成IO设备->内核态拷贝

BIO、NIO、多路复用和AIO
image.png

上图来自:小林coding

AIO

AIO:异步非阻塞IO,这种IO既不用等待IO设备->内核态的过程,也不要等待内核态->用户态将数据的处理逻辑写成回调方法,数据在用户态准备好后,自动可以调用你的回调方法

BIO、NIO、多路复用和AIO
image.png

上图来自:小林coding

以NIO为基础的IO多路复用

回顾NIO的特点,不用等待数据从IO设备->内核态的拷贝,但是要不断地询问内核有没有完成拷贝好。这样总感觉到不够优雅,于是IO多路复用出现了。BIO、NIO、多路复用和AIO

图片来自:小林coding

用户程序调用select或者poll或者epoll方法后开始阻塞,等内核态数据准备好,方法返回 用户程序立即就可以对这些数据进行读操作

IO多路复用的意义是啥?

从用户使用角度对比下IO多路复用和BIO

IO多路复用:用户调用select()开始阻塞,阻塞结束后,可以直接读。 BIO:用户调用read()开始阻塞,阻塞结束后,可以直接读。

我的天,从我用户(程序员)角度来看IO多路复用和我用BIO的效果不是一样的吗? 有没有朋友和我有同样疑问的?下面我来说说我的理解

其实不然,IO多路复用主要应用于网络IO,让单线程下处理多客户端Socket成为可能。在单线程中,select()阻塞返回后,必定是一些数据准备好的客户端Socket,可以直接依次读数据无需担心会被哪个客户端Socket所阻塞。处理完一批后,继续select(),循环往复。 相反,如果在单线程通过BIO的方式来处理多个Socket,场景会是:客户端A的Socket迟迟没有新数据,客户端B的Socket数据早已准备好等待被读。但是线程中先调用了客户端A的Socket的read(),请问如果客户端A的Socket迟迟不发新数据,阁下又该如何应对?这就是标准的“占着茅坑不拉屎”。

总结:IO多路复用是在NIO的基础上产生的,主要用于网路IO,可以实现单线程下多客户端(多路)的处理。因此得名 IO多路复用。

总结IO分类

BIO、NIO、多路复用和AIO
image.png

参考:小林coding

代码实操

本小节想通过Java代码,向大家演示一下,Java中BIO、NIO、AIO以及IO多路复用的代码是什么样子的。毕竟“纸上得来终觉浅,绝知此事要躬行”。

代码中没关闭IO流的地方请大家见谅,工作当中记得关闭IO流。

磁盘IO(文件IO)

读取文件-BIO方式

BIO、NIO、多路复用和AIO通过FileInputStream#read()会发生阻塞,等待数据从IO设备->内核态内核态->用户态的两次数据拷贝才会返回

读取文件-NIO方式

BIO、NIO、多路复用和AIOJava为我们提供了FileChannel来完成NIO,从理论上来讲,read()方法在数据从IO设备->内核态未拷贝好时不会阻塞,但是内核态->用户态这部分会阻塞。所以红色框中代码会阻塞的(具体XiXi也没验证)

读取文件-AIO方式

BIO、NIO、多路复用和AIOJava为我们提供了AsynchronousFileChannel来完成AIO,从实验结果来看,调用完read()后立刻就往下执行了,程序自动会回调我自定义方法。

读取文件-IO多路复用

使用IO多路复用怎么读取文件?没有相关实现,IO多路复用用于网络IO。BIO、NIO、多路复用和AIO

网络IO

网络IO-BIO

BIO、NIO、多路复用和AIO
image.png
  • 在没有客户端连接服务器时,accept()方法会阻塞
  • 在客户端没有新的数据传输过来时,read()方法会阻塞

网路IO-NIO

BIO、NIO、多路复用和AIO
image.png
  • 通过配置ServerSocketChannel非阻塞来开启NIO,当没有客户端连接时accept()不会阻塞
  • 通过配置SocketChannel非阻塞来开启NIO,当客户端没有新消息发送过来时read()不会阻塞

网络IO-IO多路复用

BIO、NIO、多路复用和AIO
image.png
  • select()会发生阻塞
  • 每次select会返回多个事件:ACCEPT、READ
  • 处理完后一定要remove()

网络IO-AIO

select、poll和epoll模型

IO多路复用的实现方式

select和poll

BIO、NIO、多路复用和AIO摘自:小林coding

  • 已连接的Socket放在文件描述符集合,集合从用户态拷贝到内核态内核态标识出有事件的Socket并将标识后的集合再次拷贝回用户态用户态遍历集合,处理有事件的Socket。
  • select使用的Bitsmap作为集合(有大小限制),poll使用链表(突破大小限制)
BIO、NIO、多路复用和AIO
image.png

epoll

select或poll的方式都会产生2次整个集合的拷贝和遍历。 于是epoll出现了

BIO、NIO、多路复用和AIOepoll模型不再使用集合拷贝的方式,而是在内核态维护了一棵红黑树,待检测的Socket会加入红黑树,红黑树中有事件的Socket会加入链表,用户select(epoll_waitl)时只会返回有事件的Socket。

事件触发模式

边缘触发和水平触发BIO、NIO、多路复用和AIO摘自:小林coding

  • 边缘触发:Socket有事件后苏醒一次,用户即使没有read数据,也不会再苏醒,除非有新事件。
  • 水平触发:Socket中的事件只要没被处理(read),下一次还是会苏醒。

  • select和poll只支持水平触发
  • epoll默认水平触发,可以设置为边缘触发
  • 边缘触发高于水平触发效率,可以减少epoll_waitl的调用次数

参考文章

  • https://xiaolincoding.com/os/8_network_system/selete_poll_epoll.html
  • https://xiaolincoding.com/os/6_file_system/file_system.html#%E6%96%87%E4%BB%B6-i-o


原文始发于微信公众号(溪溪技术笔记):BIO、NIO、多路复用和AIO

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/255227.html

(0)
李, 若俞的头像李, 若俞

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!