IO模型

1. IO读写原理

无论是Socket的读写还是文件的读写,在Java层面的应用开发或者是Linux系统底层开发,都属于input和输出output的处理,简称为IO读写。在原理和处理流程上,都是一致的。区别在于参数的不同。

用户程序在进行IO的读写,基本都会调用到read&write两大系统调用。可能因为操作系统的不同,实现read&write两个功能的函数名称不同。

read系统调用,并不是直接将数据从物理设备读取到内存;write系统调用,也并不是直接将数据写入到物理设备。

read系统调用,是把数据从内核缓冲区复制到进程缓冲区;write系统调用,是把数据从进程缓冲区复制到内核缓冲区。这两个系统调用,都不负责数据在内核缓冲区和磁盘之间的交换。底层数据的交换,是由操作系统kernel内核完成的。

1.1. 内核缓冲区与进程缓冲区

缓冲区的目的,是为了减少频繁的系统IO调用。大家都知道,系统调用需要保存之前的进程数据和状态等信息,而结束调用之后回来还需要恢复之前的信息,为了减少这种损耗时间、也损耗性能的系统调用,于是出现了缓冲区。

在linux系统中,系统内核有个缓冲区叫内核缓冲区。每个进程自己独立的缓冲区,称为进程缓冲区。

有了缓冲区,操作系统使用read函数把数据从内核缓冲区复制到进程缓冲区,write函数把数据从进程缓冲区复制到内核缓冲区中。等待缓冲区中的数据达到一定数量的时候,再进行IO的调用,提升性能。读取和存储由系统内核来决定,用户程序不需要关心。

所以,用户程序的IO读写程序,大多数情况下,并没有实际的IO操作,而是在读写自己的进程缓冲区。

1.2. Java IO读写的底层流程

用户程序的IO读写,是数据在内核缓冲区和进程缓冲区之间的交换。

一个典型的Java服务端处理网络请求的过程:

1)客户端请求

Linux通过网卡,读取客户端的请求数据,将数据读取到内核缓冲区。

2)获取请求数据

服务器从内核缓冲区读取数据到Java进程缓冲区。

3)服务器端业务处理

Java服务器端在自己的用户空间中,处理客户端的请求。

4)服务端返回数据

Java服务端已构建好的响应,从用户空间写入系统缓冲区。

5)发送给客户端

Linux内核通过网络IO,将内核缓冲区中的数据,写入网卡,网卡通过底层的通讯协议,会将数据发送给目标客户端。

2. 四种常见的IO模型

阻塞IO,指的是需要内核IO彻底完成之后才返回到用户空间,执行用户的操作。传统的IO模型都是同步阻塞IO,在Java中默认创建的socket都是阻塞的。

非阻塞IO,指的是用户程序不需要等待内核IO操作完成,内核会立即返回给用户一个状态值,用户空间无需等到系统的IO操作彻底完成,可以立即返回用户空间,执行用户的操作,处于非阻塞的状态。

同步IO/异步IO,是一种用户空间与内核空间的调用发起方式。同步IO是指用户空间线程是主动发起IO请求的一方,内核空间是被动接受方,异步IO则反过来。

1) 同步阻塞IO (Blocking IO)

用户空间主动调用,且等待内核空间彻底执行完成才能返回执行用户空间的程序。

2)同步非阻塞IO (Non-blocking IO)

用户空间主动调用,不需要等待内核IO操作完成就能继续执行用户空间的程序。

3)IO多路复用(IO Multiplexing)

经典的Reactor设计模式,有时也被称之为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型。

4)异步IO(Asynchronous IO)

内核空间主动调用,类似于Java中的回调模式,用户空间向内核空间注册各种IO时间的回调函数,由内核主动调用。

3. 同步阻塞IO

在Linux的Java进程中,默认情况下所有的socket都是blocking IO。在阻塞式I/O模型中,应用程序在从IO系统调用开始,一直到系统调用返回,这段时间都是阻塞的。返回成功后,应用程序开始处理用户空间的缓存数据。

发起一个blocking socket的read读操作系统调用,流程大概是这样:

1)当用户线程调用了read系统调用,内核(kernel)就开始了IO的第一个阶段:准备数据。很多时候,数据在一开始还没有到达(比如,还没有收到一个完整的Socket数据包),这个时候kernel就要等到足够的数据到来。

