源码阻塞IO、非阻塞IO、同步IO、异步IO这些名词相信很多小伙伴都比较迷惑吧?我以前也是,怎么了?同步非阻塞IO,异步非阻塞IO,头太大了。仔细读完《UNIX 网络编程卷一 套接字联网 API(第三版)》的6.2章,终于明白了这些名词。下面我就用《UNIX 网络编程卷一 套接字联网 API(第三版)》我根据6.2章的内容,整理了各种网络IO模型的具体定义和一些容易混淆的地方。介绍Unix下有5种IO模型,分别是:BlockingI/ONon-blockingI/OI/OMultiplexing(selectandpoll)Signal-drivenI/O(SIGIO)AsynchronousI/O(POSIXaio_seriesfunctions)BlockingI/O模型最新的IO操作是阻塞IO(BlockingIO)。以UDP数据报socket为例,下图是其阻塞IO的调用流程:sendsanerror因此,进程从调用recvfrom到返回的整个期间都是阻塞的。非阻塞IO(Non-BlockingIO)进程设置一个socket为非阻塞来通知内核:当调用线程请求的IO操作需要调用线程休眠等待操作完成时,做此时不休眠调用线程,而是返回错误。如上图所示,前三次调用recvfrom时,没有数据返回,因此内核转而立即返回EWOULDBLOCK错误。第四次调用recvfrom时,已经有数据了。这时候recvfrom会阻塞,等待内核把数据分配给应用进程的缓冲区,然后返回。(注意,当有数据时,recvfrom是阻塞的,它会等待内核将数据复制到应用进程的缓冲区中才返回)。当应用进程像这样在非阻塞描述符上调用recvfrom时,我们称之为轮询(polling)。应用进程不断轮询内核,看一个操作是否完成,这会消耗大量的CPU时间,但这种模型偶尔会遇到,通常只出现在专门提供某种功能的系统中。IO多路复用模型与IO多路复用(IOmultiplexing),我们可以调用select或poll,阻塞在这两个系统调用之一上,而不是阻塞在真正的IO系统调用上。例如:如上图所示,当select被调用时,select会阻塞等待数据报socket变为可读。当select返回socket可读的条件时,我们可以调用recvfrom将读取的数据报复制到应用程序进程缓冲区。IO多路复用模型与blockingIO相比,并没有明显的优势,而且在使用上,IO多路复用模型需要多调用一次select,所以从easy易用性上来说,略逊于blockingIO。不过select的杀手级特性是可以监听多个文件描述符,大大减少了阻塞线程的数量。信号驱动IO模型信号驱动模型如上图所示。当文件描述符准备好后,我们可以让内核用信号通知我们。我们首先需要开启socket的信号驱动IO功能,通过sigaction系统调用安装一个信号处理函数。sigaction系统调用是异步的,它会立即返回。当有数据时,内核会向进程发送一个SIGIO信号,然后我们的信号处理函数就会执行,我们可以在这个函数中调用recvfrom来读取数据。AsynchronousIO模型异步IO(asynchronousIO)是由POSIX规范定义的,POSIX中定义了几个异步IO操作函数。这个函数的工作原理是:告诉内核开始一个动作,让内核执行整个操作(包括将数据从内核复制到应用进程缓冲区),完成后通知我们的应用进程。异步IO模型和信号驱动IO模型的主要区别在于,信号驱动IO是在我们可以开始IO操作的时候由内核通知的,而异步IO模型是在我们的IO操作开始时由内核通知的完全的。异步IO模型的运行过程如图所示:当我们调用aio_read函数(POSIX异步IO函数以aio_或lio_开头)时,将描述符、缓冲区指针、缓冲区大小传递给内核(三个参数相同asread)和文件偏移量(类似于lseek),并告诉内核在整个操作完成后如何通知应用进程。系统调用立即返回,应用进程在等待IO完成时不会阻塞。各种IO模型的比较如图所示。以上五种IO模型中,前四种模型(阻塞IO、非阻塞IO、IO多路复用、信号驱动IO)的主要区别在于初始阶段,因为它们的第二阶段是相同的:期间数据从内核复制到调用者的缓冲区,进程被阻塞在recvfrom调用中。而第五个,也就是在异步IO模型中,这两个阶段不需要应用进程处理,内核是我们处理的好了,数据等待和数据复制过程。关于同步IO和异步IO,根据POSIX定义:AsynchronousI/OoperationcausestherequestingprocesstobeblockeduntilthatI/Ooperationcomplete(导致请求进程阻塞直到IO操作Complete)。异步I/O操作不会导致请求进程被阻塞(doesnotcausetherequestingprocesstobeblocked)。根据上面的定义,我们的前四种模型:阻塞IO模型、非阻塞IO模型、IO多路复用模型信号驱动IO模型和信号驱动IO模型都是同步IO模型,因为真正的IO操作(recvfromcall)会阻塞进程(因为当有数据时,recvfrom会阻塞等待内核将数据从内核空间拷贝到应用进程空间,当赋值完成后,recvfrom返回。)只有异步IO模型匹配POSIX定义的异步IO。综上所述,在处理网络IO操作时,阻塞IO和非阻塞IO都是同步IO。只有调用了特殊的API,才是异步IO。因此网上常说的“同步阻塞IO”和“同步非阻塞IO”其实就是阻塞IO模型和非阻塞IO模型,因为阻塞IO和非阻塞IO模型都是同步的,加上“同步”二字其实是多余的。网上常说的“异步非阻塞IO”,其实就是异步IO模型。
