前言

在上一篇文章中,我们了解了操作系统中内核程序和用户程序之间的区别和联系,还提到了内核空间和用户空间,当我们需要读取一条数据的时候,首先需要发请求告诉内核,我需要什么数据,等内核准备好数据之后再从内核空间拷贝到用户空间 注意加粗的部分,这两个阶段至关重要

对以上的两个过程以及操作系统的IO流程不了解的,请务必左转去看上一篇文章,上篇文章中是学习IO的基础知识,只有把上一篇文章的内容看懂了,对于后续的IO几种模型的学习和理解才会更为深刻,上一篇文章可以说是整个IO中的基石级别的知识。

文章链接 【网络IO系列】 预备知识 操作系统之内核程序和用户程序

IO的五种模型

我们回到正题,从上篇文章我们知道,当我们进行一次IO的时候,是要经过这两个阶段的,分别是

第一阶段 :等待内核准备数据

第二阶段:数据从内核空间拷贝到用户空间

这两个阶段则决定着IO的各种模型的类型,通过这两个阶段,可以将IO模型分成五种,分别是

  • 阻塞式IO(BIO)
  • 非阻塞式IO(NIO)
  • IO多路复用
  • 信号驱动IO
  • 全异步IO(AIO)

阻塞

说到阻塞,在这里想先明确一下,什么是阻塞。从线程或者进程的角度来看,阻塞就是因为当前执行的这个线程,暂时的失去了CPU的执行权,被挂起等待下一次线程的调度或者线程被唤醒。如果是具体到我们的IO的话,则可以理解为,阻塞的时候你需要等待,等到数据准备好或者执行结果返回,才能继续下一步的操作,不然只能一直等待下去。而非阻塞,则是,即便数据没有准备好,或者执行没有完成,你也可以去做其他的事情。

  举个例子,比如说你出门排队买东西,假如你没有带手机,那你只能老老实实排队等轮到你,在这之前,你除了排队这件事之外,其他什么事情都干不了也不允许干,这个时候你就是阻塞的,因为你只能做排队这一件事。那么同样是排队,这一次你带了手机,那你排队的时候,可以边玩手机边排队,这个时候你就是非阻塞的。

接下来让我们对应到上面的两个阶段,如果等待内核准备数据的时候,执行线程可以去做其他的事,那么在第一阶段就是非阻塞的,否则就是阻塞的。如果在数据从内核空间拷贝到用户空间阶段,执行线程可以去干其他的事,那么第二阶段就是非阻塞的,否则就是阻塞的。

阻塞式IO(BIO)

阻塞式IO,是在两个阶段都阻塞的一种IO模型,用户发起IO请求,在等待数据和数据拷贝阶段,都会被阻塞,只有这两个阶段都完成了,才能去做下一阶段的事情。

  就像是你没带手机去吃饭,你跟老板说要吃鱼香肉丝,然后要等老板做好菜(准备数据),然后从厨房把菜端到你面前(数据拷贝),这两个阶段你都只能等着,什么事都干不了。

由于BIO阻塞时间长,因此相对性能就会较低,所以现在用的相对也比较少了。

非阻塞IO(NIO)

非阻塞IO,可以看作是半阻塞IO,因为他在第一阶段数据准备阶段不阻塞,第二阶段数据拷贝阶段阻塞,当用户发出IO请求的时候,会有一个线程去询问内核数据准备好了吗,一直问一直问,在这期间,用户主进程可以去干其他的事,等数据准备好了,到了第二阶段,这个时候,用户线程就要执行拷贝数据,这个时候是阻塞的。这种方式的缺点就是反复的轮训去询问内核数据好了没,是很消耗CPU资源的。

  就像是你带手机去吃饭,你点好菜之后,你可以一直问老板,我的菜好了没,老板说没有,问完之后就可以继续玩手机继续等,继续问。等到有一次你问,老板我的菜好了吗,老板说好了,你自己过来端一下。(注意,问菜好没有的,得是你自己问,这家NIO店的老板比较高冷,菜好了你不问他是不会主动告诉你的,这就是NIO的特点,数据准备就绪是用户线程主动发出的询问),这个时候菜好了,你要自己去端(数据拷贝),这个端菜的阶段,你期间啥都干不了,也不能玩手机,所以NIO的第二个阶段是阻塞的。

说到这里我们可以看出BIO和NIO之间的区别了,一个是傻等老板做菜给你,期间你什么都干不了,一个是自己主动询问老板,菜好了没,期间你可以玩手机,或者干其他的,相比BIO,NIO的效率就高了很多。当然,你可能会问了,为啥菜好没好,还得我自己主动去问,这也太不人性化了,确实,这个问题我们想得到,计算机的科学界大师们自然也想得到,于是为了解决这个问题,于是出现了信号驱动IO和IO多路复用。

IO多路复用

