在谈到Socket I/O模型的时候通常有5种:

  • blocking I/O —— 阻塞式
  • nonblocking I/O —— 非阻塞式
  • I/O multiplexing(select and poll) —— I/O多路复用
  • signal driver I/O(SIGIO) —— 信号驱动的I/O
  • asynchronous I/O (the POSIX aio_functions) —— 异步I/O

在考虑I/O模型的过程,需要区分操作系统和应用程序,也就是说在一次I/O操作中会涉及到两个对象:调用命令的进程(或者线程)和系统内核(kernel),后面将会以read操作为例学习I/O模型,所以我们需要清楚进行一次read操作需要经历两个阶段:

  1. 等待数据准备好
  2. 将数据从内核拷贝到进程中

而5中I/O模型的区别也就在这两个阶段的处理方式上。

blocking I/O

 

Socket I/O 模型_Socket I/O

在这种模式下,整个读操作都会被阻塞,直至待读取数据被读入到应用程序中。

nonblocking I/O

 

Socket I/O 模型_基础知识_02

这种方式下,读操作不会被阻塞,系统立即返回,不过当数据还没有准备好的时候,系统返回EWOULDBLOCK错误代码,如果数据准备好,则将数据拷贝到进程的内存空间,而后返回OK。

I/O Multiplexing

 

Socket I/O 模型_Linux_03

这 种模式将这种读取的调用过程分为两部分,首先使用select方法判断内核是否为读取数据做好了准备,而后调用读命令将数据从内核拷贝到进程的内存空间。 调用select方法也是阻塞的,不过select方法可以一次性检测很多个文件描述符(一个文件描述符代表一个socket连接),所以它可以同时处理 多个I/O操作。也就是说使用多路复用模式,阶段阻塞还是存在,不过增大了带宽,从而使得对socket连接的处理会高效,消息模型中的 reactor(反应器)就是利用这点来实现的。

signal-driven I/O

 

Socket I/O 模型_基础知识_04

信号驱动I/O,应用程序先向内核注册一个数据到达的信号量,当内核检测到数据准备好的时候,就通知应用进程执行读数据的操作,将数据读入应用进程。

Asynchronous I/O

 

Socket I/O 模型_Linux_05

异步I/O模型中,应用程序调用读操作时,会将一个回调函数传给内核,当内核将数据准备好,并读入到应用进程空间后会调用回调函数完成读操作。内核读取、拷贝数据的整个过程,应用进程不会被阻塞。

钓鱼和泡MM

为了方便理解,将5中模型总结为A、B、C、D和E五人和一群MM出去钓鱼,说好要钓很多鱼,搞一个鲜鱼Party:

  • A用最老式的鱼竿,一直守着,等鱼上钩了在拉杆。
  • B用的鱼竿好一点,能够显示是否有鱼上钩了,所以他过会就看看有没有鱼,有就拉杆,没有就泡MM。
  • C用的鱼竿和B一样,不过他没有去泡MM,而后多放了几个鱼竿,然后守着,一旦有鱼上钩就拉杆。
  • D的鱼竿跟高级一点,如果有鱼上钩了,就会发声告诉D,不用D过会还的过来看看,D有更多的时间泡MM。
  • E有钱,很有钱,他直接找了一个人替他钓鱼,说好每钓上一条就给我发个短信,然后他就专心泡MM。


参考:

《Unix Network Programming Volume 1》

还有几位仁兄的博客,当时没有记住,如果有侵权的情况,请告知,谢谢!