zl程序教程

您现在的位置是:首页 >  其他

当前栏目

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

同步异步 堵塞
2023-09-11 14:20:45 时间

近期在招聘中,发现不少人对BIO、NIO、AIO等理解非常模糊,认为有必要写文章来纠正下非常多人的误解。
在谈这些之前,非常有必要先介绍下Unix 5种IO模型:
堵塞:
堵塞是最经常使用的IO模型,默认情况下全部的文件操作都是堵塞的。以套接字编程为例。在进程空间中调用recvfrom。其系统调用直到数据报文到达且被复制到应用程序进程的缓存区(或者错误发生)后才返回,期间一直在等待。

进程在从调用recvfrom開始到它返回的整段时间内是被堵塞的。有一张非常经典的图:
堵塞IO模型
非堵塞:
调用recvfrom从应用层到内核的过程中,假设该缓冲区没有数据的话,则直接返回一个EWOULDBLOCK的错误,通常会轮询的进行检查状态,看内核空间有没有数据来。

直到有数据,最后完毕拷贝。

例如以下图:
非堵塞IO模型
IO多路复用:
Linux系统提供的select/poll/epoll,进程将一个或者多个FD(文件描写叙述符)传递给一个或者多个poll/select系统调用,堵塞在select。

select和poll能够帮助侦听非常多的FD是否准备就绪。可是,select和poll是顺序扫描去检查FD的就绪状态。效率比較低,并且支持的FD数量有限(没记错的话,默认好像是1024还是2048。详细记不清)。而epoll是通过事件驱动的方式,当有FD准备就绪的时候,马上回调函数rollback。如图:
IO多路复用模型
谈到epoll,不得不提一个经典的问题。apache和nginx的对照。为什么nginx比apache效率高非常多,这就是根本的原因。
信号驱动:
这样的模型在实际应用的非常少,这里不做过多介绍,能够看图:
信号驱动的IO模型
异步:
告知内核启动某个操作。并让内核在整个操作完毕后(包含将数据从内核空间复制到自己的缓冲区)通知。异步IO的主要特点是完毕操作后主动通知。如图:
异步IO模型

好,上面的可能有点抽象。以下用通俗点的语言来总结一下堵塞,非堵塞。同步,异步
堵塞,非堵塞:进程/线程要訪问的数据是否就绪,进程/线程是否须要等待;
同步,异步:訪问数据的方式,同步须要主动读写数据。在读写数据的过程中还是会堵塞;
异步仅仅须要I/O操作完毕的通知。并不主动读写数据,由操作系统内核完毕数据的读写。


再举个网上流传的,非常easy理解的样例:
老张爱喝茶。废话不说。煮开水。
出场人物:老张。水壶两把(普通水壶。简称水壶;会响的水壶。简称响水壶)。
1 老张把水壶放到火上,立等水开。(同步堵塞)老张认为自己有点傻
2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非堵塞)
老张还是认为自己有点傻,于是变高端了。买了把会响笛的那种水壶。

水开之后,能大声发出嘀~~~~的噪音。
3 老张把响水壶放到火上。立等水开。(异步堵塞)老张认为这样傻等意义不大
4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非堵塞)老张认为自己聪明了。
所谓同步异步。仅仅是对于水壶而言。普通水壶,同步;响水壶。异步。尽管都能干活,但响水壶能够在自己完工之后,提示老张水开了。

这是普通水壶所不能及的。


同步仅仅能让调用者去轮询自己(情况2中),造成老张效率的低下。

所谓堵塞非堵塞,仅仅对于老张而言。

立等的老张。
堵塞。看电视的老张。非堵塞。

情况1和情况3中老张就是堵塞的。媳妇喊他都不知道。
尽管3中响水壶是异步的。可对于立等的老张没有太大的意义。所以一般异步是配合非堵塞使用的,这样才干发挥异步的效用。