源码阻塞IO、非阻塞IO、同步IO、异步IO这些名词相信很多小伙伴都比较迷惑吧?我以前也是,怎么了?同步非阻塞IO,异步非阻塞IO,头太大了。仔细读完《UNIX 网络编程卷一 套接字联网 API(第三版)》的6.2章,终于明白了这些名词。下面我就用《UNIX 网络编程卷一 套接字联网 API(第三版)》我根据6.2章的内容,整理了各种网络IO模型的具体定义和一些容易混淆的地方。简介Unix下有5种可用的IO模型,分别是:阻塞I/O非阻塞I/OI/O多路复用(select和poll)信号驱动I/O(SIGIO)异步I/O(POSIXaio_系列函数)阻塞I/O模型最流行的IO操作是阻塞IO(BlockingIO)。以UDP数据报套接字为例,下图是其阻塞IO的调用过程:上图中,进程调用recvfrom,其系统调用,直到数据报返回并复制到应用进程的缓冲区中或当发送错误时,进程从调用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相比,IO多路复用模型的优势并不明显,而且在使用上,IO多路复用模型需要多调用一次select,所以在易用性上略逊于blockingIO。但是select的杀手级特性是它可以监听多个文件描述符,大大减少了阻塞线程的数量。signal-drivenIOmodel信号驱动模型如上图所示。当文件描述符准备好后,我们可以让内核通过信号的方式通知我们。我们首先需要开启socket的信号驱动IO功能,通过sigaction系统调用安装一个信号处理函数。sigaction系统调用是异步的,它会立即返回。当有数据时,内核会向进程发送一个SIGIO信号,然后我们的信号处理函数就会执行,我们可以在这个函数中调用recvfrom来读取数据。异步IO模型异步IO(asynchronousIO)是由POSIX规范定义的,POSIX中定义了几个异步IO操作函数,这个函数的工作原理是:告诉内核开始一个动作,让内核通知我们的应用进程整个操作(包括将数据从内核复制到应用进程缓冲区)完成后。异步IO模型和信号驱动IO模型的主要区别在于:信号驱动IO是在我们可以开始IO操作时由内核通知,而异步IO模型是在IO操作完成时由内核通知.异步IO模型的运行过程如图所示:当我们调用aio_read函数时(POSIX异步IO函数以aio_或lio_开头),传入描述符,缓冲区指针,缓冲区大小(和read一样的三个参数)和文件偏移量(类似于lseek)传递给内核,并告诉内核在整个操作完成后如何通知应用进程。系统调用立即返回,应用进程不会阻塞等待IO完成。各种IO模型的比较如图所示。以上五种IO模型中,前四种模型(阻塞IO、非阻塞IO、IO多路复用、信号驱动IO)的主要区别在于第一阶段,因为它们的第二阶段是相同的:在数据复制期间从内核到调用者的缓冲区,进程被recvfrom调用阻塞。第五种,即异步IO模型,应用进程不需要分两个阶段进行处理。内核为我们处理数据等待和数据复制过程。关于POSIX定义的同步IO和异步IO:AsynchronousI/OoperationcausestherequestingprocessbeingblockeduntilthatI/Ooperationcompletes(causestherequestingprocesstoblockuntiltheIOoperationiscomplete).异步I/O操作不会导致请求进程被阻塞(不会造成请求进程阻塞)。根据上面的定义,我们的前四种模型:阻塞IO模型、非阻塞IO模型、IO多路复用模型和信号驱动IO模型都是同步IO模型,因为真正的IO操作(recvfrom调用)会阻塞进程(因为当有数据时,recvfrom会阻塞等待内核将数据从内核空间复制到应用进程空间,recvfrom要等到赋值完成才会返回。)只有异步IO模型匹配定义的异步IO通过POSIX。小结在处理网络IO操作时,阻塞IO和非阻塞IO都是同步IO。只有调用了特殊的API,才是异步IO。所以,网上常说的“同步阻塞IO”和“同步非阻塞IO”,其实都是阻塞IO模型。而非阻塞IO模型,因为阻塞IO和非阻塞IO模型都是同步的,加上“同步”二字其实是多余的。网上常说的“异步非阻塞IO”其实就是异步IO模型。本文由yongshun发表于个人博客,采用类似签名-相同方式共享3.0中国大陆许可协议。Email:yongshun1228@gmail.com本文标题:Unix网络IO模型:同步与异步,傻傻分不清楚?本文链接为:https://segmentfault.com/a/1190000007355931
