美文网首页
Java NIO读书笔记 -- (二)简介

Java NIO读书笔记 -- (二)简介

作者: 我的小鱼干去哪儿了 | 来源:发表于2017-08-15 14:39 被阅读0次

I/O 的重要性

I/O操作比在内存中处理数据所需时间更长,即CPU处理速度远远大于I/O等待时间

Java I/O的重要性

多数 Java 应用程序已不 再受 CPU 的束缚(把大量时间用在执行代码上), 而更多时候是受 I/O 的束缚(等待数据传输)

NIO出现的意义

Java I/O的缺点

  1. 大多数情况下,Java 应用程序并非真的受着 I/O 的束缚。操作系统并非不能快速传送数据;相反,是 JVM 自身在 I/O 方面效率欠佳。
  2. 操作系统与 Java 基于流的 I/O 模型有些不匹配。 操作系统要移动的是大块数据(缓冲区), 这往往是在硬件直接存储器存取 (DMA)的协助下完成的。
  3. JVM 的 I/O 类喜欢操作小块数据——单个字节、几行文本。
  4. 操作系统送来整缓冲区的数据,java.io 的流数据类再花大量时间把它们拆成小块,往往拷贝一 个小块就要往返于几层对象。操作系统喜欢整卡车地运来数据,java.io 类则喜欢一铲子一铲子 地加工数据。

NIO

NIO可以轻松地把一卡车数据备份到您能直接使用的地方(ByteBuffer 对象)。

使用场景

如果代码大部分时间都处于 I/O 等待状态,那么,该考虑一下提升 I/O 效 率的问题了

I/O概念

1. 缓冲区操作

  • 输入/输出指的就是把数据移进或移出缓冲区
  • 进程执行I/O操作,就是向操作系统发出请求,让它要么把缓冲区里的数据排干(写),要么用数据把缓冲区填满 (读)。
图1

1.1 用户空间和内核空间

用户空间

  1. 用户空间是常规进程所在区域。JVM就是常规进程,驻守于用户空间。
  2. 用户空间是非特权区域,该区域执行的代码不能直接访问硬件设备。

内核空间

  1. 内核空间是操作系统所在区域。
  2. 内核代码有特别的权力,它能与设备控制器通讯,控制着用户区域进程的运行状态等
  3. 所有I/O 都直接或间接通过内核空间

1.2 I/O操作

  1. 当进程请求 I/O 操作的时候,它执行一个系统调用将控制权移交给内核。

C/C++程序员所熟知的底层函数 open( )、read( )、write( )和 close( )要做的无非就是建立和执行适当的系统调用。

当内核以这种方式被调用,它随即采取任何必要步骤,找到进程所需数据,并把数据 传送到用户空间内的指定缓冲区。内核试图对数据进行高速缓存或预读取,因此进程所需数据可能 已经在内核空间里了。如果是这样,该数据只需简单地拷贝出来即可。如果数据不在内核空间,则进程被挂起,内核着手把数据读进内存。

  1. 看了图 1,您可能会觉得,把数据从内核空间拷贝到用户空间似乎有些多余。为什么不直接 让磁盘控制器把数据送到用户空间的缓冲区呢?这样做有几个问题:

    • 首先,硬件通常不能直接访问 用户空间 。
    • 其次,像磁盘这样基于块存储的硬件设备操作的是固定大小的数据块,而用户进程请 求的可能是任意大小的或非对齐的数据块。
    • 在数据往来于用户空间与存储设备的过程中,内核负责数据的分解、再组合工作,因此充当着中间人的角色。
  2. 许多操作系统能把组装/分解过程进行得更加高效。

进程只需一个系统调用,就能把一连串缓冲区地址传递给操作系统。然后,内核就可以顺序填充或排干多个缓冲区,读的时候就把数据发散到多个用户空间缓冲区,写的时候再从多个缓冲区把数据汇聚起来

这样用户进程就不必多次执行系统调用(那样做可能代价不菲),内核也可以优化数据的处理 过程,因为它已掌握待传输数据的全部信息。

1.3 虚拟内存

所有现代操作系统都使用虚拟内存。虚拟内存意为使用虚假(或虚拟)地址取代物理(硬件 RAM)内存地址。

  1. 虚拟内存的好处
    • 一个以上的虚拟地址可指向同一个物理内存地址。
    • 虚拟内存空间可大于实际可用的硬件内存。
    • 把内核空间地址与用户空间的虚拟地址映射到同一个物理地址,这样, DMA 硬件(只能访问物理内存地址)就可以填充对内核与用户空间进程同时可见的缓冲区。
内存空间多重映射

1.4 文件I/O

文件系统是更高层次的抽象,是安排、解释磁盘(或其他随机存取块设备)数据的一种独特方 式。

文件系统把一连串大小一致的数据块组织到一起。有些块存储元信息,如空闲块、目录、索引 等的映射,有些包含文件数据。单个文件的元信息描述了哪些块包含文件数据、数据在哪里结束、 最后一次更新是什么时候,等等。

