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


    最近在招聘中,聊到基础的网络编程的时候,发现不少人对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的主要特点是完成操作后主动通知。如图: 

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

  • 相关阅读:
    docker学习(3) 容器的启动过程
    docker学习(2) mac中docker-machine使用vmware fusion以及配置国内镜像加速
    docker学习(1) 安装
    maven/gradle 打包后自动上传到nexus仓库
    bash/shell编程学习(3)
    jenkins 入门教程(下)
    jenkins 入门教程(中)
    jenkins 入门教程(上)
    bash/shell编程学习(2)
    ssh远程执行目标机器上的命令
  • 原文地址:https://www.cnblogs.com/dinglang/p/5102200.html
Copyright © 2020-2023  润新知