谈I/O模型
一个IO操作涉及两个系统对象:
- 调用这个IO的用户Process/Thread
- 系统内核 - System Kernel
一个具体的Read操作包括两个阶段:
- 内核等待数据准备就绪:Waiting for the data to be ready
- 从内核向用户进程/线程拷贝数据:Copying the data from the Kernel to the Process/Thread
只有在同步的情况下才会有“阻塞”和“非阻塞”之说,异步情况,必须是非阻塞的!
“同步 - 异步”指访问数据的一种协作方式,同步需要主动读写数据,在读写数据的过程中还是会阻塞;异步只需要I/O操作完成的通知,并不主动读写数据,由内核完成数据的读写。“阻塞 - 非阻塞”指进程/线程要访问的数据是否就绪,进程/线程是否需要等待。同步和异步着重点在于多个任务的执行过程中,一个任务的执行是否会导致整个流程的暂时等待,阻塞和非阻塞着重点在于发出一个请求操作时,如果进行操作的条件不满足是否会返会一个标志信息告知条件不满足。
同步/异步仅关注消息通知的方式,并不关心消息如何处理,消息处理过程中应用程序(线程)的状态用阻塞/非阻塞来描述,是函数/方法的实现方式。
参考:socket阻塞与非阻塞、同步与异步概念详解 - 1;socket阻塞与非阻塞、同步与异步概念详解 - 2;
同步 vs 异步
消息的通知机制,针对Client端(和Server端的交互),进程/线程触发IO操作后:
- 同步:进程/线程等待IO操作完成(阻塞)或轮询查看IO操作是否完成(非阻塞);
- 异步:直接返回,进程/线程做自己的事情,IO操作交给内核处理、完成后内核通知进程/线程;
其中,异步中处理的执行部通过三种途径返回结果给调用者:
- 状态:需要调用者定时检查,效率低;
- 通知:效率高;
- 回调函数:类似通知;
同步 Synchronous
A Synchronous-I/O operation causes the requesting process to be blocked until that I/O operation completes.
串行,无条件等待,具体见下前4种I/O。
异步 Asynchronous
An Asynchronous-I/O operation does not cause the requesting process to be blocked.
并发,无需等待、无需轮询,具体见下异步I/O。
阻塞 vs 非阻塞
应用程序在等待消息时的状态,针对Server端,Server端是否阻塞与Client端无关,至于Client端在等待的前提下啥也不做还是边做其他事边等待,由Client端自己决定。关于阻塞/非阻塞,在网络编程中通常应用在是不是需要等待数据就绪。
阻塞 Blocking
挂起等待,具体见下阻塞I/O。
非阻塞 Non-Blocking
轮询检查等待,具体见下非阻塞I/O。
I/O模型
阻塞I/O(Blocking I/O)
进程阻塞等待,进程挂起睡眠直到数据拷贝完成。
简单易实现,不适于同时处理大量套接字、扩展性差。改进:
- 多进程/线程;
- 线程池或连接池;
非阻塞I/O(Non-Blocking I/O)
进程非阻塞等待,数据准备阶段进程不睡眠而是轮询等待(导致CPU占用率高)、数据拷贝阶段进程阻塞。
便于处理多套接字的通信,但实现复杂、需仔细检查返回代码并对收到的错误信息进行处理。
I/O多路复用(I/O Multiplexing) Reactor模式
对一个IO端口,两次调用、两次返回。
数据准备阶段进程阻塞等待于select()/epoll()、数据拷贝阶段进程阻塞,其中select()可以同时阻塞多个I/O端口的操作,直到某个端口有数据可读/写时,才真正调用I/O操作函数。 实现同时对多个IO端口进行监听,适于处理大量套接字的连接。
I/O多路复用经典模式(2种):
- 基于同步I/O的Reactor
- 基于异步I/O的Proactor
参考:Reactor和Proactor;
信号驱动I/O(Signal Driven I/O)
两次调用,两次返回。
数据准备阶段进程继续执行、数据拷贝阶段进程阻塞。 需要建立信号处理程序。
异步I/O(Asynchronous I/O) Proactor模式
无阻塞,无需等待、无需轮询、无需多线程。
异步过程调用,调用者不能立刻得到结果,实际处理这个调用的部件在完成处理后,通过状态、通知或回调来通知调用者。异步需操作系统的底层支持。
注:信号驱动模型由内核通知用户可以开始一个IO操作(数据在内核缓冲区中),异步IO由内核通知IO操作已经完成(数据已在用户空间中)。
比较
同步IO - 异步IO:数据访问时的消息通知机制,数据访问/拷贝时(第二阶段)进程是否等待,主动等待还是被动通知等待消息的触发! 同步IO和异步IO的关键区别反映在数据拷贝阶段是由用户线程完成还是内核完成。
阻塞IO - 非阻塞IO:等待数据就绪时(第一阶段)应用程序的调用是否立即返回!
推荐书籍:
- Richard Stevens -《UNIX网络编程 - 卷1》:第六章
参考: