写点什么

NIO 看破也说破(三)—— 不同的 IO 模型

发布于: 2020 年 05 月 11 日
NIO 看破也说破(三)—— 不同的IO模型

上两节我们提到了 select 和 poll 函数,查看 man 手册:



SELECT(2) Linux Programmer's Manual SELECT(2)
NAME select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - synchronous I/O multiplexing
复制代码


POLL(2)                  Linux Programmer's Manual                         POLL(2)
NAME poll, ppoll - wait for some event on a file descriptor
SYNOPSIS #include <poll.h>
复制代码

synchronous I/O multiplexing 中文解释是同步的多路复用,因此 select 是一个同步的 I/O 多路复用模式。Unix 共五种 I/O 模型:

  • 阻塞 I/O

  • 非阻塞 I/O

  • I/O 多路复用

  • 信号驱动

  • 异步 I/O


信号驱动和真正的异步 I/O 并不常用,我们重点说一下前三个。


阻塞和非阻塞的概念描述的是用户线程调用内核 IO 操作的方式:阻塞是指 IO 操作需要彻底完成后才返回到用户空间;而非阻塞是指 IO 操作被调用后立即返回给用户一个状态值,无需等到 IO 操作彻底完成

阻塞 I/O


ServerSocket server = new ServerSocket(8080);while (true) {  Socket socket = server.accept();  System.out.println("链接端口:" + socket.getPort());  InputStream inputStream = socket.getInputStream();  BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));  String str = null;  while ((str = reader.readLine()) != null) {    System.out.println("接受:" + str);    socket.getOutputStream().write("ok\n".getBytes());    socket.getOutputStream().flush();    if ("over".equals(str)) {      System.out.println("要关闭了");      socket.close();      break;    }  }  System.out.println("===========");}
复制代码

当有数据获取时,用户线程要释放 cpu,直到数据由内核处理完成,整个过程用户线程是阻塞的。

阻塞IO


用户线程调用内核 IO 操作,需要等 IO 彻底完成后才返回到用户空间,因此是阻塞 IO

非阻塞 I/O


ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.bind(new InetSocketAddress(8888));serverSocketChannel.configureBlocking(false);while (true) {  SocketChannel socketChannel = serverSocketChannel.accept();  if (socketChannel == null) {    System.out.println("没有链接 ");    continue;  }  System.out.printf("新链接,端口是 %s", ((InetSocketAddress) socketChannel.                                   getRemoteAddress()).getPort());  ByteBuffer ds = ByteBuffer.allocate(10);  socketChannel.read(ds);  System.out.println("接受数据");}
复制代码


非阻塞IO

IO 操作被调用后立即返回给用户一个状态值,无需等到 IO 操作彻底完成,因此是非阻塞的。(用户线程不因为 I/O 还未到达,一直傻傻的等待,而是需要不断去问内核是否有可用的数据。在准备就绪的情况下获取数据。


补充一个 gif(刷新查看):


I/O 多路复用


非阻塞 IO 中需要用户线程在每个 IO 通路上,各自不断轮询 IO 状态,来判断是否有可处理的数据。如果把一个连接的可读可写事件剥离出来,使用单独的线程来对其进行管理。多个 IO 通路,都复用这个管理器来管理 socket 状态,这个叫 I/O 多路复用。


多路复用


多路复用在内核中提供了 select,poll,epoll 三种方式:

select

原理示意


select

特点

只能处理有限(不同系统参数:1024/2048)个 socket

select 监控 socket 时不能准确告诉用户是哪个 socket 有可用数据,需要轮询判断


poll

原理示意

poll

特点

与 select 没区别

采用链表实现,取消了文件个数的限制

epoll

原理示意

epoll


epoll_wait 直接检查链表是不是空就知道是否有文件描述符准备好了

fd 上的事件发生时,与它对应的回调函数就会被调用把 fd 加入链表,其他处于“空闲的”状态的则不会被加入

epoll 从上面链表中获取有事件发生的 fd


epoll 准确的表述应该是 I/O 事件通知器:

EPOLL(7)               Linux Programmer's Manual                EPOLL(7)
NAME epoll - I/O event notification facility
SYNOPSIS #include <sys/epoll.h>
复制代码

特点

没有最大连接限制

可以直接告诉用户程序哪一个,哪个连接有数据了


epoll 详细的介绍可以参考我的专辑 http://mp.weixin.qq.com/mp/homepage?__biz=MzI3MDYwOTYwOA==&hid=2&sn=b7d5d6aa0b9f18c382a97f146c02b540&scene=18#wechat_redirect 中的第二篇文章《读懂才会用:Redis 源码系列》

同步异步的概念


同步和异步的概念描述的是用户线程与内核的交互方式:

同步是指用户线程发起 IO 请求后需要等待或者轮询内核 IO 操作完成后才能继续执行;异步是指用户线程发起 IO 请求 后仍继续执行,当内核 IO 操作完成后会通知用户线程,或者调用用户线程注册的回调函数。


因此 阻塞 I/O,非阻塞 I/O,I/O 多路复用,都属于同步调用。只有实现了特殊 API 的 AIO 才是异步调用,之后单开一篇讲解。

AIO(7)                         Linux Programmer's Manual               AIO(7)
NAME aio - POSIX asynchronous I/O overview
DESCRIPTION The POSIX asynchronous I/O (AIO) interface allows applications to initiate one or more I/O operations that are performed asyn‐ chronously (i.e., in the background). The application can elect to be notified of completion of the I/O operation in a variety of ways: by delivery of a signal, by instantiation of a thread, or no notification at all.
复制代码


系列

NIO 看破也说破(一)—— Linux/IO 基础

NIO 看破也说破(二)—— Java 中的两种 BIO

NIO 看破也说破(三)—— 不同的 IO 模型

NIO 看破也说破(四)—— Java 的 NIO

NIO 看破也说破(五): 搞,今天就搞,搞懂Buffer


关注我


如果您在微信阅读,请您点击链接 关注我 ,如果您在 PC 上阅读请扫码关注我,欢迎与我交流随时指出错误


发布于: 2020 年 05 月 11 日阅读数: 2408
用户头像

欢迎关注公众号“小眼睛聊技术” 2018.11.12 加入

互联网老兵,关注产品、技术、管理

评论 (9 条评论)

发布
用户头像
喜欢你的文风。图的质量很高,也不啰嗦
2020 年 05 月 16 日 21:29
回复
谢谢,欢迎关注。也欢迎关注我的公众号。
2020 年 05 月 16 日 22:38
回复
用户头像
看不懂了,非阻塞 IO 的图有点不懂,作者能着重细节讲下吗
2020 年 05 月 14 日 20:32
回复
我补充了一个gif,你刷新看看,是否能看懂
2020 年 05 月 15 日 01:57
回复
看懂了。但是有个问题,其实非阻塞IO和IO多路复用在全局也是阻塞的,因为他们有两个步骤:
1. 是否有数据
2. 读数据
在这两种 IO 模型中,第一步是非阻塞的,但是第二部读数据是阻塞的
2020 年 06 月 17 日 16:40
回复
用户头像
最近高产啊,首页继续推荐啦。
2020 年 05 月 11 日 13:43
回复
梳理下自己🤦‍♂️
2020 年 05 月 11 日 20:52
回复
这个系列继续更新了,您看是否符合推荐的标准 Thanks♪(・ω・)ノ
2020 年 05 月 19 日 15:38
回复
没有更多了
NIO 看破也说破(三)—— 不同的IO模型