一文搞懂I/O模型
基础知识
内核
操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。
内核空间&用户空间
为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操心系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间。针对linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空间,而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为用户空间。
缓存IO
文件和流
不管socket,还是FIFO、管道、终端,对我们来说,一切都是文件,一切都是二进制流。在信息交换的过程中,我们都是对这些流进行数据的收发操作,简称为I/O操作(input and output),,往流中读出数据,系统调用read,写入数据,系统调用write。
文件描述符
计算机里有这么多的流,我怎么知道要操作哪个流呢?对,就是文件描述符,即通常所说的fd,一个fd就是一个整数,所以,对这个整数的操作,就是对这个文件(流)的操作。我们创建一个socket,通过系统调用会返回一个文件描述符,那么剩下对socket的操作就会转化为对这个描述符的操作。
思想:这又是一种分层和抽象的思想
。
I/O模型
同步阻塞 I/O(blocking IO)
- 当用户进程调用了recvfrom等阻塞方法时,内核进入IO的第1个阶段:准备数据(内核需要等待足够的数据再拷贝)这个过程需要等待,用户进程会被阻塞,等内核将数据准备好,然后拷贝到用户地址空间,内核返回结果,用户进程才从阻塞态进入就绪态
- Linux中默认情况下所有的socket都是阻塞的
同步阻塞IO模型是最简单的IO模型,用户线程在内核进行IO操作时被阻塞。如图1所示,用户线程通过系统调用read发起IO读操作,由用户空间转到内核空间。内核等到数据包到达后,然后将接收的数据拷贝到用户空间,完成read操作。
即用户需要等待read将socket中的数据读取到buffer后,才继续处理接收的数据。整个IO请求的过程中,用户线程是被阻塞的,这导致用户在发起IO请求时,不能做任何事情,对CPU的资源利用率不够。
同步非阻塞 I/O(blocking IO)
当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。
用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作
一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回
非阻塞IO模式下用户进程需要不断地询问内核的数据准备好了没有
同步非阻塞IO是在同步阻塞IO的基础上,将socket设置为NONBLOCK。这样做用户线程可以在发起IO请求后可以立即返回。如图2所示,由于socket是非阻塞的方式,因此用户线程发起IO请求时立即返回。但并未读取到任何数据,用户线程需要不断地发起IO请求,直到数据到达后,才真正读取到数据,继续执行。
即用户需要不断地调用read,尝试读取socket中的数据,直到读取成功后,才继续处理接收的数据。整个IO请求的过程中,虽然用户线程每次发起IO请求后可以立即返回,但是为了等到数据,仍需要不断地轮询、重复请求,消耗了大量的CPU的资源。一般很少直接使用这种模型,而是在其他IO模型中使用非阻塞IO这一特性。
I/O 多路复用( IO multiplexing)
通过一种机制,一个进程可以监视多个文件描述符(套接字描述符)一旦某个文件描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作(这样就不需要每个用户进程不断的询问内核数据准备好了没)
常用的IO多路复用方式有select、poll和epoll
select不会告诉用户进程哪个文件描述符可以用,而且数量限制1024。
poll和select类似,只不过不限制数量啦。
epoll方式不限制数量的前提下,还会通知用户进程哪些文件描述符里有数据,直接返回。是一种那空间换时间的方式。
IO多路复用模型是建立在内核提供的多路分离函数select基础之上的,使用select函数可以避免同步非阻塞IO模型中轮询等待的问题。
如图3所示,用户首先将需要进行IO操作的socket添加到select中,然后阻塞等待select系统调用返回。当数据到达时,socket被激活,select函数返回。用户线程正式发起read请求,读取数据并继续执行。
从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视socket,以及调用select函数的额外操作,效率更差。但是,使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。
IO多路复用模型(Reactor)
然而,使用select函数的优点并不仅限于此。虽然上述方式允许单线程内处理多个IO请求,但是每个IO请求的过程还是阻塞的(在select函数上阻塞),平均时间甚至比同步阻塞IO模型还要长。如果用户线程只注册自己感兴趣的socket或者IO请求,然后去做自己的事情,等到数据到来时再进行处理,则可以提高CPU的利用率。
如图5所示,通过Reactor的方式,可以将用户线程轮询IO操作状态的工作统一交给handle_events事件循环进行处理。用户线程注册事件处理器之后可以继续执行做其他的工作(异步),而Reactor线程负责调用内核的select函数检查socket状态。当有socket被激活时,则通知相应的用户线程(或执行用户线程的回调函数),执行handle_event进行数据读取、处理的工作。由于select函数是阻塞的,因此多路IO复用模型也被称为异步阻塞IO模型。注意,这里的所说的阻塞是指select函数执行时线程被阻塞,而不是指socket。一般在使用IO多路复用模型时,socket都是设置为NONBLOCK的,不过这并不会产生影响,因为用户发起IO请求时,数据已经到达了,用户线程一定不会被阻塞。
IO多路复用是最常使用的IO模型,但是其异步程度还不够“彻底”,因为它使用了会阻塞线程的select系统调用。因此IO多路复用只能称为异步阻塞IO,而非真正的异步IO。
信号驱动 I/O( signal driven IO)
内核文件描述符就绪后,通过信号通知用户进程,用户进程再通过系统调用读取数据。
此方式属于同步IO(实际读取数据到用户进程缓存的工作仍然是由用户进程自己负责的)
异步 I/O(asynchronous IO)
用户进程发起read操作之后,立刻就可以开始去做其它的事。内核收到一个异步IO read之后,会立刻返回,不会阻塞用户进程。
内核会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,内核会给用户进程发送一个signal告诉它read操作完成了
如图7所示,异步IO模型中,用户线程直接使用内核提供的异步IO API发起read请求,且发起后立即返回,继续执行用户线程代码。不过此时用户线程已经将调用的AsynchronousOperation和CompletionHandler注册到内核,然后操作系统开启独立的内核线程去处理IO操作。当read请求的数据到达时,由内核负责读取socket中的数据,并写入用户指定的缓冲区中。最后内核将read的数据和用户线程注册的CompletionHandler分发给内部Proactor,Proactor将IO完成的信息通知给用户线程(一般通过调用用户线程注册的完成事件处理函数),完成异步IO。
相比于IO多路复用模型,异步IO并不十分常用,不少高性能并发服务程序使用IO多路复用模型+多线程任务处理的架构基本可以满足需求。况且目前操作系统对异步IO的支持并非特别完善,更多的是采用IO多路复用模型模拟异步IO的方式(IO事件触发时不直接通知用户线程,而是将数据读写完毕后放到用户指定的缓冲区中)。Java7之后已经支持了异步IO,感兴趣的读者可以尝试使用。
一文搞懂I/O模型的更多相关文章
- (四) 一文搞懂 JMM - 内存模型
4.JMM - 内存模型 1.JMM内存模型 JMM与happen-before 1.可见性问题产生原因 下图为x86架构下CPU缓存的布局,即在一个CPU 4核下,L1.L2.L3三级缓存与主内存的 ...
- 基础篇|一文搞懂RNN(循环神经网络)
基础篇|一文搞懂RNN(循环神经网络) https://mp.weixin.qq.com/s/va1gmavl2ZESgnM7biORQg 神经网络基础 神经网络可以当做是能够拟合任意函数的黑盒子,只 ...
- 一文搞懂RAM、ROM、SDRAM、DRAM、DDR、flash等存储介质
一文搞懂RAM.ROM.SDRAM.DRAM.DDR.flash等存储介质 存储介质基本分类:ROM和RAM RAM:随机访问存储器(Random Access Memory),易失性.是与CPU直接 ...
- 一文搞懂 Prometheus 的直方图
原文链接:一文搞懂 Prometheus 的直方图 Prometheus 中提供了四种指标类型(参考:Prometheus 的指标类型),其中直方图(Histogram)和摘要(Summary)是最复 ...
- Web端即时通讯基础知识补课:一文搞懂跨域的所有问题!
本文原作者: Wizey,作者博客:http://wenshixin.gitee.io,即时通讯网收录时有改动,感谢原作者的无私分享. 1.引言 典型的Web端即时通讯技术应用场景,主要有以下两种形式 ...
- 一文搞懂vim复制粘贴
转载自本人独立博客https://liushiming.cn/2020/01/18/copy-and-paste-in-vim/ 概述 复制粘贴是文本编辑最常用的功能,但是在vim中复制粘贴还是有点麻 ...
- 三文搞懂学会Docker容器技术(中)
接着上面一篇:三文搞懂学会Docker容器技术(上) 6,Docker容器 6.1 创建并启动容器 docker run [OPTIONS] IMAGE [COMMAND] [ARG...] --na ...
- 三文搞懂学会Docker容器技术(下)
接着上面一篇:三文搞懂学会Docker容器技术(上) 三文搞懂学会Docker容器技术(中) 7,Docker容器目录挂载 7.1 简介 容器目录挂载: 我们可以在创建容器的时候,将宿主机的目录与容器 ...
- 一文搞懂所有Java集合面试题
Java集合 刚刚经历过秋招,看了大量的面经,顺便将常见的Java集合常考知识点总结了一下,并根据被问到的频率大致做了一个标注.一颗星表示知识点需要了解,被问到的频率不高,面试时起码能说个差不多.两颗 ...
- 一文搞懂 js 中的各种 for 循环的不同之处
一文搞懂 js 中的各种 for 循环的不同之处 See the Pen for...in vs for...of by xgqfrms (@xgqfrms) on CodePen. for &quo ...
随机推荐
- [粘贴]环绕闸极不能让三星在3nm工艺领先台积电
环绕闸极不能让三星在3nm工艺领先台积电 http://www.pinlue.com/article/2019/08/1510/599507978757.html 转身遇见她 2019-08-15 收 ...
- CentOS上面阿里源的设置过程
1. 移除已经有的yum仓库 #原因: 公司内部部分境外网站不能访问,会提示异常. rm -rf /etc/yum.repos.d/* 2. 使用阿里源进行处理. #主要有两个, 一个是base的一个 ...
- Mark 一下 Redisson 可能需要升级版本
貌似有bug 我们高并发的情况下貌似遇到了 https://github.com/redisson/redisson/issues/2299
- React Hooks 指北
前言 这篇文章旨在总结 React Hooks 的使用技巧以及在使用过程中需要注意的问题,其中会附加一些问题产生的原因以及解决方式.但是请注意,文章中所给出的解决方式并不一定完全适用,解决问题的方案有 ...
- 【解决一个小问题】golang 的 `-race`选项导致 unsafe代码 panic
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 为了提升性能,使用 unsafe 代码来重构了凯撒加密的代 ...
- 4G5G和上网带宽与下载速度的换算方法
前言 2020年5G越来越火热,而且运营商多次推出免费宽带升级,免费升级到100M,20M升级50M等等.很多人疑惑我们平时 的下载速度也就几百K或者有时候能上1M,但是就算升级到10M的宽带,也从来 ...
- TienChin 活动管理-修改活动接口
前端 activity.js 直接替换现有的,最求速度了,后面在详细一个个记录,不在过多解释了. import request from '@/utils/request' /** * 查询活动列表 ...
- 华为云虚拟IP申请
在上一篇文章当中我们搭建好了我们的 Nginx,这节呢,我们主要就是搭建我们的 Keepalive 与 Haproxy,搭建之前我们首先不要将我们华为云上面的弹性公网IP给取消了后续在取消,这次我介绍 ...
- Linux命令-tail命令
Linux命令-tail 命令分析 命令格式 参数 例子 Linux命令-tail 命令分析 tail命令可用于查看文件的内容,通常用来查看日志,加上-f参数就可以查看最新的日志并且不断刷新. 命令格 ...
- CE修改器入门:浮点数的扫描
在前面的教程中我们使用4字节的方式进行扫描,但有些游戏使用了"浮点数"来存储数值,浮点数是带有小数点的数值(如 5.12 或 11321.1),正如本关中的健康和弹药,两者都以浮点 ...