在《UNIX网络编程卷1》中,提到了五种IO模型,分别如下:
- 阻塞IO(Blocking IO)
- 非阻塞IO(nonblocking IO)
- IO多路复用(IO multiplexing)
- 信号驱动的IO(SIGIO)
- 异步IO(asynchronous IO)
对于一个IO操作,通常可以将其分为两个阶段
- 内核等待数据ready
- 将数据从内核空间拷贝到用户空间
上述不同IO模型的区别主要在IO操作两个阶段的不同
阻塞IO
以recefrom为例,阻塞IO的数据读取操作流程如下。当用户调用了recvfrom后,进程会阻塞,直到数据到来并且数据从内核缓冲区拷贝到用户缓冲区,调用才会得到返回结果,进程结束阻塞。
非阻塞IO
同样以recvfrom为例,当用户设置socket为非阻塞时,其数据读取操作流程如下。用户进程开始执行recvfrom后,如果在内核缓冲区没有数据,则函数直接返回,不会阻塞进程。在用户进程看起来,它发起一个read操作后,立刻得到了结果。如果数据没有准备好,用户进程需要不断轮询;如果数据准备好了,操作系统将内核缓冲区中的数据拷贝到用户缓冲区,然后返回。
IO多路复用
IO多路复用的流程如下图所示。其意义在于一个进程可以处理多个IO。IO多路复用的原理是一个进程轮询用户进程感兴趣的IO操作,当某个IO发生了用户感兴趣的事件,通知用户进程。
以select为例,当用户进程调用了select后,整个进程阻塞,直到select所监听的某一socket数据准备好(内核空间数据ready),select返回。返回后,用户进程需要再调用recvfrom,将内核缓冲区的数据拷贝到用户空间。
注意:为了防止进程阻塞,IO多路复用中的socket一般设置为no-blocking
异步IO
上述三种IO都属于同步IO,异步IO的流程如下图所示。当用户进程发起异步IO调用之后,内核立刻返回,用户进程不会被阻塞。之后,内核等待数据ready,并将其拷贝到用户空间。操作完成之后,内核会通过信号或回调函数的形式,通知用户进程数据拷贝完成,进行下一步处理