2)当kernel一直等到数据准备好了,它就会将数据从内核缓冲区拷贝到用户缓冲区(用户内存),然后内核返回结果。

3)从开始IO读的read系统调用开始,用户线程就进入阻塞状态。一直到kernel返回结果后,用户线程才解除block的状态,重新运行起来。

所以,blocking IO的特点就是在内核进行IO执行的两个阶段,用户线程都被block了。

BIO的优点

程序简单,在阻塞等待数据期间,用户线程挂起。用户线程基本不会占用CPU资源。

BIO的缺点

一般情况下,会为每个连接配套一条独立的线程,或者说一条线程维护一个连接成功的IO流的读写。在并发量小的情况下,这个没有什么问题。但是,在高并发的场景下,需要大量的线程来维护大量的网络连接,内存、线程切换开销会非常巨大。因此,基本上BIO模型在高并发场景下是不可用的。

4. 同步非阻塞IO

在Linux系统中,可以通过设置socket使其变为non-blocking。NIO模型中应用程序在一开始IO系统调用,会出现一下两种情况:

1)在内核缓冲区没有数据的情况下,系统调用回立即返回,返回一个调用失败的信息。

2)在内核缓冲区有数据的情况下,是阻塞的,直到数据从内核缓冲区复制到用户进程缓冲区。复制完成后,系统调用返回成功,应用程序开始处理用户空间的缓存数据。

发起一个non-blocking socket的read读操作系统调用的流程:

1)在内核数据没有准备好的阶段,用户线程发起IO请求时,立即返回。用户线程需要不断地发起IO系统调用。

2)内核数据到达后,用户线程发起系统调用,用户线程阻塞。内核开始复制数据。它会将数据从kernel内核缓冲区拷贝到用户缓冲区,然后kernel返回结果。

3)用户线程解除block状态,重新运行起来。经过多次尝试,用户线程终于真正获取到数据,继续执行。

NIO的特点

应用程序的线程需要不断地进行IO系统调用,轮询数据是否已经准备好,如果没有准备好,继续轮询,直到完成系统调用为止。

NIO的优点

每次发起的IO系统调用,在内核的等待数据的过程中可以立即犯返回,用户线程不会阻塞,实时性较好。

NIO的缺点

需要不断地重复发起IO系统调用,这种不断地轮询,将会不断地询问内核,这将占用大量的CPU时间,系统资源利用率较低。

总之,NIO在高并发场景下,也是不可用的。一般的Web服务器不适用这种IO模型。一般很少直接用这种模型,而是在其他IO模型中使用非阻塞IO这一特性。

5. IO多路复用

如何避免同步非阻塞NIO模型中轮询等待的问题呢?这就是IO多路复用模型。

IO多路复用模型,就是通过一种新的系统调用,一个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是内核缓冲区可读/可写),内核kernel能够通知程序进行相应的IO系统调用。

目前支持IO多路复用的系统调用,有select,epoll等等。select系统调用,是目前几乎在所有的操作系统上都支持,具有良好的跨平台特性。epoll是在Linux2.6内核中提出的,是select系统调用的Linux增强版本。

IO多路复用模型是基本原理就是select/epoll系统调用,单个线程不断地轮询select/epoll系统调用所负责的成百上千的socket连接,当某个或者某些socket网络连接有数据到达了,就返回这些可以读写的连接。因此,通过一次select/epoll系统调用, 就查询到可以读写的一个甚至是成百上千的网络连接。

发起一个多路复用IO的read读操作系统调用的流程:

1)进行select/epoll系统调用,查询可以读的连接。kernel会查询所有select的可查询的socket列表,当任何一个socket中的数据准备好了,select就会返回。当用户进程调用了select/epoll,那么整个线程会被block。

2)用户线程获得了目标连接后,发起read系统调用,用户线程阻塞。内核开始复制数据。它就会将数据从kernel内核缓冲区拷贝到用户缓冲区,然后kernel返回结果。

3)用户线程解除block的状态,继续执行。

多路复用IO的特点:

建立在操作系统kernel内核能够提供的多路分离系统调用select/epoll基础之上的。多路复用IO需要用到两个系统调用(system call),一个select/epoll查询调用,一个是IO的读取调用。

