0%

Linux - BIO、NIO、AIO

在计算机通信中,I/O在数据交换中起到了至关重要的作用。    

最初,只有BIO这种同步阻塞式的I/O编程通信模型。
随着计算机普及及流量的上涨,产生了NIO这种同步非阻塞式的I/O编程通信模型。
再后来,NIO出现了瓶颈,便产生了AIO这种异步非阻塞式的I/O编程通信模型。

对于I/O类型,分为:文件I/O、网络I/O。

文件I/O:基本本地磁盘,在内核空间与用户进程空间之间交换数据。
网络I/O:基本网络Socket通信,在不同(主机)进程之间交换数据。

对于I/O方式,分为:同步、异步。

同步方式:后续的操作需要等待前面操作完成才可以继续执行。
异步方式:后续的操作不需要等待前面操作完成,可以直接返回,通过event、callback调用。

对于I/O状态,分为:阻塞、非阻塞。

阻塞状态:当前执行的线程将处于阻塞状态,无法继续执行其他的任务。
非阻塞状态:当前执行的线程不会处于阻塞状态,可以继续其它任务,I/O操作由后台去处理。

I/O通信模型围绕着上述方式和状态分为:BIO、NIO、AIO。

首先,是BIO,同步阻塞I/O,最为原始,设计最简单,适用于并发线程少于1000的情况。
在服务端,绑定IP、监听端口,启动一个Acceptor线程,阻塞等待accept。
当客户端进发起连接请求时,创建一个线程进行Socket连接,等待与客户端之间进行读写I/O流操作。
客户端使用连接完毕之后,断开连接,销毁线程。
当然,由于创建线程成本过高,可以采用线程池进行优化线程成本。
但是,如果线程内部I/O阻塞,线程池资源也会带来瓶颈,也因此无法同时承受太多并发连接。

linux-bio

os-bio

接着,是NIO,同步非阻塞I/O,底层基于Reactor模型来实现。
BIO不同的是,在NIO中,客户端与服务端的Channel建立连接后,由Selector线程不断的去轮询,获取当前就绪的Channel。
而不是来一个请求直接启动一个线程。Selector是一个多路复用器,Channel被注册到Selector上。
客户端只与Channel进行交互,所有的读写不是基于流,而是基于Buffer,有I/O操作时才会创建线程。
通过多路复用器的轮询,而不是Acceptor的阻塞accept,实现了非阻塞I/O。
对于多路复用器轮询出可用Channel的操作,根据操作系统实现又包含有:select、poll、epoll、kqueue。

linux-nio

os-nio

AIO作为NIO的升级版,是异步非阻塞I/O,底层基于Proactor模型来实现,适用高并发场景。
整体上基于事件和回调机制,文件通道、套接字通道都是异步的,读写返回的是Future
不需要再去注册相关感兴趣的key,只需要等待事件和I/O操作结果返回即可。

linux-aio

总的来说:
BIO,基于同步阻塞,适用连接较少且连接使用时间均匀的场景,耗费服务器资源较多。
NIO,基于同步非阻塞,适用连接较多且连接使用时间短的场景,充分利用服务器资源。
AIO,基于异步非阻塞,适用连接较多且连接使用时间长的场景,充分利用操作系统来完成并发操作。