也谈阻塞、非阻塞、同步、异步

最近在招聘中,聊到基础的网络编程的时候,发现很多人对BIO、NIO、AIO等理解很是模糊,以为有必要写文章来纠正下不少人的误解。 
在谈这些以前,很是有必要先介绍下Unix 5种IO模型: 
阻塞: 
阻塞是最经常使用的IO模型,默认状况下全部的文件操做都是阻塞的。以套接字编程为例。在进程空间中调用recvfrom,其系统调用直到数据报文到达且被拷贝到应用程序进程的缓存区(或者发生错误)后才返回,期间一直在等待。进程在从调用recvfrom开始到它返回的整段时间内是被阻塞的。有一张很经典的图: 
 
非阻塞: 
调用recvfrom从应用层到内核的过程当中,若是该缓冲区没有数据的话,则直接返回一个EWOULDBLOCK的错误,通常会轮询的进行检查状态,看内核空间有没有数据来。直到有数据,最后完成拷贝。以下图: 
 
IO多路复用: 
Linux系统提供的select/poll/epoll,进程将一个或者多个FD(文件描述符)传递给一个或者多个poll/select系统调用,阻塞在select。select和poll能够帮助侦听不少的FD是否准备就绪。可是,select和poll是顺序扫描去检查FD的就绪状态,效率比较低,并且支持的FD数量有限(没记错的话,默认好像是1024仍是2048,具体记不清)。而epoll是经过事件驱动的方式,当有FD准备就绪的时候,当即回调函数rollback。如图: 
 
谈到epoll,不得不提一个经典的问题,apache和nginx的对比,为何nginx比apache效率高不少,这就是根本的缘由。 
信号驱动: 
这种模型在实际应用的很是少,这里不作过多介绍,能够看图: 
 
异步: 
告知内核启动某个操做,并让内核在整个操做完成后(包括将数据从内核空间拷贝到本身的缓冲区)通知。异步IO的主要特色是完成操做后主动通知。如图: 
nginx

好,上面的可能有点抽象。下面用通俗点的语言来总结一下阻塞,非阻塞,同步,异步。
阻塞,非阻塞:进程/线程要访问的数据是否就绪,进程/线程是否须要等待; 
同步,异步:访问数据的方式,同步须要主动读写数据,在读写数据的过程当中仍是会阻塞; 
异步只须要I/O操做完成的通知,并不主动读写数据,由操做系统内核完成数据的读写。 
再举个网上流传的,很是容易理解的例子: 
老张爱喝茶,废话不说,煮开水。 
出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。 
1 老张把水壶放到火上,立等水开。(同步阻塞)老张以为本身有点傻 
2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞) 
老张仍是以为本身有点傻,因而变高端了,买了把会响笛的那种水壶。水开以后,能大声发出嘀~~~~的噪音。 
3 老张把响水壶放到火上,立等水开。(异步阻塞)老张以为这样傻等意义不大 
4 老张把响水壶放到火上,去客厅看电视,水壶响以前再也不去看它了,响了再去拿壶。(异步非阻塞)老张以为本身聪明了。 
所谓同步异步,只是对于水壶而言。普通水壶,同步;响水壶,异步。虽然都能干活,但响水壶能够在本身完工以后,提示老张水开了。这是普通水壶所不能及的。 
同步只能让调用者去轮询本身(状况2中),形成老张效率的低下。所谓阻塞非阻塞,仅仅对于老张而言。立等的老张, 
阻塞;看电视的老张,非阻塞。状况1和状况3中老张就是阻塞的,媳妇喊他都不知道。 
虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。因此通常异步是配合非阻塞使用的,这样才能发挥异步的效用。apache

相关文章
相关标签/搜索