和NIO模型相似,多路复用IO需要轮询。负责select/epoll查询调用的线程,需要不断地进行select/epoll轮询,查找出可以进行IO操作的连接。

另外,多路复用IO模型与前面的NIO模型,是有关系的。对于每一个可以查询的socket,一般都设置成non-blocking模型。只是这一点,对于用户程序是无感知的。

多路复用IO的优点:

可以同时处理成千上万个连接,系统不必创建和维护线程,从而大大减小系统的开销。

多路复用IO的缺点:

本质上,select/epoll系统调用,属于同步IO,也是阻塞IO。都需要在读写时间就绪后,自己负责进行读写,也就是说这个读写过程是阻塞的。

如何充分的解除线程的阻塞呢?那就是异步IO模型。

6. 异步IO

如何进一步提升效率,解除最后一点阻塞呢?这就是异步IO模型,全称asynchronous I/O,简称AIO。

AIO的基本流程是:用户线程通过系统调用,告知kernel内核启动某个IO操作,用户线程返回。kernel内核在整个IO操作(包括数据准备、数据复制)完成后,通知用户程序,用户执行后续的业务操作。

kernel的数据准备是将数据从网络物理设备(网卡)读取到内核缓冲区;kernel的数据复制是将数据从内核缓冲区拷贝到用户缓冲区。

发起一个异步IO的read读操作系统调用的流程:

1)当用户线程发起read系统调用,立刻就可以开始去做其他的事,用户线程不阻塞。

2)kernel内核开始准备数据,等数据准备好了,它会将数据从内核缓冲区复制到用户缓冲区。

3)kernel会给用户线程发送一个信号(signal),或者回调用户线程注册的回调接口,告诉用户线程read操作完成了。

4)用户线程读取用户缓冲区的数据,完成后续的业务操作。

异步IO模型的特点:

在kernel内核的等待数据和复制数据的两个阶段,用户线程都不是block的。用户线程需要接受kernel的IO操作完成的事件,或者说注册IO操作完成的回调函数到操作系统的内核。所以说,异步IO有的时候,也叫做信号驱动IO。

异步IO模型缺点:

需要完成事件的注册与传递,这里边需要底层操作系统的大量支持,去做大量的工作。

目前来说,Windows系统下通过IOCP实现了真正的异步I/O。但是,就目前的业界形式来说。Windows系统,很少作为百万级以上或者更高并发应用的服务器的操作系统来使用。

而在Linux系统下,异步IO模型在2.6版本才引入,目前并不完善。所以,这也是在Linux下,实现高并发网络编程时都是以IO复用模型为主的原因。

7. 小结

四种IO模型,理论上越往后,阻塞越少,效率也是最优。在这四种I/O模型中,前三种属于同步IO,因为其中真正的IO操作将阻塞用户线程。只有最后一种,才是真正的异步IO模型,可惜目前Linux操作系统尚缺完善。

