其实这几个概念可能在实际的开发中并没有什么需要注意的,因为你调用什么函数就知道接下来需要怎么做了,但是如果要确实分清这几个概念的区别和联系,还是需要动动脑筋的。下面这幅图十分的好:
总结来说,区分的关键点在于:
同步-异步:IO资源可用与否是自己去检测,还是依赖于状态、信号、回调等其它机制来通知;
阻塞-非阻塞:IO调用的函数在资源不可用时候是否立即返回,还是被挂起状态直到资源可用;
一、同步阻塞IO
算是最简单最容易理解的模型了,且Linux默认的IO模型就是这样的,比如你用open打开文件或者socket创建套接字而没有显式使用O_NONBLOCK/SOCK_NONBLOCK参数的时候,那么后续使用read/write/recv/send等函数的时候,当资源不可用,应用程序将阻塞在这些调用上面,直到这些调用成功后返回,程序才会继续执行下去。这时候的阻塞,内核会将程序进程切换到睡眠的状态,当内核完成IO将数据返回到用户态可用的时候,程序会被切换继续运行下去。
二、同步非阻塞IO
通常在open和socket调用时候添加了O_NONBLOCK/SOCK_NONBLOCK标志,或者采用ioctl等机制后续设置了这个标志的时候,当调用上面的IO操作函数时候,如果此时资源不可用,调用会立即返回(通常返回EAGAIN/EWOULDBLOCK错误)。这个时候应用程序通常的做法是sleep一会儿,或者干点别的事情,然后再次进行IO调用看资源是否可用了。总体来说该方法是比较低效率的,如果忙等待会浪费很多计算资源;且如果休眠时间长或者干别的事情长,那么总体IO响应将会变得很不及时。
三、异步阻塞IO
这其实是一个带阻塞通知的非阻塞IO,是select/poll/epoll函数族的典型情况。从使用上来说,虽然将fd/socket设置为了非阻塞形式,但是select/poll/epoll的调用却是阻塞的,所以这里实际上将阻塞从原先的IO操作转嫁挪动到的资源通知上面了。
四、异步非阻塞IO
最复杂的情况了,在linux中有aio_xxxx对应的函数族,其最大的特点是程序的执行和IO操作可以重叠执行:程序调用aio_xxxx后立即返回,然后程序就执行其它的代码了,而内核完成IO操作之后,会通过状态、信号、回调函数等机制完成IO操作之后对应要处理的内容。Windows的完成端口就是典型的异步非阻塞IO模型。而在Linux环境下,感觉异步非阻塞IO不太遭怎么待见,网站基本的构架都是基于epoll这类异步阻塞IO设计的。我想,这可能是因为异步非阻塞IO的优势不是特别的明显,而异步阻塞IO比较符合一般人的思维编程习惯吧。
本文完!