当用户进程请求读取文件数据时,文件系统需要确定数据具体在磁盘什么位置,然后着手把相 关磁盘扇区读进内存。 老式的操作系统往往直接向磁盘驱动器发布命令, 要求其读取所需磁盘扇 区。而采用分页技术的现代操作系统则利用请求页面调度取得所需数据。

1.4.1 内存映射文件

  1. 背景
    传统的文件 I/O 是通过用户进程发布 read( )和 write( )系统调用来传输数据的。为了在内核空间 的文件系统页与用户空间的内存区之间移动数据,一次以上的拷贝操作几乎总是免不了的。这是因 为,在文件系统页与用户缓冲区之间往往没有一一对应关系。

  2. 内存映射I/O

还有一种大多数操作系统都支 持的特殊类型的 I/O 操作,允许用户进程最大限度地利用面向页的系统 I/O 特性,并完全摒弃缓冲区拷贝。这就是内存映射 I/O

内存映射 I/O 使用文件系统建立从用户空间直到可用文件系统页的虚拟内存映射。

  1. 内存映射I/O的好处
    • 用户进程把文件数据当作内存,所以无需发布 read( )或 write( )系统调用。
    • 当用户进程碰触到映射内存空间,页错误会自动产生,从而将文件数据从磁盘读进内
      存。如果用户修改了映射内存空间,相关页会自动标记为脏,随后刷新到磁盘,文件得到更新。
    • 操作系统的虚拟内存子系统会对页进行智能高速缓存,自动根据系统负载进行内存管 理。
    • 数据总是按页对齐的,无需执行缓冲区拷贝。
    • 大型文件使用映射,无需耗费大量内存,即可进行数据拷贝。

虚拟内存和磁盘 I/O 是紧密关联的,从很多方面看来,它们只是同一件事物的两面。在处理大 量数据时,尤其要记得这一点。如果数据缓冲区是按页对齐的,且大小是内建页大小的倍数,那 么,对大多数操作系统而言,其处理效率会大幅提升。

1.4.2 文件锁定

文件锁定机制允许一个进程阻止其他进程存取某文件,或限制其存取方式。

通常的用途是控制 共享信息的更新方式,或用于事务隔离。在控制多个实体并行访问共同资源方面,文件锁定是必不可少的。数据库等复杂应用严重信赖于文件锁定。

“文件锁定”从字面上看有锁定整个文件的意思(通常的确是那样),但锁定往往可以发生在更 为细微的层面,锁定区域往往可以细致到单个字节。锁定与特定文件相关,开始于文件的某个特定 字节地址,包含特定数量的连续字节。这对于协调多个进程互不影响地访问文件不同区域,是至关 重要的。

  1. 文件锁定有两种方式
    • 共享的。 多个共享锁可同时对同一文件区域发生作用
    • 独占的。 独占锁它要求相关区域不能有其他锁定在起作用。
  2. 共享锁和独占锁的经典应用
    控制最初用于读取的共享文件的更新。某个进程要读取文件, 会先取得该文件或该文件部分区域的共享锁。第二个希望读取相同文件区域的进程也会请求共享锁。两个进程可以并行读取,互不影响。但是, 假如有第三个进程要更新该文件,它会请求独占 锁。该进程会处于阻滞状态,直到既有锁定(共享的、独占的)全部解除。一旦给予独占锁,其他 共享锁的读取进程会处于阻滞状态,直到独占锁解除。这样,更新进程可以更改文件,而其他读取 进程不会因为文件的更改得到前后不一致的结果。
共享锁阻断独占锁请求
独占锁阻断共享锁请求

1.5 流I/O

并非所有 I/O 都像前几节讲的是面向块的,也有流 I/O,其原理模仿了通道。I/O 字节流必须顺序存取,常见的例子有 TTY(控制台)设备、打印机端口和网络连接。

流的传输一般(也不必然如此)比块设备慢,经常用于间歇性输入。多数操作系统允许把流置 于非块模式,这样,进程可以查看流上是否有输入,即便当时没有也不影响它干别的。这样一种能 力使得进程可以在有输入的时候进行处理,输入流闲置的时候执行其他功能。

比非块模式再进一步,就是就绪性选择。就绪性选择与非块模式类似(常常就是建立在非块模 式之上),但是把查看流是否就绪的任务交给了操作系统。操作系统受命查看一系列流,并提醒进 程哪些流已经就绪。这样,仅仅凭借操作系统返回的就绪信息,进程就可以使用相同代码和单一线 程,实现多活动流的多路传输。这一技术广泛用于网络服务器领域,用来处理数量庞大的网络连 接。就绪性选择在大容量缩放方面是必不可少的。

相关文章

网友评论

      本文标题:Java NIO读书笔记 -- (二)简介

      本文链接:https://www.haomeiwen.com/subject/dojmrxtx.html