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

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

接下来切入正文。

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

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. mysql之对象创建

    1 --创建表空间 2 create tablespace tablespace_name 3 innodb and ndb: 4 add datafile 'file_name' 5 innodb ...

  2. Javascript 数组对象常用的API

    常用的JS数组对象API ES5及以前的Api ECMAScript5为数组定义了5个迭代方法,每个方法接收两个参数, 一个是每项运行的函数,一个是运行该函数的作用域对象(可选项),传入这些方法的函数 ...

  3. maven项目install时忽略执行test

    1.在项目所在文件夹根目录使用maven命令打包时: <!-- 不执行单元测试,也不编译测试类 --> mvn install -Dmaven.test.skip=true 或 <! ...

  4. mysq中char,varchar,text的区别

    mysql5.0.3以后,n都表示字符数(varchar(n)) 检索效率 char > varchar > text 当varchar长度超过255之后,跟text一致,但是设置varc ...

  5. Apache Hudi 与 Hive 集成手册

    1. Hudi表对应的Hive外部表介绍 Hudi源表对应一份HDFS数据,可以通过Spark,Flink 组件或者Hudi客户端将Hudi表的数据映射为Hive外部表,基于该外部表, Hive可以方 ...

  6. Mysql资料 锁机制

    目录 一.简介 二.类型 三.操作 四.死锁 第一种情况 第二种情况 第三种情况 一.简介 数据库和操作系统一样,是一个多用户使用的共享资源.当多个用户并发地存取数据 时,在数据库中就会产生多个事务同 ...

  7. Python3元组的简介和遍历

    一.Python3元组简介 1.1.如何创建一个元组 ''' Python的元组与列表类似,不同之处在于元组的元素不能修改. 元组使用小括号(),列表使用方括号[]. 元组创建很简单,只需要在括号中添 ...

  8. iphone4 与iphone5适配

    转:http://www.cnblogs.com/ygm900/archive/2013/05/24/3098015.html iphone4/4s 应用程序升级适配 iphone5 的方法有很多,而 ...

  9. 华为HMS Core图形引擎服务携手三七游戏打造移动端实时DDGI技术

    在2021年HDC大会的主题演讲中提到,华为HMS Core图形引擎服务(Scene Kit)正协同三七游戏一起打造实时DDGI(动态漫反射全局光照:Dynamic Diffuse Global Il ...

  10. Go - 如何编写 ProtoBuf 插件(二)?

    目录 前言 定义插件 使用插件 获取自定义选项 小结 推荐阅读 前言 上篇文章<Go - 如何编写 ProtoBuf 插件 (一) >,分享了使用 proto3 的 自定义选项 可以实现插 ...