I/O 请求可以分为两个阶段,分别为调用阶段和执行阶段。
I/O 调用阶段 : 用户进程向内核发起系统调用。I/O 执行阶段:内核等待 I/O 请求处理完成返回。该阶段分为两个过程:首先等待数据就绪,并写入内核缓冲区;随后将内核缓冲区数据拷贝至用户态缓冲区。为了方便大家理解,可以看一下这张图:
1 同步阻塞 I/O(BIO)应用进程向内核发起 I/O 请求,发起调用的线程一直等待内核返回结果。
一次完整的 I/O 请求称为BIO(Blocking IO,阻塞 I/O),BIO 在实现异步操作时,一个请求对应一个线程,在此期间,线程不能执行任何其他任务。
假如想提高并发度,只能采用多线程,但是,线程的资源是有限且宝贵的,创建过多的线程会增加线程切换的开销。
适用场景:
对 I/O 要求极低的简单应用。系统的简单性和易用性比性能更重要。并发连接数较少的情况。2 同步非阻塞 I/O(NIO)在非阻塞 I/O 模型中,即使数据尚未可用,I/O 操作也会立即返回。应用程序可以在等待 I/O 操作完成的同时执行其他任务。
应用进程向内核发起 I/O 请求后不再会同步等待结果,而是会立即返回,通过轮询的方式获取请求结果。
NIO 相比 BIO 虽然大幅提升了性能,但是轮询过程中大量的系统调用导致上下文切换开销很大。所以,单独使用非阻塞 I/O 时效率并不高,而且随着并发量的提升,非阻塞 I/O 会存在严重的性能浪费。
3 I/O 多路复用多路复用实现了一个线程处理多个 I/O 句柄的操作。
多路指的是多个数据通道,复用指的是使用一个或多个固定线程来处理每一个 Socket。
select、poll、epoll 都是 I/O 多路复用的具体实现,线程一次 select 调用可以获取内核态中多个数据通道的数据状态。
多路复用解决了同步阻塞 I/O 和同步非阻塞 I/O 的问题,是一种非常高效的 I/O 模型。
4 信号驱动 I/O信号驱动 I/O 并不常用,它是一种半异步的 I/O 模型。
在使用信号驱动 I/O 时,当数据准备就绪后,内核通过发送一个 SIGIO 信号通知应用进程,应用进程就可以开始读取数据了。
5 异步 I/O异步 I/O 最重要的一点是从内核缓冲区拷贝数据到用户态缓冲区的过程也是由系统异步完成,应用进程只需要在指定的数组中引用数据即可。