【文章仅供非商业用途或交流学习使用】
很多朋友在遇到I/O类话题的时候,总是感觉比较吃力,对里面的很多概念似懂非懂,今天大体说一下我对I/O模型的理解。
一、相关概念
同步和异步
同步和异步用来描述用户线程和内核的交互方式,其中:
同步是指用户线程发起I/O请求后需要等待或者轮询内核I/O操作完成后才能继续执行;
异步是指用户线程发起I/O请求后仍继续执行,当内核I/O操作完成后会通知用户线程,或者调用用户线程注册的回调函数。
阻塞和非阻塞
阻塞和非阻塞用来描述用户线程调用内核的操作方式,其中:
阻塞是指I/O操作需要彻底完成后才返回到用户空间;
非阻塞操作是指I/O操作被调用后立即返回给用户一个状态值,无需等到I/O操作彻底完成。
二、Unix的IO五种模型
Unix下有五种可用的I/O模型:
阻塞I/O、非阻塞I/O、多路复用I/O、信号驱动I/O、异步I/O
1 阻塞I/O
最传统的一种I/O模型,在数据读写过程中会发生阻塞现象, Linux下默认I/O模型,即所有的socket都是Blocking。
典型的操作流程分为两个阶段:
阶段一:等待数据就绪。数据从网络中陆续到达,当所有数据到达时,它被复制到内核缓冲区中;
阶段二:数据从内核拷贝到进程。内核负责把缓冲区的数据复制到用户的程序所在进程。
Block I/O2 非阻塞I/O
Linux下,可以通过设置socket使其变为non-blocking。当对一个non-blocking的socket进行操作时,流程如下:
Non-Blocking I/O一般很少使用这种模型,而是在其他I/O模型中使用非阻塞I/O这一特性。
3 多路复用I/O
多路复用I/O会用到 select / epoll 函数,这两个函数也会使进程阻塞,流程如下:
多路复用 I/O从流程上来看,多路复用I/O和阻塞I/O本质上区别不大,而且还要额外监视socket,以及调用select函数的额外操作,效率应该更低一些。但是使用了select的最大优势是用户可以在一个线程内同时处理多个socket的I/O请求,用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一线程内同时处理多个I/O请求的目的,相当于多线程下的阻塞I/O模型。
多路复用I/O模型使用了 Reactor 设计模式实现了这一机制。
调用 select / epoll 该方法由一个用户态线程负责轮询多个socket,直接第一阶段的数据准备就绪,再通知实际的用户线程执行第二阶段的拷贝。通过一个专职的用户态线程执行非阻塞I/O轮询,模拟实现了第一阶段的异步化操作。
4 信号驱动I/O
在信号驱动I/O模型中,当用户线程发起一个I/O请求操作,会给对应的socket注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接受到信号之后,便在信号函数中调用I/O读写操作来进行实际的I/O请求操作。流程如下:
信号驱动 I/O5 异步I/O
调用aio_read函数通知内核启动某个操作,然后立即返回。当内核在整个操作完成后通知用户线程。流程如下:
异步 I/O异步I/O模型使用了Proactor设计模式实现了这一机制。
告知内核,当整个过程(阶段一、阶段二)全部完成时,通知应用程序来读数据。
三、总结、比较五种I/O模型
前四种模型都属于同步I/O模型,他们的区别在于第一阶段的操作方式。同步I/O方式引起请求进程阻塞,直到I/O操作完成。异步I/O操作不引起请求进程阻塞。
五种 I/O 模型比较
网友评论