最近在学习Java IO这一块的东西,写篇文章总结一下。

所谓IO,是Input和Output的简称,也就是输入和输出。所以严瑾的写法应该是I/O,本文为了方便,就以IO代替。

IO有内存IO、网络IO和磁盘IO三种,通常我们说的IO指的是后两者。下面将介绍Linux IO和Java中的IO编程模型。

Linux中的IO模型

当前Linux主要有五种IO模型:

同步阻塞

同步非阻塞

IO复用

信号驱动

异步非阻塞

下面将一一介绍。

用户空间和内核空间

Linux为了保证系统安全,把内存分为了两种空间,分别是用户空间和内核空间。只有内核空间可以直接调用硬件(比如磁盘,网卡等)。

而我们一般的进程是在用户空间的,位于用户空间的进程是不能直接调用硬件的,而是通过系统调用来请求内核空间协助完成IO操作。

那内核空间是怎么协助的呢?内核空间会为每个IO设备维护一个缓冲区buffer(比如文件系统IO,就是页缓存page cache)。内核收到来自用户空间的进程的系统调用请求后,从IO设备中获取数据到buffer中,再将buffer中的数据copy到用户进程的地址空间。我们以读取数据为例,可以把它分为两个阶段:

准备数据:把数据从磁盘或网卡填充到内核空间的buffer里;

拷贝数据:把数据从内核空间拷贝到用户空间。

同步阻塞

在linux中,是通过recvfrom这个系统调用去通知内核准备IO数据的。同步阻塞是最简单的IO模型,上述两个阶段都是阻塞的,所以性能不高。示意图如下:

同步非阻塞

非阻塞的意思是你发出指令后就可以立即得到一个响应,不需要阻塞在那里等待。那如果你想得到这个指令的结果怎么办呢?其中一种方式就是轮询,不断地去尝试获取结果,如果得到结果,就进行下一步处理,否则就继续轮询。

同步非阻塞就是这种轮询的方式,这么做往往耗费大量CPU时间,不过这种模型偶尔也会遇到,通常是在只专门提供某种功能的系统中才有。并不是很常见。示意图:

IO复用

IO复用全称叫做IO多路复用(IO multiplexing)。

这里“复用”指的是对线程的复用。在上面的同步非阻塞模型中,仍然需要每个线程来单独操作一个IO,但如果使用多路复用模型,就只需要一个线程就可以操控多个IO连接。

Linux中,提供了select、poll、epoll三种接口函数来实现IO多路复用。Linux 2.4内核前主要是select和poll,自Linux 2.6内核正式引入epoll以来,epoll已经成为了目前实现高性能网络服务器的必备技术。尽管它们的使用方法不尽相同,但是本质上却没有什么区别。

其中select和poll有一些缺陷,它们会随着IO连接性能的增加,性能不断下降。所以现在一般都是使用epoll。想要研究这三者的原理的可以参考这篇文章。

当用户进程调用了epoll,那么进程会被等待,内核中一个或者多个IO条件就绪了,epoll就会返回。这个时候进程再进行下一步的拷贝数据阶段。

示意图:

这个图和同步阻塞的图其实并没有太大的不同,事实上,性能可能还更差一些。因为这里需要使用两个系统调用(select 和 recvfrom),而同步阻塞只调用了一个系统调用(recvfrom)。但是,用IO复用的优势在于它可以同时处理多个IO连接。

所以,如果处理的连接数不是很高的话,使用IO复用的Web服务器不一定比使用多线程 + 同步阻塞性能更好,可能延迟还更大。IO复用的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。

信号驱动

信号驱动IO模型是纯属信号来做的。

应用进程在发起IO时时通知内核,如果某个IO连接的某个事件发生时,请向我发一个信号。在收到信号后,信号对应的处理函数会进行后续处理,这个信号一般是SIGIO。

信号驱动IO主要是在UDP套接字上使用,在TCP套接字上几乎是没有什么使用的。

在UDP上,SIGIO信号会在下面两个事件的时候产生:

数据报到达套接字

套接字上发上一部错误

因此我们很容易判断SIGIO出现的时候,如果不是发生错误,那么就是有数据报到达了。而在TCP上,由于TCP是双工的,它的信号产生过于频繁,并且信号的出现几乎没有告诉我们发生了什么事情。因此对于TCP套接字,SIGIO信号是没有什么使用的。

示意图:

异步IO

其实上述四种IO严格来讲都是“同步的”,因为尽管它们有些在第一阶段实现了IO的非阻塞,但第二阶段仍然都是阻塞的。

异步IO在两个阶段都是非阻塞的。所以又称为“异步非阻塞IO”。如下图所示:

但数据完成拷贝后,内核就会就会产生一个信号或执行一个基于进程的回调函数来完成这次IO处理过程。

理论上来说,AIO似乎是一个很完美的IO模型。但Linux的AIO似乎做得并不好。详情可以参考以下两个链接:

Linux内核5.1版本推出了io_uring用于支持AIO,有兴趣的可以了解一下这篇文章:《Linux 5.1内核AIO 的新归宿:io_uring》。

Java中的IO模型

BIO

在Java 1.4以前,只有一种IO模型,就是BIO(Blocking-IO,阻塞IO)。所以当时Java在处理高并发时性能并不好,通常使用多线程+阻塞IO来实现Web Server,然后使用线程池协助。比如Tomcat就是基于这个原理。

BIO相关的类和接口就是我们在入门Java的时候通常会了解到的InputStream、OutputStream、Reader、Writer等等。

BIO虽然性能不高,但编程比较简单。如果你的程序对性能要求不大,可以考虑使用BIO。

NIO

