I/O 阻塞与非阻塞,同步与异步
本文为博主原创文章,未经博主允许不得转载 http://www.cnblogs.com/kiplove/p/6724431.html
一、I/O模型
一个输入操作通常包括两个阶段:
(1)等待数据准备好;
(2)从内核向进程复制数据。
1、阻塞式
系统调用直到数据报到达且被复制到应用进程的缓冲区或者发生错误才会返回。也就是说recvfrom从开始到它返回的整段时间内都是被阻塞的。
进程把一个套接字设置成非阻塞是在通知内核:当所有的I/O操作非得把本进程投入睡眠才能完成时,不要把进程投入睡眠而是返回一个错误,可以看的出来每一次系统调用都会立即返回。当一个应用对一个非阻塞描述符循环的询问内核是否准备好数据(轮询),往往会消耗大量的CPU时间,所以一般我们都是配合某一功能的系统才会使用这种模型。
3、I/O复用(事件驱动)
常见的I/O复用是select、poll和epoll的调用,阻塞时发生在这几个系统调用之上,而不是阻塞在真正的I/O系统调用之上。使用它们的优势在于可以同时等待多个描述符就绪,从而减少了等待时间。
4、信号驱动式
等待数据到达期间不阻塞,程序继续执行,只要等待来自信号处理函数的通知:数据准备好被处理,但是在数据复制阶段还是阻塞的。
5、异步
告知内核启动某个操作,并让内核在整个操作(包括上述的两个阶段)完成后再通知,即两个阶段都不阻塞。 话句话说就是内核已经完成的数据的读写,通知的只是结果。
总结:
前四种主要区别在第一阶段,就是等待数据的过程,而第二阶段是一样的,在数据从内存复制到调用者的缓冲区间,都发生阻塞了;而异步在这两个阶段都处理了,也就是全程没发生阻塞。因而前四种都属于同步模型,只有最后一个是异步的。异步一般都是通过特殊的API实现的,比如AIO,而无论阻塞模型与非阻塞模型或者I/O复用都是同步的。
二、阻塞与非阻塞
首先我们要明确阻塞与非阻塞发生在第一阶段,关注的是程序在等待调用的结果时状态(数据准备好了吗),如果一直等待着结果出来,不去干其他事,这就是阻塞;如果调用后立刻返回一个状态,如果没有准备好就挂起,干其他事去,之后再来询问,如此循环往复直到获得结果,这就是非阻塞。
三、同步与异步
同步与异步发生在两个阶段,关注的是消息的通信机制,同步就是在结果没有出来前,调用时不会返回的,一旦返回就得到返回值,也就是说是调用者自己等待调用的结果;异步则是调用发出后,直接返回没有等待结果,而是被调用者来通知调用者处理完成了。
同步I/O操作:导致请求进程阻塞,直达I/O操作完成;
异步I/O操作:不导致请求进程阻塞。
例子:
你网上问淘宝客服有没有《unix网络编程》,你一直坐在电脑前等待,直到客服回复有没有的结果(这就是阻塞);你不管客服有没有告诉你,你自己先一边去玩游戏去了, 偶尔过几分钟check一下客服有没有回复(这就是非阻塞)。如果你问了客服之后就不管了,直到客服@你了(这就是异步反之就是同步)。