关于:socket阻塞、非阻塞,同步、异步、I/O模型
一般提起I/O模型的时候,这几个概念总是有点让人模糊,其实如果理解了,还是比较好区分的。
Ps:参考《UNIX网络编程:卷一》
1、“同步”和“异步”
如果单单理解这两个概念还是很好区分的:
同步:处理事情的的时候需要串行执行,也就是说做完一件再去做另一件,不管是否需要时间等待。也就是说,无条件等待这件事被完成。
异步:处理事情的的时候可以并发,即可以同时做几件事,不需要一件事做完再做另一件事。也就是说,当一件事完成的时候,通知我完成了就OK了
。(这里需要注意理解的是,用于真正处理事件的是其他的处理部件(函数))
2、“阻塞”和“非阻塞”
阻塞:简单理解就是说我想要做这件事,但是现在还不能去做,那么我必须挂起等待。举个不太恰当的例子:去饭店吃饭, 没有座位了,我就只能一
直等着,等到有座位。
非阻塞:简单理解就是说我想要做这件事,但是现在还不能去做,那么我立即返回。但是需要注意,非阻塞情况下需要不断的 去轮循查看是不是可
以做这件事了。用上面的例子,去饭店吃饭,没有座位,那么我先到隔壁的休息室坐着等着,每5min去看一次,是不是有座位了。
3、综合理解
“同步”和“异步”指的是整体上我是不是需要去等待。“阻塞”和“非阻塞”指的是当一件事不能被立即处理的时候,是不是需要等待。也可以这么理解,
“同步”和“异步”的在是不是需要等待方面的处理优先级比“阻塞”和“非阻塞”高。或许这句话说得不是很准确,
但是确实可以这样理解:只有在同步的情况下才会有“阻塞”和“非阻塞”之说,异步情况,必须是不阻塞的!
> 同步:阻塞 / 非阻塞
> 异步:
关于阻塞/非阻塞,在网络编程中大部分都是应用在是不是需要等待数据就绪,一般当我们需要读数据的时候,这个时候数据还没有准备好,那么如果
是阻塞套接字,那么需要一直阻塞等到数据准备就绪;如果是非阻塞套接字,那么立刻返回,一定周期时间后再次轮询看看是不是准备好了!
一般情况使用阻塞情况就OK,但是并发性就难以保证,不过我们还是可以使用多线程技术来处理,每一个套接字使用一个线程处理,那么谁睡阻塞和
我没大关系,但是线程多了之后再性能上难以保证。
关于“非阻塞”应用点:我们时刻需要知道的是非阻塞在数据还没有就绪的时候,就立即返回,那么在这一点上做一些文章就得到了select这样的I/O复用
的套接字。经过改良之后,select本质还是轮询套接字,只不过,这一次轮询的不是一个套接字,如果只轮询一个套接字,那么着实有点浪费啊~select
可以轮询多个套接字,看看那些套接字准备好了数据,然后处理就OK,这样的话,select将非阻塞IO的效率就提高很多了。
4、常用5中I/O模型(UNIX网络编程)
1) 阻塞I/O
2) 非阻塞I/O
3) I/O复用(select 、poll、epoll)
4) 信号驱动I/O (signal driven I/O (SIGIO))
5) 异步I/O (asynchronous I/O (the POSIX aio_functions))
pre:在前面已经说过,只有在同步下,才有阻塞/非阻塞之说,异步就是异步,没有任何等待。。。
那么上面的5中IO,只有最后一个才是真正的异步IO。
1、阻塞I/O
按照上面的说法我们可以说成是“同步阻塞IO”,那么也就是说这个会一直阻塞,阻塞到数据拷贝结束!
看下图(下面所有图示来自:UNIX网络编程:卷一)
最左边的那句话“进程阻塞于recvfrom的调用”说明在整个过程中进程都是阻塞的!右边的“等待数据”这句话说明,当数据没有准备好的时候,由于现在
是“阻塞”模式,那么需要等待数据。同时注意,在copy数据的时候,必须等待。
2、非阻塞I/O
按照我们的说法其实是:“同步非阻塞IO”,当数据没有准备的好的时候,需要反复轮询看看是不是准备好,其实这个时候进程本质上也是在等待,只不
过等待是无止境的等待轮询的结果。
最左边的那句话“进程反复调用recvfrom等待返回成功指示”说明本质还是在等待,只不过换了个方式玩而已!即:不断轮询,不断等待。。。同时注意
,在copy数据的时候,必须等待。
3、I/O复用
I/O复用模型一般有select、poll、epoll函数,其实按照我们上面的说法,叫“同步I/O复用”。这个本质上是“非阻塞”模型,但是是演化之后的升级版模型
。拿select来说,这个函数一次可以轮询很多个套接字,而不仅仅上面那样只轮询一个套接字,select轮询过程中只要有一个套接字上面数据准备好,
那么就可以被处理了。这样的话比上面的优势就体现出来了,上面的非阻塞模式,如果在一个点上阻塞了,那么即使后面存在可以读写的套接字,那也得跟着等待,所有select提高很大效率。
同时注意,在copy数据的时候,必须等待。
4、信号驱动I/O
这个也是“非阻塞IO”模型一种演化。在套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个
SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。
注意:当数据没有就绪的时候,进程不会阻塞,继续运行,当数据就绪的时候,通过信号通知进行处理数据!
同时注意,在copy数据的时候,必须等待。
5、 异步I/O
没有阻塞,只需要发出一个处理命令,然后自己做自己的事,然后那件事被处理结束之后,通知说事件结束,处理一下收尾工作就OK。
注意:在copy数据的时候,不需要等待!!
总结:一个IO操作基本包括两个阶段:
(1)等待数据准备就绪
(2)从内核向进程复制数据
如果是:
同步阻塞:那么会一直阻塞下去。数据没有就绪,那么需要等待,拷贝数据也需要等待。
同步非阻塞:其实也需要一直等待,只不过方式不同。数据没有准备好,那么需要轮询,这里衍生了像I/O复用这样更高效率的IO手段。拷贝数据需要
等待。
异步:只需要处理头尾,中间无需等待。
OK,总结完毕..