在Java 1.4的时候,JDK提供了对NIO的支持。NIO底层是使用的上述的“IO复用”模型,也就是基于epoll。事实上目前很多高性能的服务或框架都用到了epoll,比如nginx,redis,nodejs等等。

NIO主要有这三个概念:Channel、Buffer、Selector。

基本上,所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点象流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。这里有个图示:

Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。这是在一个单线程中使用一个Selector处理3个Channel的图示:

后续会有文章详细分析Java NIO,本文主要做IO模型概述,所以这里不赘述。

Netty是Java NIO的集大成者,并且对网络编程模型的许多常见的问题做了处理和优化,后续也会写一点关于netty的文章。

AIO

AIO即异步IO,用到的就是上面介绍的最后一种网络模型:异步IO。Java AIO是在JDK 1.7引入的,也被称为NIO 2.0。

前面也提到了,Linux下的AIO做得并不好。所以JAVA AIO框架在windows下使用windows IOCP技术,在Linux下使用epoll多路复用IO技术模拟异步IO。

感兴趣的同学可以先看看这篇文章:《Java aio 编程》。

原文链接:https://yasinshaw.com/articles/52

java linux 信号_Linux和Java的I/O模型相关推荐

  1. java linux 串口_Linux Java 串口通信 | 学步园

    费了好大的劲搞定Linux系统上用Java写串口通信的问题. jdk中没有原生的串口api,网上找了半天的资料,大概知道了:Linux系统上用Java写串口程序,有两个包比较常用,一个是当年sun官方 ...

  2. 怎么看有没有java环境变量_linux查看java环境变量

    本文收集整理关于linux查看java环境变量的相关议题,使用内容导航快速到达. 内容导航: Q1:linux 怎么修改java环境变量 纯java开发的软件在linux下面也可以应用自如.那么首先就 ...

  3. java linux at_linux下运行java程序报错,求大神解答

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 Exception in thread "main" java.lang.UnsatisfiedLinkError: /usr/loc ...

  4. java linux 管理系统_用Java开发一个本地服务管理软件

    使用Java开发一个本机服务管理程序,能够控制本机Tomcat.Apache服务的开启和关闭,图形界面控制.用户可以自己扩展其他服务,用来学习图形界面编程.多线程.事件响应等都不错. 一.最终界面 先 ...

  5. java linux 信号_Java 中关于信号的处理在Linux下的实现

    Java里信号处理的函数 在linux里可以设置进程级别的信号的处理函数,在内核中信号值及进程针对该信号的处理函数建立了映射关系,主要有2个函数来设置信号处理函数:signal(),sigaction ...

  6. linux java测试工具_Linux下Java虚拟机状态监测工具

    Linux下Java虚拟机状态检测工具 工具名称: jps     JVM Process Status Tool - Lists instrumented HotSpot Java virtual ...

  7. linux设置java环境变量_linux配置java环境变量

    一. 解压安装jdk 在shell终端下进入jdk-8u5-linux-x64.gz文件所在目录, 执行命令 tar zxvf jdk-8u5-linux-x64.gz 解压出一个目录 jdk1.8. ...

  8. linux java进程消失_Linux服务器Java进程消失问题解决

    Linux服务器Java进程消失问题解决 发布时间:2020-08-20 15:17:37 来源:脚本之家 阅读:65 作者:myseries 这篇文章主要介绍了Linux服务器Java进程消失问题解 ...

  9. linux java获取路径_linux中java获取路径的方法

    linux中java获取路径的方法 发布时间:2020-05-06 11:11:26 来源:亿速云 阅读:700 作者:小新 今天小编给大家分享的是linux中java获取路径的方法,相信很多人都不太 ...

最新文章

  1. Mysql高级调优篇——第二章:Explain执行计划深度剖析
  2. 百度Apollo飘了!在京开启全无人RoboTaxi体验,车型还能选,原因竟是嫌40万人次体验不够...
  3. Blockchain区块链架构设计之四:Fabric多通道和下一代账本设计
  4. 山大计算机上机复试题目,2010年计算机复试上机 回忆
  5. git idea 本地历史版本回滚_如何为IDEA项目创建GitHub存储库和本地Git存储库
  6. 有一个分数序列:2/1,3/2,5/3,8/5,13/8,21/13...求出这个数列的前20项之和
  7. kubernetes视频教程笔记 (18)-service
  8. 服务器上搭shinyApp:shiny-server配置及报错解决
  9. 数据结构——栈(链栈)
  10. JanusGraph的使用
  11. linuxmint/ubuntu修改主机名hostnam
  12. 输入英文句子,导出英语单词个数和英文字母个数 Python
  13. 1.6 Image Rotation
  14. Linux普通用户su root权限的开启和禁止
  15. cpython cython_Cython笔记
  16. GIS应用技巧之景观格局分析(一)
  17. js制作倒计时,天,小时,分,秒
  18. Error:A JNI error has occurred,please check your installation and try again
  19. iis服务器里网站无法访问,IIS服务器网站无法访问解决方法(图文).doc
  20. 计算机一级wps选择题必背知识点,计算机一级WPS提高练习题及答案

热门文章

  1. Hyperledger Fabric服务器配置及修改Docker容器卷宗存储根目录/位置
  2. 苹果本周四将发布 OS X Yosemite 公开测试版
  3. JDBC--调用函数与存储过程
  4. 棋牌游戏服务器架构: 详细设计(3) 数据库设计
  5. soltrace教程(3)基本使用
  6. Java垃圾回收精粹 — Part1
  7. Hyper-V 3中虚拟机CPU竞争机制
  8. Android开发人员的10大抱怨
  9. C#/.net 中的事件与代理
  10. makefile编译脚本