通过我们上面对NIO的了解,我们可以知道,NIO多少存在着一些不够好的地方,因为反复的轮训也是很消耗cpu资源的。如果饭店的人少还好说,但是如果饭店人多起来了,比如说来了几百个人,那每个人时不时就要发起一次询问请求,那老板管不过来啊,cpu占用率也会非常高。于是,IO多路复用就出现了,IO多路复用可以说是目前用的最多的一个IO模型,在不同的操作系统内核,也有不同的实现方式,在这篇文章中,我们IO多路复用的大概思想,至于详细介绍,后面会用一篇文章来详细的介绍IO多路复用

IO多路复用,实际上,是通过IO请求都通过一个selector来管理,用户进程的IO请求就不直接发给内核处理程序了,而是注册到这个selector上面,由selector来告诉内核需要哪些数据,然后定时的去查询内核程序,我这个selector上需要的数据,有哪些准备好了,然后再由selector告诉那些准备好了的用户线程,让该用户线程去拷贝数据。在非阻塞IO中,不断地询问状态时通过用户线程去进行的,而在IO多路复用中,询问每个状态是内核在进行的,在IO请求非常多的时候,这个效率要比用户线程轮询要高的多。

  就像是你带手机去饭店吃饭,现在这家饭店的老板由于生意越来越好,人越来越多,他有点管理不过来了,于是他请了几个服务员(selector)协助管理,然后现在饭店客户的点餐都是告诉服务员,我需要什么菜,然后服务员把xx桌客户的菜,记在自己的单子上。然后服务员告诉厨房他这个单子上需要哪些菜,让厨房去做。。服务员定时问厨房看看有哪些菜已经准备好了,然后告知15号桌和89号桌客人你们的菜已经好了,请来前台端一下,然后你就去前台端菜,端菜的阶段是阻塞的。

来比较一下IO多路复用和NIO,我们可以发现,当IO请求多的时候,IO多路复用效率无疑是更高的。因为对于用户线程来说,你点完菜就可以一直玩手机了,不用因为一直问老板而分心分神,耽误你打王者,因为菜好了,服务员会通知你

信号驱动IO

通过我们上面两种IO模型的了解,我们可以知道,不管是NIO还是IO多路复用,本质上还是轮询,只不过NIO是用户线程轮询,IO多路复用是委托给selector让他来轮询,那有没有什么办法能让内核主动通知数据好了没。所以,信号驱动IO出现了。信号信号,顾名思义,就是会有一个信号通知你数据已经准备好了,不用你一直去问。信号驱动IO,用户线程发出一个请求告诉内核我需要什么数据,数据准备好了你告诉我一声,然后内核就会记录下这个请求,内核准备好了之后会主动通知用户线程去执行拷贝数据,数据拷贝阶段是阻塞的,需要等数据拷贝完才能做其他的事。

   就像是你带手机去吃饭,你点好菜之后,你就只管玩手机了,啥也不用管,就等老板通知你,期间你想干啥就干啥,等到菜准备好了,老板会大声说(内核主动通知用户进程),xxx你的鱼香肉丝已经准备好了,请过来前台端一下,这个时候你要自己去端(数据拷贝),这个端菜的阶段,你期间啥都干不了,也不能玩手机,所以信号驱动IO的第二个阶段也是阻塞的。

我们对比信号驱动IO和NIO,可以发现最重要的区别就是NIO是用户主动询问内核数据好了吗,而信号驱动IO是内核主动通知用户数据已经好了,这就改善了上面说的NIO的问题。

全异步IO

全异步IO是最理想的一种IO模型,所谓全异步IO就是,用户进程发起了一个IO请求,接下来可以干其他的事了,不需要等内核准备好,也不需要执行数据拷贝,数据异步拷贝到用户空间之后,用户进程直接拿来用就行了,这两个阶段都是由内核自动完成。完全不用用户线程操心这些事。

前面四种IO模型实际上都属于同步IO,只有最后一种才是是真正的异步IO,因为不管是是IO多路复用还是信号驱动,IO操作的第2个阶段都会让用户线程阻塞,也就是内核进行数据拷贝的过程都会让用户线程阻塞。

  举个例子就像是,你去饭店吃饭,点好餐之后,你就可以玩手机了,饭菜做好之后,服务员会把饭菜端到你的面前,你也不需要自己去端,你需要点餐和吃饭就行了,其他的你都不用管。简单来说,就是发出请求之后,只需要等待数据完成直接使用,等待期间,你可以做其他的事。整个过程完全的异步,体验最好。

全异步IO虽然非常牛逼,但是现在还不是很成熟,支持全异步IO的操作系统和框架也还不是很多,所以用的也不是很多。我们只需要了解一下就行了

总结

我们这篇文章讲了五种IO模型的思想,并且每种模型我们都通过一个通俗易懂的例子,来描绘其过程。相信你看完之后一定有收获。其中比较重要的两种是NIO和IO多路复用,这是目前来说用的最多的两种,后面的篇幅,会专门的讲这两种模型,尤其是IO多路复用,在不同的OS上,又有select,poll,和epoll方式。等下一篇文章,我们将会细讲。

