欢迎转载,转载请注明原文地址:http://blog.csdn.net/majianfei1023/article/details/45314803
零.导论
阻塞是不是同步,非阻塞是不是异步,他们是什么关系?刚接触网络编程的同学经常会问这种问题,而且把他们搞混,在这里我用我自己的理解详细解答一下。
想了半天,决定先讲阻塞和非阻塞,为什么呢,因为他们的时序在前面(至少我是这么理解的)。
我们以一句总结开篇,再以另一句总结结束吧。
阻塞IO和非阻塞IO的区别就在于:阻塞/非阻塞, 它们是程序在等待消息(调用函数)时的状态,应用程序的调用是否立即返回!
同步IO和异步IO的区别就在于:同步/异步, 它们是消息的通知机制,同步IO是调用完成之后返回来通知的,异步是调用完成之后不等返回,自己做自己的事,然后消息完成之后自然会有它的方式(状态、通知、回调等)来通知它。
所以,他们关注的点是不一样的,阻塞和非阻塞关注的是调用是否立即返回,同步和异步关注的是消息怎么通知给程序的。
一、阻塞与非阻塞
阻塞/非阻塞, 它们是程序在等待消息(无所谓同步或者异步)时的状态.
1. 阻塞I/O模型:
阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。
下面我用 UNP1的图来进行讲解:
如图6.1:
进程调用recvfrom向内核请求数据,内核数据并没有准备好时,不返回给进程。这时进程就阻塞在recvfrom系统调用以等待数据就绪。
阻塞函数在完成其指定的任务以前不允许程序调用另一个函数。例如,程序执行一个读数据的函数调用时,在此函数完成读操作以前将不会执行下一程序语句。当服务器运行到recvfrom语句时,而没有客户连接服务请求到来,服务器就会停止在recvfrom语句上等待连接服务请求的到来。这种情况称为阻塞(blocking)。阻塞的时候cpu不会给线程分配时间片,即线程暂停运行,但CPU可以给其他线程(或者进程)使用,并不会浪费CPU性能。
当数据没有准备好,没有返回成功(或失败)的指示之前,程序卡在recvfrom调用而不会继续往下走。直到调用返回成功(或失败)。
2.非阻塞I/O模型:
非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
如图6.2:
当内核中没有数据准备好时,内核立即返回一个EWOULDBLOCK错误给应用程序,应用程序不需要把本进程投入睡眠。这样应用程序(进程)就需要不停的系统调用,也就是轮询(polling)。应用程序持续轮询内核,以查看数据是否就绪。这么做往往耗费大量CPU时间(当然你只是想查看数据有没有准备好,没准备轮询的话,那就不会了)。
看到上面两个图有什么区别吗?在等待数据阶段,
果然看图比我巴拉巴拉讲一大堆有用得多,我们仔细看,系统调用recvfrom分为两个阶段,
1.等待数据准备好的阶段(这个决定了是阻塞还是非阻塞);
2.讲数据从内核拷贝到用户空间的阶段(这个我们稍后讲,它决定了是同步还是异步)。
从图可以看出,阻塞和非阻塞的区别主要在于等待数据的时候是等待数据就绪还是立即返回,
所以:阻塞/非阻塞, 它们是程序在等待消息(无所谓同步或者异步)时的状态。(阻塞就是一直阻塞到有数据准备就绪,非阻塞就是立即返回)。
二、同步与异步
同步/异步, 它们是消息的通知机制:
1.同步IO模型:
所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。
2.异步IO模型:
当一个异步过程调用发出后,调用者不会立刻得到结果。
实际处理这个调用的部件是在调用发出后,
通过状态、通知来通知调用者,或通过回调函数处理这个调用。
(也就是说,阻塞关注的是立即返回还是稍后返回的话,异步返回都不需要,他不关注这个,处理完会自动通知他)
我们继续看 图 6.1和图6.2,之前研究阻塞和非阻塞的时候,只顾研究他们的不同了,那么现在我们研究一下他们的相同点:看下半部分,将数据从内核复制到用户空间的时候,等到复制完成,返回给进程,进程继续进行。
那么我们再看一张图:
如图6.5,这是异步I/O模型,不用我说,大家都看到了。那么这个跟上面两张图的相同点(即:将数据从内核复制到用户空间,然后进程继续执行)有什么区别呢?
真相只有一个:
进程调用后就继续执行了(也就是我们说的,它不关注返不返回),不等数据复制到用户空间,进程继续执行了,那么数据复制到用户空间之后怎么告诉进程呢?反正内核有办法通知你数据已经处理好了,你可以选择处理了。
这个是异步,那上面自然是同步了,我们总结一下,同步和异步的区别:
同步I/O操作:导致请求进程阻塞,直到I/O操作完成(你没完成,我就看着你做,然后什么也不做,等你做完了,把东西给我);
异步I/O操作:不导致请求进程阻塞。而当你完成拷贝后,想办法通知进程就是了,它才懒得等你(所以说,异步I/O是很傲娇的,我是老大,我凭什么等你,我先做其他事,等你处理好了,打个电话告诉我)
阻塞IO和非阻塞IO的区别就在于:应用程序的调用是否立即返回!
同步IO和异步IO的区别就在于:数据拷贝的时候进程是否阻塞!
所以,简单地说,阻塞和非阻塞是指等待数据阶段,调用是否立即返回,而同步异步则是指的,复制数据的时候,我是阻塞在这里等你返回,还是我先忙其他的,等你处理好了,告诉我一声。
我再继续拿张图总结一下:
如果我们要观察 阻塞和非阻塞,观察 1和2就可以了:
程序发出调用后是否立即返回。
如果我们要观察同步和异步,观察3和4就可以了:
函数完成时进程是否阻塞(是被动等待返回,还是你主动通知)。
第一列是:同步阻塞I/O模型
第二列是:同步非阻塞I/O模型
第五列是:异步I/O模型。
对unix来讲:阻塞式I/O(默认),非阻塞式I/O(nonblock),I/O复用(select/poll/epoll)都属于同步I/O,因为它们在数据由内核空间复制回进程缓冲区时都是阻塞的(不能干别的事)。只有异步I/O模型(AIO)是符合异步I/O操作的含义的,即在1数据准备完成、2由内核空间拷贝回缓冲区后
通知进程,在等待通知的这段时间里可以干别的事。