2013年08月17日

Unix网络io模型


在Unix网络编程一书中,指出在Unix下面有5种可用的I/O模型,分别是:

1.阻塞式IO 2.非阻塞式IO 3.IO复用(select和poll) 4.信号驱动式(SIGIO) 5.异步IO(POSIX的aio_系列函数)

对于上面的各种IO模型的理解,是基于一个输入操作的分析,每一个输入操作通常包括两个不同的阶段:

1.等待数据准备好 2.从内核向进程复制数据

对于一个套接字上的输入操作,第一步通常涉及等待数据从网络中到达,当所等待分组到达时,它被复制到内核的某个缓冲区;第二步就是把数据从内核缓冲区复制到进程缓冲区。

阻塞式IO模型

最流行的IO模型是阻塞式IO模型,书中给出了一个使用recvfrom系统调用来演示阻塞式IO模型。

可以看出,调用recvfrom函数之后,就一直阻塞,知道数据报到达并且拷贝到应用程序缓冲区或是出错之后才返回。该调用返回之后,进程才开始执行后面的语句处理数据报。

非阻塞式IO模型

进程将一个套接字设置成非阻塞是在通知内核:当所请求的IO操作非得让进程睡眠不能完成时,不要让进程睡眠,而应返回一个错误。看下面给出的示意图明白这句话的意思。

前三次调用recvfrom时没有数据返回,因此内核立即返回一个EWOULDBLOCK错误。第四次调用recvfrom时,数据报准备好,被拷贝到应用进程缓冲区,recvfrom返回成功指示,接着可以处理数据。 当一个应用进程像这样对一个非阻塞式描述字循环调用recvfrom时,这个过程称为polling轮询。应用进程不断的查询内核,是否某操作以准备好。这对CPU时间是极大浪费的。

IO复用模型

有了IO复用,就可以调用select或poll,这两个系统调用中的某一个上阻塞,而不是阻塞在真正的IO系统调用上。看下面的示意图。

我们阻塞在select调用上,等待数据报套接字变为可读,当select返回套接字可读这一条件时,我们调用recvfrom把所读数据报复制到应用进程缓冲区上。与上面的阻塞式IO模型,可能不显得有什么优势,并且还需要两次系统调用,其真正的优势在于可以一次等待多个描述符就绪。

信号驱动式IO模型

可以使用信号,让内核在描述符就绪时发送SIGIO信号通知进程,看下面的图。

首先需要开启套接字的信号驱动式IO功能,并通过sigaction系统调用安装一个信号处理函数,该系统调用立即返回,进程可以继续做其他的工作。也就是说进程此时没有阻塞。当数据报准备好读时,内核就为该进程产生一个SIGIO信号。随后就可以在信号处理函数中调用recvfrom读取数据报,recvfrom过程可能需要把数据从内核缓冲区拷贝到进程缓冲区。

异步IO模型

异步IO是由POSIX规范定义。我们让内核启动操作,并在整个操作完成之后(包括将数据从内核拷贝到进程的缓冲区)通知我们。这与前面的信号驱动式IO模型的主要区别在于:信号驱动式IO模型是由内核通知我们何时可以启动一个IO请求,例如何时调用recvfrom函数,而异步IO模型是由内核通知我们IO操作何时完成。看下面的图。

我们调用aio_read函数(POSIX异步IO函数以aio_或lio_开头),给内核传递描述符,给内核传递描述符,缓冲区指针,缓冲区大小和文件偏移,并告诉内核当整个操作完成时如何通知我们。该系统调用立即返回,而且在等待IO完成期间,我们的进程不阻塞。

总结

这5种不同的模型,可以看出,前4种模型的主要区别在于第一阶段,因为它们的第二阶段是一样的:在数据从内核复制到调用者的缓冲区期间,进程阻塞于cevfrom调用。

对于同步IO和异步IO,POSIX把这两个术语定义如下:

1.同步IO操作导致请求进程阻塞,直到IO操作完成 2.异步IO操作不导致请求进程阻塞

下面是5种IO的对比图。

根据上面的定义,前4种模型–阻塞式IO模型,非阻塞式IO模型,IO复用模型和信号驱动式IO模型都是同步IO模型,因为真正的IO操作recvfrom将阻塞进程。只有异步IO模型与POSIX定义的异步IO相匹配。

前一篇: Openstack Eventlet分析(一) 后一篇: 最长递增子序列