美文网首页
BIO&NIO&AIO

BIO&NIO&AIO

作者: 哓晓的故事 | 来源:发表于2019-01-28 13:17 被阅读0次

BIO/NIO/AIO原理

IO模型

一个输入操作通常包括两个阶段:

  1. 等待数据准备好
  2. 从内核向进程复制数据

对于一个套接字上的输入操作
第一步通常涉及等待数据从网络中到达。当所等待数据到达时,它被复制到内核中的某个缓冲区
第二步就是把数据从内核缓冲区复制到应用进程缓冲

什么是阻塞

网络交互,系统可预见的没有读到数据,是阻塞
磁盘交互,系统不可预见的抖动,导致IO阻塞一会,不是阻塞

BIO和NIO

BIO每个连接都需要占用一个线程没有读到IO流的时候都处于阻塞

IO阻塞会导致线程无法释放,即便是不需要IO时也会阻塞
会导致服务端线程增大,线程池也无法解决

NIO每个连接都需要注册一个事件监听(select/poll/epoll)任何时候立即返回IO流的当前数据

线程不会因为IO阻塞而无法释放,可以去做额外的事情
线程只有在需要IO的时候才会被阻塞住

IO多路复用

操作系统提供一种机制(poll、select、epoll),允许注册IO请求,当有任何一个请求被触发,会有反馈
poll、select每次都要遍历所有的注册,并且轮询
epoll只会返回对应被触发的注册时间(并且提供了边缘触发,允许有条件的获取数据),并轮询

NIO实现

NIO = 非阻塞(立即放回)IO + IO多路复用(通知IO就绪了) + 缓存(ByteBuffer)
NIO特有的是 selector(实现了IO多路复用策略) + buffer 只是更加优化的方式

  1. 单Reactor单线程模型
  2. 单Reactor多线程模型:有一个Thread-Acceptor线程用于监听接收客户端的TCP连接事件/IO读写事件(N条链路)。有一个ThreadPool-负责网络IO的读写操作
  3. 主从Reactor多线程模型:有一个Thread-Acceptor线程用于监听接收客户端的TCP连接事件,有一个ThreadPool专门负责IO读写事件。有一个ThreadPool-负责网络IO的读写操作
  4. Netty线程模型(对主从Reactor多线程模型做出细微改造)
    MainReactor负责客户端的连接请求,并将请求转交给 SubReactor
    SubReactor负责相应通道的IO读写请求
    非 IO 请求(具体逻辑处理)的任务则会直接写入队列,等待 worker threads 进行处理
netty.png

BIO & NIO 优势

BIO少量的连接使用非常高的带宽,一次发送大量的数据
NIO:(交易信息上传)如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据

磁盘IO

磁盘的最小操作单位簇(sector),对于Linux来说,虚拟文件系统(VFS)抽象了磁盘设备,统一称为“设备”(block device)。数据是按照一块块来组织的。操作系统可以随机的定位到某个“块”,读写某个“块”
->的转换,是由设备驱动来完成的
在VFS上层的应用是感受不到“簇”的,他们只能感受到“块”
即使你只想读取1个Byte,磁盘也至少要读取1个块;要写入1个Byte,磁盘也至少要写入一个块

虚拟文件系统层VFS之上,是内存。这一层被称为Page Cache(在内核态)
在内存之上的是应用程序,应用程序总是需要在用户态分配一段内存空间作为buffer,然后将Page Cache中的数据copy出来进行处理。处理完成后,将数据写回(copy回)到Page Cache

image.png
PageCache和BlockDevice.png

Page Cache对于磁盘IO性能表现极度重要。比如,当通过write API写入数据到磁盘时,数据先会被写入到Page Cache。此时,这个Page被称为“dirty page”。dirty page会最终被写入到磁盘上,这个过程为称之为“写回”(writeback)。写回往往不会立刻发生。写回可能由于调用者直接使用类似于fsync这样的API,也有可能因为操作系统根据某种策略和算法决定自动写回。写回发生之前,如果机器挂了,就有可能丢失数据。这也是为什么有持久性要求的程序都需要用fsync来保证数据落地的原因。

上图会出现两次CPU copy,分别是从内核态->用户态用户态->内核态

AIO(异步IO)

AIO在操作系统层面, 只支持磁盘IO, 不支持网络IO, 并且上层(nodejs,Java NIO)都会选择用线程池+BIO来模拟文件AIO
网络IO,需要用epoll这样的IO多路复用技术结合

为了避免2次CPU copy,可以采用mmapsendfile技术
同步 I/O:将数据从内核缓冲区复制到应用进程缓冲区的阶段,应用进程会阻塞通知复制是阻塞和非阻塞IO来简化
异步 I/O:不会阻塞

mmap

mmap.png
采用Page Cache中的内核空间内存地址直接映射用户空间

sendfile

sendfile可以直接将Page Cache中某个fd的一部分数据传递给另外一个fd, 目标的fd可以是socket,而不用经过到应用层的两次copy无法做任何修改,称这种实现为“Zero Copy”

sendfile.png

Direct IO

Linux允许应用程序在执行磁盘IO时绕过缓冲区高速缓存,从用户空间直接将数据传递到文件或磁盘设备,称为直接IO(direct IO)或者裸IO(raw IO
DirectBuffer 需要 AlignedBlock对齐块

相比“Buffered IO”,Direct IO必然会带来性能上的降低。所以Direct IO有特定的应用场景。比如,在数据库的实现中,为了保证数据持久,写入新数据到WAL(Write Ahead Log)必须直接写入到磁盘,不能等待。这里用Direct IO来实现WAL就非常理想。
使用Direct IO的另外一种场景是,应用程序对磁盘数据缓存有特别定制的需要,而常规的Page Cache的各种策略并不能满足这种需要。于是开发人员可以自己设计和实现一套“Cache”,配合Direct IO。毕竟最熟悉数据访问场景的,是应用程序自己的需求。

  • 用于传递数据的缓冲区(buffer),其内存边界必须对齐(aligned block)块大小(block size)的整数倍
  • 数据传输的开始点,即文件和设备的偏移量offset,必须是块大小(block size)的整数倍
  • 待传递数据的长度必须是块大小(block size)的整数倍

以上约束的是内存写入磁盘时, 内存的规定. 不遵守上述任一限制均将导致EINVAL错误

相关文章

  • BIO&NIO&AIO

    BIO/NIO/AIO原理 IO模型 一个输入操作通常包括两个阶段: 等待数据准备好 从内核向进程复制数据 对于一...

  • Java-网络编程-BIO&NIO&AIO

    一、IO模型 IO模型就是说用什么样的通道进行数据的发送和接收,Java共支持3种网络编程IO模式:BIO,NIO...

  • java的I_O模型-BIO&NIO&AIO

    [toc] Posted by 微博@Yangsc_o 原创文章,版权声明:自由转载-非商用-非衍生-保持署名 |...

网友评论

      本文标题:BIO&NIO&AIO

      本文链接:https://www.haomeiwen.com/subject/ldlajqtx.html