乱弹 --- Linux 之 I/O模型

随着现在互联网技术的发展,在以前对一个网站的要求都不高,但是现在要求越来越高了,不断的优化服务,不断的提高硬件设施,在以前I/O方面的消耗还影响不大,但是现在I/O的瓶颈越来越凸显,了解I/O模型,能了解I/O的发展历史,同时也能帮助我们写出更加健壮的代码。

一般来说,I/O模型可以分为阻塞/非阻塞和同步/异步,我们先从阻塞/非阻塞模型说起。

同步阻塞I/O

用户进程发起I/O操作后,需要等待其操作完成才能继续运行。阻塞一词可以理解为等待,具体什么等待呢?— 用户等待,用户一旦发起一个阻塞I/O,那么用户就需要等待内核把数据准备好,只有内核数据准备好了后,用户才读写数据然后继续往下执行。
阻塞IO的编程模型非常易于理解,但性能却并不理想,它会造成CPU的大量闲置(注意这里的CPU闲置指的是用户CPU时间)。使用阻塞IO开发的系统吞吐量会比较低。虽然可以优化为每一次 Socket 请求使用独立的线程,但会造成线程膨胀,使系统越来越慢,当达到一定的并发后,服务器最终会宕机。通过线程池可以控制系统创建线程的数量,但是毕竟用了很多的线程来提高吞吐量,但是随着线程的增多,线程的开销也会越来越大,因此仍然无法做到系统性能的最优。

同步阻塞 I/O 模型的典型流程:
这里写图片描述
注:部分图片引自 使用异步 I/O 大大提高应用程序的性能

同步非阻塞I/O

用户进程发起I/O操作后,无需等待内核IO操作完成(这里的等待是用户线程等待),可继续做其它事情,但是用户进程需要不断的询问I/O操作是否就绪,因为内核不会通知用户线程数据已经准备好了,需要用户线程主动的去判断,阻塞I/O也需要主动的等待数据,只是在等待的过程中,不能干其它的事情,而非阻塞I/O在等待内核数据的同时可以去干其它的事情,当询问得知数据准备好了后,再去读取数据,但是用户线程也不知道什么时候数据准备好了,因此需要不断去问内核数据是否准备就绪,因此不停轮询会略微增加额外的CPU资源浪费。

同步非阻塞 I/O 模型的典型流程:
这里写图片描述

异步阻塞I/O

用户进程发起I/O操作后,不等待内核 IO 操作的完成,等内核完成 IO 操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问 IO 是否完成,那么为什么说是阻塞的呢?因为用户发起的调用是阻塞的。

异步阻塞 I/O 模型的典型流程 (以select为例):
这里写图片描述

异步非阻塞I/O

用户进程只需要发起一个 IO 操作然后立即返回,等 IO 操作真正的完成以后,应用程序会得到 IO 操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的 IO 读写操作,因为 真正的 IO读取或者写入操作已经由 内核完成了。
异步非阻塞 I/O 模型的典型流程:
这里写图片描述

Java中的I/O

Java对于IO的封装分为BIO、NIO和AIO。Java目前并不支持异步IO,BIO对应的是同步阻塞IO,NIO和AIO对应的都是同步非阻塞IO。

java nio的io模型是同步非阻塞(基于事件驱动机制,感觉像异步),这里的同步异步指的是用户进程是否需要参与IO,很明显 目前 java 中的nio 都需要自己去read 数据。

java nio提供了异步处理,这个异步指的内核准备好数据后通知用户进程,然后用户进程再继续处理,因此从这方面看是异步的,它对IO的读写还是同步阻塞的。只是通过线程复用,将IO的准备时间分离出来。

BIO

Java中的BIO对应的是同步阻塞IO。 BIO的服务器实现模式为一个连接分配一个线程,服务端的线程个数和客户端并发访问数呈正比,随着访问量的增加会迅速导致线程数量膨胀,最终导致系统性能的急剧下降。可以通过使用线程池实现一个线程处理多个客户端的模型,但开启线程的数量终归会受到系统资源的限制,而且频繁的线程上下文切换也会导致CPU的利用率不高。

NIO

JDK 1.4中的java.nio.*包中引入了全新的Java I/O类库,与之相对应的是同步非阻塞IO。相比于BIO,NIO的性能实现了质的提升,它适用于连接数目多且连接比较短的轻量级操作架构。

AIO

NIO.2虽然在2003年的JSR 203就已经提出,但直到2011年才于JDK 7中实现并一同发布。它提供了更多的文件系统操作API以及文件的异步IO操作,即AIO。由于每个操作系统AIO对应的实现方式都不同,因此Java做了封装。Linux系统中2.6内核及其以上对应的是epoll,低版本仍然对应poll,Windows系统也有相应的IOCP的系统级支持。由于Java的服务端程序很少将Windows系统作为生产服务器,因此Linux系统的I/O模型更加受到关注。我们看到,在Linux系统上,Java实际并未真正使用异步IO,而是非阻塞IO,AIO虽然封装的更好,模拟成为了异步IO的样子,但是其本质仍然是poll或epoll这样的同步IO。

Linux I/O 模型

阻塞IO

基本描述同上

非阻塞IO

基本描述同上

IO复用

Linux提供select/poll,进程通过将一个或多个fd传递给select或poll系统调用,阻塞在select操作上,这样,select/poll可以帮我们侦测多个fd是否处于就绪状态。

select/poll是顺序扫描fd是否就绪,而且支持的fd数量有限,因此它的使用受到了一些制约。

Linux还提供一个epoll系统调用,epoll使用基于事件驱动方式代替顺序扫描,因此性能更高。当有fd就绪时,返回需要处理的事件,而不需要像select 遍历所有的fd那样。

epoll支持两种触发模式:边沿触发方式,水平触发模式(默认)
为了更好的理解这两种触发模式,考虑下面场景:
(1)客户端发来100字节的数据
(2)服务器调用epoll_wait后,发现套接字可读,于是读取了50字节,则在服务器套接字接收缓存中还有50字节未读,
(3)当服务器再次执行epoll_wait后,如果是边沿触发,则尽管接收缓存中还有50字节数据未读,则再次调用epoll_wait后,epoll_wait将不再返回该套接字可读,反之,若服务器工作于水平触发模式,则因为套接字缓存中有数据可读,所以当再次调用epoll_wait后,将仍可以返回此套接字为可读状态,因此边沿触发只发生在状态改变的那一时刻,而对于水平触发,只要套接字可读写,epoll_wait就立即返回。

信号驱动IO

我们可以使用信号,来告诉内核当数据准备就绪的时候,使用SIGIO信号来通知我们。我们将此称为信号驱动的I/O

异步IO

通知内核启动某个操作,并让内核在整个操作完成后(包括数据的复制)通知用户进程。
信号驱动I/O模型通知的是何时可以开始一个I/O操作,异步I/O模型有内核通知I/O操作何时已经完成

参考

使用异步 I/O 大大提高应用程序的性能

聊聊Linux 五种IO模型

linux中的“5种网络 IO 模型”

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值