IO多路复用

先说明一个问题:在Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO。网络编程其实就是多了一个连接的过程,常用在Netty一些框架。本文主要讲述标准输入输出NIO

一.BIO,NIO,AIO的区别

先说一些IO的模式

  • 我们发现,IO不是我们想象中那么简单。要想进行IO操作,需要发一个请求给CPU,CPU得到通知后,此时CPU就需要调用操作系统内核程序(磁盘控制器)。这就是用户态->内核态。
  • 磁盘控制器接到通知,使用DMA技术将数据放到PageCache中,再由CPU把数据从内核程序传回用户程序(buffer)。这就是内核态->用户态。
  • 关于上图的操作系统知识,如果不是清楚,请先移步操作系统,分3章讲清楚操作系统核心内容。

根据上面的模型,其实我们可以把一次IO粗略的分为2个步骤:「内核数据准备好」和「数据从内核态拷贝到用户态」

  • 同步IO:
  • 阻塞I/O 两个步骤都要等待。(BIO)
    • 适用于连接数目比较小,且固定的架构

IO多路复用

*
– 非阻塞I/O 第一个步骤无序等待,但是会一直询问内核是否准备好。(BIO的多线程版)

IO多路复用

*
– 多路复用I/O 在非阻塞的前提下优化,不会一直询问。但是多一个select期。(NIO)
+ 适用于连接数目多且连接比较短的操作(轻操作)。比如rpc框架,聊天服务器等。

IO多路复用
  • 异步I/O: 两个步骤都无需等待,询问完交给内核自动完成。(AIO)
  • 适用于连接数目多且连接比较长的操作(重操作),比如相框服务器,充分调用OS参与并发操作。

IO多路复用

多路复用的系统方法说明:一直在升级

  • select:用一个线程,接收到IO的请求,就将这些进行IO的文件描述符复制一份到内核,然后内存进行遍历,看哪个文件已经准备好数据,将准备好的文件描述符的个数返回给用户态,用户态再进行遍历,找到是哪个文件描述符的IO。有3个缺点如下
  • select 调用需要传入 fd 数组,需要拷贝一份到内核,高并发场景下这样的拷贝消耗的资源是惊人的。(可优化为不复制)
  • select 在内核层仍然是通过遍历的方式检查文件描述符的就绪状态,是个同步过程,只不过无系统调用切换上下文的开销。(内核层可优化为异步事件通知)
  • select 仅仅返回可读文件描述符的个数,具体哪个可读还是要用户自己遍历。(可优化为只返回给用户就绪的文件描述符,无需用户做无效的遍历)
  • poll:它和 select 的主要区别就是,去掉了 select 只能监听 1024 个文件描述符的限制。
  • epoll:epoll 是最终的大 boss,它解决了 select 和 poll 的一些问题。
  • 内核中保存一份文件描述符集合,无需用户每次都重新传入,只需告诉内核修改的部分即可。
  • 内核不再通过轮询的方式找到就绪的文件描述符,而是通过异步 IO 事件唤醒。
  • 内核仅会将有 IO 事件的文件描述符返回给用户,用户也无需遍历整个文件描述符集合。

这里附上一个大佬写的关于IO多路复用的动画:IO多路复用

二.NIO的组成

Channel:

  • 数据传输的双向通道,既可以用来读数据又可以用来写数据。主要实现类如下:
  • FileChannel:文件IO
  • DatagramChannel:UDP的IO
  • SocketChannel 和 ServerSocketChannel:TCP的IO

Buffer:

  • 用于和 NIO 通道进行交互。
  • Buffer 是一个顶层父类,它是一个抽象类,常用的 Buffer 的子类有:ByteBuffer、 IntBuffer、 CharBuffer、 LongBuffer、 DoubleBuffer、 FloatBuffer、ShortBuffer等
  • 下图为网络IO的一个简单图

IO多路复用

Selector:

  • 选择器 ,也可以翻译为多路复用器 。它是 Java NIO 核心组件中的一个,用于检查一个或多个 NIO Channel(通道)的状态是否处于可读、可写。如此可以实现单线程管理多个 channels,也就是可以管理多个网络链接,就可以避免线程上下文切换。一个Selector对应一个线程,

IO多路复用

三.Buffer中对应的Position, Mark, Capacity,Limit都啥?

IO多路复用
* limit:对buffer缓冲区使用的一个限制,从这个index开始就不能读取数据了。
* position:代表着数组中可以开始读写的index, 不能大于limit。
* mark:是类似路标的东西,在某个position的时候,设置一下mark,此时就可以设置一个标记
– 后续调用reset()方法可以把position复位到当时设置的那个mark上。去把position或limit调整为小于mark的值时,就丢弃这个mark,如果使用的是Direct模式创建的Buffer的话,就会减少中间缓冲直接使用DirectorBuffer来进行数据的存储。

四.Java中的IO流(BIO):

分类

  • 按照操作数据单位不同:字节流(8bit),字符流(16bit)
  • 按照数据流的流向不同:输入流,输出流
  • 按照流的角色分为:
  • 节点流:直接从数据源或目的地读写数据
  • 处理流:不直接连接到数据源或目的地,而是连接在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提 供更为强大的读写功能。

IO多路复用

实现类:

IO多路复用

寄语:我努力奔跑是为了追上那个曾经被寄予厚望的自己

Original: https://www.cnblogs.com/monkey-xuan/p/15877586.html
Author: 小猴子_X
Title: IO多路复用

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/579544/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球