图解四大IO模型与原理相关推荐

  1. 常用 IO 模型图解介绍

    很多时候对于不同的IO模型的概念和原理我们可能不是很清楚,有时候可能也会在不同的IO间迷糊,笔者也是有同样的问题.所以经过系统的学习以后将我们常见的五种IO模型在这里做一下总结,以供大家参考和学习. ...

  2. 打卡学习 | Redis原理应用-线程IO模型

    这是小小国庆节更新的第一篇,小小本篇将会更新Redis原理应用,线程IO模型.我是小小,我们这期见面了. 送书反馈 小小送的书到啦,一共三本书,晒图如下. 小小开始今天的文章,跟我学Redis系列之, ...

  3. java基础巩固-宇宙第一AiYWM:为了维持生计,四大基础之OS_Part_2整起~IO们那些事【包括五种IO模型:(BIO、NIO、IO多路复用、信号驱动、AIO);零拷贝、事件处理及并发等模型】

    PART0.前情提要: 通常用户进程的一个完整的IO分为两个阶段(IO有内存IO.网络IO和磁盘IO三种,通常我们说的IO指的是后两者!):[操作系统和驱动程序运行在内核空间,应用程序运行在用户空间, ...

  4. 图文详解 epoll 原理【Redis,Netty,Nginx实现高性能IO的核心原理】epoll 详解

    [Redis,Netty,Nginx 等实现高性能IO的核心原理] I/O 输入输出(input/output)的对象可以是文件(file), 网络(socket),进程之间的管道(pipe).在li ...

  5. JAVA那点破事!并发、IO模型、集合、线程池、死锁、非阻塞、AQS....

    关于Java面试,面试官一般喜欢问哪些问题? 本文对一些高频问题做了汇总,为了便于大家查找问题,了解全貌,整理个目录,我们可以快速全局了解关于 JAVA 接下来,我们逐条来看看每个问题及答案 JDK. ...

  6. netty发送数据_【Netty】JAVA IO模型

    为什么要学Netty? 其实我们每学一样东西,就要了解学这个的必要性.那么为什么要学Netty呢. 其实但凡涉及网络通信就必然离不开网络编程.Netty目前作为JAVA网络编程最热门的框架,毫不夸张的 ...

  7. 常见的 IO 模型有哪些?Java 中 BIO、NIO、AIO 的区别?

    IO 模型这块确实挺难理解的,需要太多计算机底层知识.写这篇文章用了挺久,就非常希望能把我所知道的讲出来吧!希望朋友们能有收货!为了写这篇文章,还翻看了一下<UNIX 网络编程>这本书,太 ...

  8. python 靶心_手把手教你使用Python实战反欺诈模型|原理+代码

    原标题:手把手教你使用Python实战反欺诈模型|原理+代码 作者 | 萝卜 来源 | 早起Python(ID: zaoqi-python) 本文将基于不平衡数据,使用Python进行 反欺诈模型数据 ...

  9. 【Netty】Netty 简介 ( 原生 NIO 弊端 | Netty 框架 | Netty 版本 | 线程模型 | 线程 阻塞 IO 模型 | Reactor 模式引入 )

    文章目录 一. NIO 原生 API 弊端 二. Netty 简介 三. Netty 架构 四. Netty 版本 五. Netty 线程模型 六. 阻塞 IO 线程模型 七. 反应器 ( React ...

最新文章

  1. paramiko监控 windows服务器 被监控服务器只需要安装openssh服务即可基于wmic完成大部分监控...
  2. http 协议上传文件multipart form-data boundary 说明--转载
  3. Jenkins中连接Git仓库时提示:无法连接仓库:Error performing git command: git ls-remote -h
  4. ThreadLocal 变量和 与线程池配合使用时可能会出现的问题
  5. ORACLE---Unit04: SQL(高级查询)
  6. textbox回车事件中拿不到text的处理办法(wpf)
  7. Use Visual Studio Code to create and run Transact-SQL scripts for SQL Server
  8. MongoDB的存储结构及对空间使用率的影响
  9. Kbengine游戏引擎-【4】demo-kbengine_unity3d_demo 在容器docker上安装测试
  10. matlab 基础 —— 文本文件读取
  11. 单机如何修改服务器,修改dnf单机服务器地址
  12. ORACLE通过身份证号计算年龄
  13. 160413、生成随机校验码
  14. java_opts=quot;-server,tomcat高并发的配置
  15. 支付宝异步回调步骤+内网穿透++雷神商城项目
  16. mysql 1146 错误处理
  17. 论文——多指标与机器学习算法相结合的中国县级玉米产量早期预测
  18. 5.2 BGP水平分割
  19. 鸿蒙启智 博学多才,花园小学:浩荡儒风续 鸿蒙今日开
  20. auc和roc曲线解释_ROC曲线和AUC —解释

热门文章

  1. .net core优秀开源项目(更新中)
  2. Adaptec by PMC为高密度超大规模数据中心三倍提升存储连接能力
  3. 新安全生产法知识问答活动
  4. 在java商城开发中map集合的应用
  5. 【技术教程】基于EasyRTSPSever与GB28181协议设备端EasyGBD实现的摄像机模拟器架构
  6. 华北机电学院计算机教材,华北机电学院.PDF
  7. php 判断字符存在,PHP判断字符串str中是否存在某个值
  8. 《uni-app》一个非canvas的飞机对战小游戏实现-requestAnimationFrame详解
  9. DW打卡第一次——线性回归
  10. 梯度惩罚(Pytorch)