zoukankan      html  css  js  c++  java
  • IO模型

        关于网络IO的模型涉及到的阻塞、非阻塞、同步、异步真的是让人一头雾水啊。

        首先要了解需求,这个IO模型能够解决什么问题,在我理解就是,当应用程序要使用系统资源,但是这个资源又被占用或者没准备好的时候应该怎么办。比如说你读取个几十兆的文件,不可能瞬间就拿到手,因为用户进程不能直接访问磁盘,所以要通过内核的系统调用读取,这个内核读取的过程就是用户进程等待的过程。等待内核读取后将数据从内核内存复制到进程内存。

    首先要明白的就是应用程序使用系统资源的一个过程,进程无法直接操作IO设备(磁盘),需要通过系统调用请求内核完成操作,主要有两步:
    第一步:请求,应用程序发起系统调用请求资源,内核将磁盘数据获取到内核内存空间。
    第二步:操作,内核获取到数据保存在内核空间,将数据拷贝到应用程序的内存空间的实际操作。

        然后就是阻塞和非阻塞的问题了。很容易跟同步、异步混淆。
    什么是阻塞?就是程序执行流程过来的时候你把它扣押起来,让它哪都不能去,什么也干不了。简单来说就是一个操作能不能立刻得到回应,可以立刻得到返回就是非阻塞,只要是需要等待,那就是阻塞。而阻塞会存在于以上两个步骤,即请求的时候可能被阻塞,实际复制数据的时候也可能被阻塞。

        那么同步异步的问题呢,有些说同步会把应用程序进程阻塞,异步不会,那同步和阻塞不就一样了么。其实这里按照我的理解就是,同步异步只是对于上述第二个步骤(IO操作)而言的(内核从磁盘中读数据也要等待啊)。

        引用“ 两者的区别就在于synchronous IO做”IO operation”的时候会将process阻塞。按照这个定义有人可能会说,non-blocking IO并没有被block啊。这里有个非常“狡猾”的地方,定义中所指的”IO operation”是指真实的IO操作,就是例子中的recvfrom这个系统调用。non-blocking IO在执行recvfrom这个系统调用的时候,如果kernel的数据没有准备好,这时候不会block进程。但是当kernel中数据准备好的时候,recvfrom会将数据从kernel拷贝到用户内存中,这个时候进程是被block了,在这段时间内进程是被block的。而asynchronous IO则不一样,当进程发起IO操作之后,就直接返回再也不理睬了,直到kernel发送一个信号,告诉进程说IO完成。在这整个过程中,进程完全没有被block。 ”

        我理解同步和异步的区分就在于IO复制的时候是否阻塞,阻塞则为同步。但网络中看到两种说法,IO复用有说是同步的,也有说是异步的。觉得执着于同步异步阻塞非阻塞的名词区分有点死板,关键知道他们各自是如何工作的,有哪些优缺点,解决实际问题的应用就可以了。

    下面两张图片取自open文档--网络I/O模型剖析.pdf

    image

    image

    这里推荐一篇挺好的博文:《IO - 同步,异步,阻塞,非阻塞 (亡羊补牢篇) 》,而各个模型的具体代码可以参阅《几种服务器端IO模型的简单介绍及实现

    那么就简单说说各个模型,图片来源于UNIX网络编程一书

    1、阻塞IO

    image

    2、非阻塞IO:

    clip_image002

    3、IO复用:

    clip_image002

    4、信号驱动IO:

    clip_image004

    对比I/O复用进程在第一阶段不用被阻塞。对比异步IO,信号通知是在复制到内核内存后,后者是复制到进程内存后才通知。

    5、异步IO:
    clip_image006

    根据上述做个简单的举例,就以一个买报纸比喻为数据的读取,但是报纸不是你想买就有的,如果没有(即进程读取数据时,系统并未准备好数据),那应该怎么办。针对上述描述有几种解决方案

    1、阻塞I/O, 一天早上你大老早去找报摊老伯买当天报纸,结果告诉你,你来得太早了想要的报纸还没有(也就是进程想要读取的数据被其他占用了,不能马上得到想要的数据),这个时候你就干脆等着,等老板什么时候拿到报纸再给你,老板给你一张小板凳,你什么事情都不做,坐等拿到报纸后就撤退!这里的阻塞分了两部分,一个是你等老板(IO的请求),另一个是老板也在等送报人按照订购清单派发后清点种类和数量(IO操作,内核从磁盘提取数据),你们两个人都在等,所以两个阶段都是阻塞的。

    2、非阻塞I/O, 这种情况是你老板没有小板凳,而你也不想一直等着,等着的时间浪费了还不如干点其他事情。所以你先离开了,然后每隔一段时间就跑回来看看想要的报纸有没有,这种不是一直等待的就是非阻塞,可是对于老板来说他也要等着从送报员手中收到报纸,这过程对于老板也算是阻塞(你最后一次问老板,老板告诉你报纸已经有了,就相当于数据已经复制到内核的缓冲区),而老板虽然告诉你已经有你要报纸,但是老板最后还需要从一堆报纸中找到给你(这是IO的实际操作了,相当于内核从磁盘读取数据,读取完之后再进行的第二个个步骤,从内核复制到进程内存),老板找的过程你就一直等待,所以虽然是非阻塞,但因为第二个步骤你要等待即为同步

    3、I/O多路复用, 上面第二种方式可以发现有很大缺陷了,你需要不断地去看报纸送到老板手上了没有,一来二去的想想都累,尤其是当你要的多份报纸要在几个地方才能买到(多路复用更适用于多个IO的情景),这样你就需要每个地方反复的去看一遍有没有到货,累成狗了吧。这个时候多路复用的功能就是,你找个人来帮你跑,这个人会定时地将每个报亭的报纸是否有库存记录下来,你要做的事情只需要老老实实待在你和中介的见面点喝茶(阻塞,进程受阻于select调用),一旦发现中介跑回来的就向他询问,当某次得知所有报亭都已经到货,你就可以走一圈买到所有报纸。

    4、信号驱动, 上面虽然不用亲自跑到各个报刊亭了解情况,但是仍然需要一直等待中介代理人给你信息,还是避免不了等待的麻烦,于是你直接让老板在有你想要的报纸的时候主动通知你,你再去拿报纸。这样就好多了,你足不出户就知天下事,该出手时就出手,保证每次都可以在报刊亭的报纸到达时就去取。可是取报纸的时候还是需要等待老板去翻找。

    5、异步I/O, 这个就是高科技了,你去报刊亭问老板有没有你想要的报纸,老板说还没有,然后你就不等了,去干其他事情,你也不需要反复去询问最新情况了,当老板收到了报纸的时候,会给你送货上门,一站式服务啊!你也不用到现场等待老板从茫茫报纸中翻出你要的。异步就是一件事情分开两个独立的步骤去完成,当数据准备好了,你不一定马上去获取,可以先把手头上的其他事情做完再去,所以请求和获取不是同时作为一个整体运作的。同步呢就是把一件事作为一个整体,一个请求过去,必须要等到结果才返回。

    以上几种情况的举例纯属基于个人浅显的理解,如有不当之处欢迎提出共同学习。另外有篇文章写得还是不错的 chinaunix--网络IO模型:同步IO和异步IO,阻塞IO和非阻塞IO

    -------------------------------------------------------------------------------------------------------------------------------

    2018.2.8更新:用偏向程序开发的角度理解IO模型,Linux的I/O模式、事件驱动编程模型

  • 相关阅读:
    webpack常见问题 收藏
    ES6 模块
    ES6 Class 类
    ES6 迭代器
    ES6 函数
    ES6 数组
    ES6 对象
    记一次pda(安卓)环境配置流程
    类型转换
    DOM事件
  • 原文地址:https://www.cnblogs.com/otherside/p/5605471.html
Copyright © 2011-2022 走看看