最近越来越认为,在讲解技术相关问题时,大白话固然很重要,通俗易懂,让人有想读下去的欲望。但几乎所有的事,都有两面性,在看到其带来好处时,不妨想想是否也引入了不好的地方。

例如在博客中,过于大白话的语言的确会让你阅读起来更加顺畅,也更容易理解。但这都是其他人理解,已经咀嚼过了的,人家是已经完全理解了,你从这些信息中大概可能会观察不到全貌。所以,适当的白话是很好的,但这个度得控制一下。

接下来切入正文。

相信大家经常看到这个问题:

BIO、NIO 和 AIO 有什么区别?

看到这个问题,可能你脑海中就会浮现以下这些字眼。比如 BIO 就是如果从内核获取数据会一直阻塞,直到数据准备完毕返回。再比如 NIO,内核在数据没有准备好时不会阻塞住,调用程序会一直询问内核数据是否 Ready。

虽然是正确的,字数也很少。但是这样一来,你看这些概念就不是理解,而是背诵了。其实 BIO 和 NIO 这类的名词还有一个共同的名字叫——IO模型,总共有:


IO 模型

由于信号驱动 IO 在实际中不常用,我们主要讲以下四种模型:

  1. 同步阻塞
  2. 同步非阻塞
  3. IO 多路复用
  4. 异步 IO

这里还是通过例子来理解这 4 种 IO 模型:

假设此时客户端正在发送一些数据到服务器,并且数据已经通过客户端的协议栈、网卡,陆陆续续的到达了服务器这边的内核态 Buffer 中了。

不清楚用户态和内核态区别的可以看看《简单聊聊用户态和内核态的区别》

对数据在网络中是如何传输的细节感兴趣的,可以去看看我之前写的文章 《请求数据包从发送到接收,都经历了什么?》

同步阻塞 BIO

我们需要知道,内核在处理数据的时候其实是分成了两个阶段:

  • 数据准备
  • 数据复制

在网络 IO 中,数据准备可能是客户端还有部分数据还没有发送、或者正在发送的途中,当前内核 Buffer 中的数据并不完整;而数据复制则是将内核态 Buffer 中的数据复制到用户态的 Buffer 中去。

调用线程发起 read 系统调用时,如果此时内核数据还没有 Ready,调用线程会阻塞住,等待内核 Buffer 的数据。内核数据准备就绪之后,会将内核态 Buffer 的数据复制到用户态 Buffer 中,这个过程中调用线程仍然是阻塞的,直到数据复制完成,整个流程用图来表示就张这样:

同步非阻塞 NIO

相信大家知道 Java 中有个包叫 nio,但那跟我们现在正在讨论的 NIO 不是同一个概念。

现在正在讨论的是 Non-Blocking IO,代表同步非阻塞,是一种基础的 IO 模型。而 nio 包则是 New IO,里面的 IO 模型实际上是 IO多路复用,大家不要搞混淆了。

有了 BIO 的基础,这次我们直接来看图:


NIO

还是分为两个阶段来讨论。

数据准备阶段。此时用户线程发起 read 系统调用,此时内核会立即返回一个错误,告诉用户态数据还没有 Read,然后用户线程不停地发起请求,询问内核当前数据的状态。

数据复制阶段。此时用户线程还在不断的发起请求,但是当数据 Ready 之后,用户线程就会陷入阻塞直到数据从内核态复制到用户态

稍微总结一下,如果内核态的数据没有 Ready,用户线程不会阻塞;但是如果内核态数据 Ready 了,即使当前的 IO 模型是同步非阻塞,用户线程仍然会进入阻塞状态,直到数据复制完成,并不是绝对的非阻塞。

那 NIO 的好处是啥呢?显而易见,实时性好,内核态数据没有 Ready 会立即返回。但是事情的两面性就来了,频繁的轮询内核,会占用大量的 CPU 资源,降低效率

IO 多路复用

IO 多路复用实际上就解决了 NIO 中的频繁轮询 CPU 的问题。在之前的 BIO 和 NIO 中只涉及到一种系统调用——read,在 IO 多路复用中要引入新的系统调用——select

read 用于读取内核态 Buffer 中的数据,而 select 你可以理解成 MySQL 中的同名关键字,用于查询 IO 的就绪状态。

在 NIO 中,内核态数据没有 Ready 会导致用户线程不停的轮询,从而拉满 CPU。而在 IO 多路复用中调用了 select 之后,只要数据没有准备好,用户线程就会阻塞住,避免了频繁的轮询当前的 IO 状态,用图来表示的话是这样:


IO 多路复用

异步 AIO

该模型的实现就如其名,是异步的。用户线程发起 read 系统调用之后,无论内核 Buffer 数据是否 Ready,都不会阻塞,而是立即返回。

内核在收到请求之后,会开始准备数据,准备好了&复制完成之后会由内核发送一个 Signal 给用户线程,或者回调用户线程注册的接口进行通知。用户线程收到通知之后就可以去读取用户态 Buffer 的数据了。


AIO

由于这种实现方式,异步 IO 有时也被叫做信号驱动 IO。相信你也发现了,这种方式最重要的是需要 OS 的支持,如果 OS 不支持就直接完蛋。

