zoukankan      html  css  js  c++  java
  • 同步?异步?阻塞?非阻塞?多路复用?

    写在前面

    无论是在你看各种中间件在IO方面的优化,还是在各种面试题中,IO模型都是占了举足轻重的地位。然而,什么同步阻塞同步非阻塞异步阻塞异步非阻塞等等,都会让你晕头转向。当你Google这些区别,你总能查到各种各样的生活中的例子来帮助你理解,那一刻你懂了,但是回过头来好像又觉得差了点意思。

    在这一篇文章中,我们以 《UNIX网络编程:卷一》第六章 的说法,来看看各种IO模型的本质是什么。

    在阅读这一篇文章之前, 你需要了解的前置知识点有:

    • 了解Socket与文件描述符的关系
    • 了解进程的五状态模型
    • 了解用户态与内核态的区别

    为了文章的连贯性,本文就不对这些前置知识点做过多的解释了。如果在阅读过程中你产生了一些疑问,可以先看完文章,然后再去详细的了解这些知识点。

    1.IO的流程

    在区分各种IO模型的区别之前,我们需要先了解一个IO,在操作系统的层面究竟发生了哪些事情。注意,我们说的IO模型对IO的优化,都是基于读IO。

    假设我们需要等待Socket的数据,也就是说当前是一个读操作的IO,那么在操作系统层面需要分为两步:

    • 等待数据被写入Socket的缓冲区中
    • 将Socket缓冲区中的数据拷贝到应用程序中

    我们可以很容易的发现,第一步是由对方决定的,对方决定了什么时候把数据发送过来。第二步是由操作系统决定的,操作系统需要陷入内核态拷贝数据。

    而我们的同步异步、阻塞非阻塞也是从这里出发的。

    这里先提一下,阻塞和非阻塞指的是第一个阶段,进程等待缓冲区被写入数据的方式;同步和异步指的是第二个阶段,进程需不需要等待操作系统把数据从缓冲区拷贝到应用中。

    2.五种IO模型

    2.1 阻塞

    同步阻塞的IO模型中,在第一阶段“等待数据被写入Socket的缓冲区中”,操作系统会把当前的进程设置为阻塞状态,直到缓冲区被写入数据这个进程才被唤醒。

    这也就造成了一个问题,当操作系统把这个进程置为阻塞态的时候,这个进程就什么事都做不了了。

    2.2 非阻塞

    为了解决“同步阻塞”进程可能无期限阻塞的情况,于是产生了“同步非阻塞”

    在这种IO模型中,如果发现这个Socket里面没有准备好的数据就返回一个错误,而不是把这个进程设置为阻塞状态。

    也就是说我们可以轮询这个Socket查看有无准备好的数据。

    2.3 多路复用

    假设此时我们的服务器需要管理很多的IO请求,如果给每一个IO都分配一个进程/线程,自旋的等待有无数据到来,无疑是很浪费资源的。

    如果我们用一个进程,轮询所有的IO请求,又会使IO的响应变得很慢。

    因此,产生了多路复用IO。

    多路复用IO就是用一条线程,同时监听多个IO请求,并且在有IO请求产生的时候返回。

    注意,虽然我们的IO多路复用也会阻塞,但是这里的阻塞是应用层面的,也就是说在多路复用的方法上进行阻塞,而不是在操作系统层面去阻塞。

    2.4 信号驱动

    在以上的IO模型中,都是需要应用自己去“询问”Socket是否已经准备好了数据,而信号驱动就是由Socket“主动”告诉你有没有准备好数据。

    这么做的好处在于第一阶段程序不需要任何的等待与轮询,只需要在收到了信号通知之后去处理这个IO即可。

    2.5 异步IO

    在以上的四种IO操作中,无论第一个阶段是如何进行优化的,在第二阶段都需要陷入内核态来拷贝数据。

    异步IO就是为了解决这个问题。

    异步IO在解决了阻塞这个问题后,同样把陷入内核态来拷贝数据的时间也节省了。

    这样,当程序需要进行IO的时候,只需要发出IO的请求,然后就可以继续执行后面的代码,直到IO完成操作系统会通知程序。

    同样的,异步IO是非阻塞的。

    3.总结

    到了这里,我想你应该可以分别各种IO模型了,无论是同步阻塞,还是同步非阻塞...不会被这些模型搞的一脸懵逼,记了忘忘了记。

    只需要记住阻塞跟非阻塞,是在等待数据这个阶段的;同步跟非同步,指的是需不需要等待CPU进行数据的拷贝

    写在最后

    首先,谢谢你能看到这里,新年快乐!

    我拖更了好久好久了,前段时间因为自己没能调整好时间,在这里跟大家说声抱歉。

    在过去的半年,我们比较粗粒度的学了一遍作为一个开发应该掌握的各种基本技能,基本都是各种《XXX入门》,属于技能广度上的扫盲。

    在接下来的半年中,我的小目标就是能够在技术深度上有一定的进步,也同样希望我能够把复杂的东西讲得更清晰明了。

    (这应该是新年的第一个flag了hhh)

    我初步的计划是先把gRPC研究的稍微深入一些,而不是停留在如何调用各种API。也希望可以站在一个更高一点的层面来分析gRPC,了解gRPC为什么要这么设计,而不是逐行加上注释来解释gRPC是怎么实现的

    这是一个新的尝试,会有点难,不过,我们一起努力吧!

    在这个过程中,有任何问题你都可以在公众号搜索“红鸡菌”找到我,我也同样很希望得到你的反馈,包括但不限于“我哪里写的不明白”,“你希望看到什么样的内容”等。

    再次谢谢你能看到这里!

    2021一起加油~

  • 相关阅读:
    Confd+Consul 动态生成配置文件
    Flask实例化的参数 及 对app的配置
    Flask 中的路由系统
    Flask 中内置的 Session
    Flask中的模板语言jinja2
    docker
    windows上使用git
    redis的源码编译安装+发布订阅+RDB持久化
    mariadb安装和mysql主从同步
    nginx负载均衡
  • 原文地址:https://www.cnblogs.com/hongjijun/p/14222822.html
Copyright © 2011-2022 走看看