IO

120 阅读4分钟
网络io

网络传输?

  • 应用层【计算机用户,以及各种应用程序和网络之间的接口】
  • 表示层【其主要功能是“处理用户信息的表示问题,如编码、数据格式转换和加密解密”等】
  • 会话层【用户应用程序和网络之间的接口,主要任务是:向两个实体的表示层提供建立和使用连接的方法】
  • 传输层【TCP/UDP】
  • 网络层【ip】
  • 数据链路层【port】
  • 物理层【物理层的作用是实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异】

四元组?

  • server ip&监听port
  • client ip&随机端口

三次握手?

  • client 发送sync到 server 【你收到我的包裹了么】
  • server 发送sync + ack 到 client 【我收到了你的包裹】
  • client 发送ack到 server 【我知道你收到了我的包裹】
  • 以上握手消息交互 异常重试,三次握手保证双方都能正常 发送和接收 数据

四次挥手?

  • client 发送fin 到server 【我准备要断开了】
  • server 发送ack 【我知道你准备好要断开了,等会我准备好了再告诉你】
  • server 发送ack 【我也准备好要断开了】
  • client 发送ack 【咱俩都准备好了,断开吧清理资源】
  • 四次挥手保证双方都完成了数据的 发送和接收

BIO 同步阻塞

  • socket(ip+port)
  • serverSocket(ip+port)
  • 对于server端来说,客户端的每一个socket链接需要一个线程(单独线程、线程池中的一个线程)去处理读写

NIO 异步阻塞

  • channel 通道接口
  • socketChannel(socket(ip+port)) implements channel
  • serverSocketChannel(serverSocket(ip+port)) implements channle
  • buffer
  • selector
  • 对于server端来说,客户端的多个socketChannel链接注册到selector,使用一个线程来通过select()获取那个socketChannel就绪并处理读写

AIO 异步非阻塞

IO模型

  • 阻塞IO: b4bb37757961f8bdef9ecde1f24311be.png
  • 非阻塞IO:

ba1a618ec889620760a38b2d18370589.png

  • IO多路复用:

7770d96ca5dce07922ce66e156772bd8.png

  • 信号量IO: 1e34d8b3f260d323b994b632028e440a.png

信号驱动 I/O 和多路复用型I/O的主要区别在于:信号驱动型 I/O 的底层实现机制是事件通知,而多路复型用 I/O 的底层实现机制是遍历+回调

  • 异步I/O:

不管是I/O复用还是信号驱动,要读取一个数据总是要发起两阶段的请求,第一次发送select请求,询问数据状态是否准备好,第二次发送recevform请求读取数据。

有没有有一种方式,我只要发送一个请求我告诉内核我要读取数据,然后我就什么都不管了,然后内核去帮我去完成剩下的所有事情?

于是有人设计了一种方案,应用只需要向内核发送一个read 请求,告诉内核它要读取数据后即刻返回;内核收到请求后会建立一个信号联系,当数据准备就绪,内核会主动把数据从内核复制到用户空间,等所有操作都完成之后,内核会发起一个通知告诉应用,我们称这种模式为异步IO模型。

5184b2ba04e1e12bdf396c3a020a03e6.png

同步异步?阻塞非阻塞?

socket的read 需要经历两个阶段,1内核数据缓冲区数据到达 2数据从缓冲区copy到用户态

  • 同步异步是指 1、2两个阶段是否有等待,从发起IO到得到结果没有任何阻塞。
  • 阻塞非阻塞是指 1阶段是否阻塞

文件io

BIO

  • fileInputStream
  • fileOutputStream NIO
  • Files.read
  • Files.write
  • FileChannel

NIO是面向buffer数据块读写,BIO面向流的读写。

Buffer

属性

  • capactiy 【最大容量、创建时指定且不可变】
  • limit【缓冲区当前终点,不能对缓冲区超出limit的位置进行读写】
  • position 【下一个要被读/写的索引位置,每次读写移动位置,为下次读写做准备】
  • mark 【标记暂存postion,调用reset()可以让position 恢复到标记位置】

方法

  • allocate(int capacity) 从堆中创建一个容量为capacity的数组
  • allocatDirect(int capacity) 从物理内存中创建缓冲区,开辟直接内存消耗较大,因此在缓冲区长期存在并频繁使用情况下使用
  • wrap(byte[] array)
  • wrap(byte[] array,int offset,int length)
  • limit() 获取limit位置
  • reset() position 设置为mark的值
  • clear() position=0、limit=capacity、mark=-1
  • flip()
  • rewind() position=0、mark=-1
  • remaining() limit - position
  • hasRemaining() position < limit?
  • compact()
  • get() 读取position位置数据,position++
  • get(index) 读取 index位置数据
  • get(byte,offset,length) 从position读出length个数据从byte的offset位置开始写入byte中
  • put(byte) 向position位置写入数据,position++
  • put(index,byte) 向position位置写入数据
  • put(bytebuffer) 从position 写如bytebuffer
  • put(bytebuffer,offset,length) 从offset写入 bytebuffer数据的length长度
数据copy与内核态用户态切换
  • BIO

截屏2024-04-02 16.50.36.png

  • mmap + write

截屏2024-04-02 16.50.51.png

  • sendfile

截屏2024-04-02 16.51.09.png

  • sendfile + DMA gather

截屏2024-04-02 16.51.18.png