小飞棍来喽~~~ 今天和大家一起来学习一下IO相关的知识内容。这里推荐一个公众号:小林coding
。该大佬的图解系列相当好,关于IO的相关知识XiXi也是从这位大佬的文章中学习的。 写这篇文章的目的也是为了检验自己对IO相关知识的掌握,以及用Java代码向大家说明何为BIO?何为NIO?何为多路复用?
BIO、NIO和AIO
何为IO
“
IO:I是输入(Input),O是输出(Output) 一言蔽之:从IO设备读取数据或写入数据到应用程序 从IO设备读取数据,需要先将数据拷贝到内核态,再将数据从内核态拷贝到用户态。写数据反之。
“
BIO、NIO和AIO的区别就是在于等待这两次拷贝的不同
BIO
“
BIO:阻塞式IO,需要等待数据从IO设备->内核态和内核态->用户态两次拷贝。 一旦调用
read()
,则线程将会阻塞
“
上图来自:小林coding
NIO
“
NIO:非阻塞式IO,不用等待数据从IO设备->内核态的过程,但是要等待内核态->用户态的数据拷贝 调用
read()
方法后,方法不会阻塞,但是需要主动不停地询问是否完成IO设备->内核态拷贝
“
上图来自:小林coding
AIO
“
AIO:异步非阻塞IO,这种IO既不用等待IO设备->内核态的过程,也不要等待内核态->用户态将数据的处理逻辑写成回调方法,数据在用户态准备好后,自动可以调用你的回调方法
“
上图来自:小林coding
以NIO为基础的IO多路复用
回顾NIO的特点,不用等待数据从IO设备->内核态的拷贝,但是要不断地询问内核有没有完成拷贝好。这样总感觉到不够优雅,于是IO多路复用出现了。
“
图片来自:小林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分类
“
参考:小林coding
代码实操
本小节想通过Java代码,向大家演示一下,Java中BIO、NIO、AIO以及IO多路复用的代码是什么样子的。毕竟“纸上得来终觉浅,绝知此事要躬行”。
“
代码中没关闭IO流的地方请大家见谅,工作当中记得关闭IO流。
磁盘IO(文件IO)
“
读取文件-BIO方式
通过FileInputStream#read()
会发生阻塞,等待数据从IO设备->内核态和内核态->用户态的两次数据拷贝才会返回
“
读取文件-NIO方式
Java为我们提供了FileChannel
来完成NIO,从理论上来讲,read()方法在数据从IO设备->内核态未拷贝好时不会阻塞,但是内核态->用户态这部分会阻塞。所以红色框中代码会阻塞的(具体XiXi也没验证)
“
读取文件-AIO方式
Java为我们提供了AsynchronousFileChannel
来完成AIO,从实验结果来看,调用完read()
后立刻就往下执行了,程序自动会回调我自定义方法。
“
读取文件-IO多路复用
使用IO多路复用怎么读取文件?没有相关实现,IO多路复用用于网络IO。
网络IO
“
网络IO-BIO
-
在没有客户端连接服务器时, accept()
方法会阻塞 -
在客户端没有新的数据传输过来时, read()
方法会阻塞
“
网路IO-NIO
-
通过配置 ServerSocketChannel
非阻塞来开启NIO,当没有客户端连接时accept()不会阻塞 -
通过配置 SocketChannel
非阻塞来开启NIO,当客户端没有新消息发送过来时read()
不会阻塞
“
网络IO-IO多路复用
-
select()会发生阻塞 -
每次select会返回多个事件:ACCEPT、READ -
处理完后一定要remove()
“
网络IO-AIO
略
select、poll和epoll模型
“
IO多路复用的实现方式
select和poll
“
摘自:小林coding
-
已连接的Socket放在文件描述符集合,集合从用户态拷贝到内核态,内核态标识出有事件的Socket并将标识后的集合再次拷贝回用户态,用户态遍历集合,处理有事件的Socket。 -
select使用的 Bitsmap
作为集合(有大小限制),poll使用链表(突破大小限制)
epoll
“
select或poll的方式都会产生2次整个集合的拷贝和遍历。 于是epoll出现了
epoll模型不再使用集合拷贝的方式,而是在内核态维护了一棵红黑树,待检测的Socket会加入红黑树,红黑树中有事件的Socket会加入链表,用户select(epoll_waitl)时只会返回有事件的Socket。
事件触发模式
“
边缘触发和水平触发摘自:小林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