【网络IO系列】IO的五种模型,BIO、NIO、AIO、IO多路复用、 信号驱动IO的更多相关文章

  1. JAVA-IO模型(BIO,NIO,AIO)

    基本概念 阻塞和非阻塞 阻塞是进行读写时, 如果当时没有东西可读,或者暂时不可写, 程序就进入等待, 直到有东西可读或者可写为止 非阻塞是如果没有东西可读, 或者不可写, 读写函数马上返回, 而不会等 ...

  2. 温故知新-java的I/O模型-BIO&NIO&AIO

    文章目录 摘要 传统的BIO编程 伪异步I/O编程 NIO编程 AIO编程 几种IO模型的对比 netty 参考 你的鼓励也是我创作的动力 Posted by 微博@Yangsc_o 原创文章,版权声 ...

  3. IO 的五种模型是什么

    目录 前言 用户空间和内核空间 IO 五种模型 阻塞型 IO 非阻塞 IO IO 多路复用 信号驱动 IO 异步 IO 总结 阻塞和非阻塞 同步与异步 前言 我们经常看到阻塞/非阻塞,同步/异步这两组 ...

  4. 2. 彤哥说netty系列之IO的五种模型

    你好,我是彤哥,本篇是netty系列的第二篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 本文将介绍linux中的五种IO模型,同时也会介绍阻塞/非阻塞与同步/异步的区别. ...

  5. IO的五种模型

    为了区分IO的五种模型,下面先来看看同步与异步.阻塞与非阻塞的概念差别. 同步:所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.按照这个定义,其实绝大多数函数都是同步调用(例如 ...

  6. Linux 网络编程的5种IO模型:信号驱动IO模型

    Linux 网络编程的5种IO模型:信号驱动IO模型 背景 上一讲 Linux 网络编程的5种IO模型:多路复用(select/poll/epoll) 我们讲解了多路复用等方面的知识,以及有关例程. ...

  7. I/O模型系列之三:IO通信模型BIO NIO AIO

    一.传统的BIO 网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发起连接请 ...

  8. 图解I/O的五种模型

    1.1 五种I/O模型 1)阻塞I/O 2)非阻塞I/O 3)I/O复用 4)事件(信号)驱动I/O 5)异步I/O 1.2 为什么要发起系统调用? 因为进程想要获取磁盘中的数据,而能和磁盘打交道的只 ...

  9. linux第7天 I/O的五种模型, select

    服务器端避免僵尸进程的方法: 1)通过忽略SIGCHLD信号,解决僵尸进程 signal(SIGCHLD, SIG_IGN) 2)通过wait方法,解决僵尸进程 signal(SIGCHLD, han ...

随机推荐

  1. python报错“AttributeError: 'set' object has no attribute 'items'“

    作为才开始学爬虫的萌新,遇到了一个这样的错,很懵逼 后面到网络到处查看大佬的解决方法,才发现headers的请求头部信息有错误,headers是一个字典,不是字符串,所以报错了 原代码 headers ...

  2. 论文阅读:MDNet: Learning Multi-Domain Convolutional Neural Networks for Visual Tracking

    前言 CVPR2016 来自Korea的POSTECH这个团队   大部分算法(例如HCF, DeepLMCF)只是用在大量数据上训练好的(pretrain)的一些网络如VGG作为特征提取器,这些做法 ...

  3. PyTorch 自动微分示例

    PyTorch 自动微分示例 autograd 包是 PyTorch 中所有神经网络的核心.首先简要地介绍,然后训练第一个神经网络.autograd 软件包为 Tensors 上的所有算子提供自动微分 ...

  4. CUDA运行时 Runtime(一)

    CUDA运行时 Runtime(一)             一. 概述 运行时在cudart库中实现,该库通过静态方式链接到应用程序库cudart.lib和libcudart.a,或动态通过cuda ...

  5. TensorRT宏碁自建云(BYOC, BuildYourOwnCloud)上集成

    TensorRT宏碁自建云(BYOC, BuildYourOwnCloud)上集成 这个PR增加了对分区.编译和运行TensorRT BYOC目标的支持. Building 有两个新的cmake标志: ...

  6. FFmpeg扩展开发

    FFmpeg扩展开发 对FFmpeg RTMP/FLV部分做了扩展,用于支持H.265. 针对<video_file_format_spec_v10_1> VIDEODATA部分扩展如下: ...

  7. 计算机网络-ip分类

    本网络--网络号全是0(0000 0000)的IP地址是保留地址,意思是"本网络". 环回地址--网络号是127(0111 1111)的IP地址也是保留地址,作为本地环回软件测试. ...

  8. java后端知识点梳理——java集合

    集合概览 Java中的集合,从上层接口上看分为了两类,Map和Collection.Map是和Collection并列的集合上层接口,没有继承关系. Java中的常见集合可以概括如下. Map接口和C ...

  9. 代码实测:给redis中的key取一个正确的名字多么重要

    redis对写入的key长度有限制吗? 太长的key对性能有影响吗? key越长对性能影响越大? 如何评估键长度对性能的影响? talk is cheap, show me the code! 今天我 ...

  10. 【模拟8.03】数颜色(vector//主席树)

    才知道vector在插入值后是可以直接修改的... 那就很简单了 用vector的lowerbound这样的二分操作,提前储存每个颜色的位置 发现交换相对位置不变 关于vector的lowerboun ...