Linux 系统在 2.6 版本的时候才引入了异步IO,不过那个时候并不算真正的异步 IO,因为内核并不支持,底层其实是通过 IO 多路复用实现的。而到了 Linux 5.1 时,才通过 io_uring 实现了真 AIO。

欢迎微信搜索关注【SH的全栈笔记】,如果你觉得这篇文章对你有帮助,还麻烦点个赞关个注分个享留个言

图解四种 IO 模型的更多相关文章

  1. 四种IO模型

    四种 IO 模型:       首先需要明确,IO发生在 用户进程 与 操作系统 之间.可以是客户端IO也可以是服务器端IO. 阻塞IO(blocking IO):     在linux中,默认情况下 ...

  2. Linux五种IO模型(同步 阻塞概念)

    Linux五种IO模型 同步和异步 这两个概念与消息的通知机制有关. 同步 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.比如,调用readfrom系统调用时,必须等待IO操 ...

  3. 漫谈五种IO模型

    阅读目录 1 基础知识回顾 2 I/O模式 3 事件驱动编程模型 网络编程里常听到阻塞IO.非阻塞IO.同步IO.异步IO等概念,搞清楚这些概念之前,还得先回顾一些基础的概念. 1 基础知识回顾 注意 ...

  4. Linux 网络编程的5种IO模型:多路复用(select/poll/epoll)

    Linux 网络编程的5种IO模型:多路复用(select/poll/epoll) 背景 我们在上一讲 Linux 网络编程的5种IO模型:阻塞IO与非阻塞IO中,对于其中的 阻塞/非阻塞IO 进行了 ...

  5. Linux 网络编程的5种IO模型:异步IO模型

    Linux 网络编程的5种IO模型:异步IO模型 资料已经整理好,但是还有未竟之业:复习多路复用epoll 阅读例程, 异步IO 函数实现 背景 上一讲< Linux 网络编程的5种IO模型:信 ...

  6. 正确理解这四个重要且容易混乱的知识点:异步,同步,阻塞,非阻塞,5种IO模型

    本文讨论的背景是Linux环境下的network IO,同步IO和异步IO,阻塞IO和非阻塞IO分别是什么 概念说明 在进行解释之前,首先要说明几个概念: - 用户空间和内核空间 - 进程切换 - 进 ...

  7. (四)五种IO模型

    基本概念 我们之前编写的套接字程序都是阻塞式的,其实这也是默认的形式.现在我们需要明确一些概念: 用户空间和内核空间 首先要明确,用户启动的应用程序在系统中以一个进程的形式存在,而无论对于网络数据还是 ...

  8. Atitit  五种IO模型attilax总结 blocking和non-blocking synchronous IO和asynchronous I

    Atitit  五种IO模型attilax总结 blocking和non-blocking synchronous IO和asynchronous I   1.1. .3 进程的阻塞1 1.2. 网络 ...

  9. 聊聊 Linux 中的五种 IO 模型

    本文转载自: http://mp.weixin.qq.com/s?__biz=MzAxODI5ODMwOA==&mid=2666538919&idx=1&sn=6013c451 ...

随机推荐

  1. 转 Android Lifecycle、ViewModel和LiveData

    转自:https://www.jianshu.com/p/982545e01d0a 1.概述 在I / O '17的时候,其中一个重要的主题是Architecture Components.这是一个官 ...

  2. Linux学习 - 输入输出重定向,管道符,通配符

    一.键盘输入读取read read [选项] [变量名] -p [显示信息] 在等待read输入时,输出提示信息 -t [秒数] 指定read输入等待时间 -n [字符数] 指定read只接收n个字符 ...

  3. rust常用技巧

    允许未使用的方法,写在文件开头,可过滤过掉该项提示 #![allow(unused)]

  4. 【Python】【Module】os

    os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("dirname") 改变当前脚本工作目录:相当于shell下cd os.curd ...

  5. 【Java基础】Java中如何获取一个类中泛型的实际类型

    泛型的术语 <>: 念做typeof List<E>: E称为类型参数变量 ArrayList<Integer>: Integer称为实际类型参数 ArrayLis ...

  6. 【力扣】有序矩阵中第K小的元素

    给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素.请注意,它是排序后的第 k 小元素,而不是第 k 个不同的元素. 示例: matrix = [ [ 1, 5, ...

  7. 超!超!超简单,Linux安装Docker

    1.安装依赖yum-util 提供yum-config-manager功能,另外两个是devicemapper驱动依赖的 sudo yum install -y yum-utils device-ma ...

  8. [BUUCTF]PWN——[V&N2020 公开赛]easyTHeap

    [V&N2020 公开赛]easyTHeap 附件 步骤: 例行检查,64位程序,保护全开 本地试运行一下,看看大概的情况,常见的堆的菜单 64位ida载入,main函数 最多只能申请7个ch ...

  9. MISC常见题型整理

    题目打包在这里 提取码:fhkb MISC 流量包分析 流量包_1 流量包_2 流量包_3 图片隐写 图片隐写_1 图片隐写_2 图片隐写_3 图片隐写_4 图片隐写_5 图片隐写_6 音频隐写 音频 ...

  10. IDEA版本2020.1全局MAVEN配置

    我们用IDEA打开项目有时候要每次配置maven地址,这样就很麻烦 我们可以直接设置全局的 这样所有新打开的项目都能使用