`
裴小星
  • 浏览: 265300 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
8ccf5db2-0d60-335f-a337-3c30d2feabdb
Java NIO翻译
浏览量:27826
F3e939f0-dc16-3d6e-8c0b-3315c810fb91
PureJS开发过程详解
浏览量:74152
07a6d496-dc19-3c71-92cf-92edb5203cef
MongoDB Java ...
浏览量:62957
社区版块
存档分类
最新评论

Java NIO 第一章(2)

阅读更多

第一章 简介

1.4 I/O概念

  Java平台提供了一整套I/O隐喻,其抽象程度各有不同。然而,离冰冷的现实越远,要想搞清楚来龙去脉就越难——不管使用哪一种抽象,情况都是如此。JDK 1.4NIO软件包引入了一套新的抽象用于I/O处理。与以往不同的是,新的抽象把重点放在了如何缩短抽象与现实之间的距离上面。NIO抽象与现实中存在的实体有着非常真实直接的交互关系。要想最大限度地满足Java应用程序的密集I/O需求,理解这些新的抽象,以及与其发生交互作用的I/O服务(其重要性并不亚于抽象),正是关键所在。

  本书假定您熟知基本的I/O概念,因此,本节将快速回顾一些基本概念,为下一步论述新的NIO类如何运作奠定基础。NIO类模拟I/O函数,因此,必须掌握操作系统层面的处理细节,才能理解新的I/O模型。

  在阅读本书的过程中,理解以下概念是非常重要的:

·         缓冲区操作

·         内核空间与用户空间

·         虚拟内存

·         分页技术

·         面向文件的I/O和流I/O

·         多工I/O(就绪性选择)

1.4.1 缓冲区操作

  缓冲区,以及缓冲区如何工作,是所有I/O的基础。所谓输入/输出讲的无非就是把数据移进或移出缓冲区。

  进程执行I/O操作,归结起来,也就是向操作系统发出请求,让它要么把缓冲区里的数据排干(写),要么用数据把缓冲区填满(读)。进程使用这一机制处理所有数据进出操作。操作系统内部处理这一任务的机制,其复杂程度可能超乎想像,但就概念而言,却非常直白易懂。

  图1-1简单描述了数据从外部磁盘向运行中的进程的内存区域移动的过程。进程使用read( )系统调用,要求其缓冲区被填满。内核随即向磁盘控制硬件发出命令,要求其从磁盘读取数据。磁盘控制器把数据直接写入内核内存缓冲区,这一步通过DMA完成,无需主CPU协助。一旦磁盘控制器把缓冲区装满,内核即把数据从内核空间的临时缓冲区拷贝到进程执行read( )调用时指定的缓冲区。

1-1. I/O缓冲区操作简图

  图中明显忽略了很多细节,仅显示了涉及到的基本步骤。

  注意图中用户空间和内核空间的概念。用户空间是常规进程所在区域。JVM就是常规进程,驻守于用户空间。用户空间是非特权区域:比如,在该区域执行的代码就不能直接访问硬件设备。内核空间是操作系统所在区域。内核代码有特别的权力:它能与设备控制器通讯,控制着用户区域进程的运行状态,等等。最重要的是,所有I/O都直接(如这里所述)或间接(见1.4.2小节)通过内核空间。

  当进程请求I/O操作的时候,它执行一个系统调用(有时称为陷阱)将控制权移交给内核。C/C++程序员所熟知的底层函数open( )read( )write( )close( )要做的无非就是建立和执行适当的系统调用。当内核以这种方式被调用,它随即采取任何必要步骤,找到进程所需数据,并把数据传送到用户空间内的指定缓冲区。内核试图对数据进行高速缓存或预读取,因此进程所需数据可能已经在内核空间里了。如果是这样,该数据只需简单地拷贝出来即可。如果数据不在内核空间,则进程被挂起,内核着手把数据读进内存。

  看了图1-1,您可能会觉得,把数据从内核空间拷贝到用户空间似乎有些多余。为什么不直接让磁盘控制器把数据送到用户空间的缓冲区呢?这样做有几个问题。首先,硬件通常不能直接访问用户空间(关于这一点,原因很多,不在本书讨论范围之内。硬件设备通常不能直接使用虚拟内存地址。)。其次,像磁盘这样基于块存储的硬件设备操作的是固定大小的数据块,而用户进程请求的可能是任意大小的或非对齐的数据块。在数据往来于用户空间与存储设备的过程中,内核负责数据的分解、再组合工作,因此充当着中间人的角色。

1.4.1.1 发散/汇聚

  许多操作系统能把组装/分解过程进行得更加高效。根据发散/汇聚的概念,进程只需一个系统调用,就能把一连串缓冲区地址传递给操作系统。然后,内核就可以顺序填充或排干多个缓冲区,读的时候就把数据发散到多个用户空间缓冲区,写的时候再从多个缓冲区把数据汇聚起来(图1-2)。

1-2. 三个缓冲区的发散读操作

  这样用户进程就不必多次执行系统调用(那样做可能代价不菲),内核也可以优化数据的处理过程,因为它已掌握待传输数据的全部信息。如果系统配有多个CPU,甚至可以同时填充或排干多个缓冲区。

1.4.2 虚拟内存

  所有现代操作系统都使用虚拟内存。虚拟内存意为使用虚假(或虚拟)地址取代物理(硬件RAM)内存地址。这样做好处颇多,总结起来可分为两大类:

1.       一个以上的虚拟地址可指向同一个物理内存地址。

2.       虚拟内存空间可大于实际可用的硬件内存。

  前一节提到,设备控制器不能通过DMA直接存储到用户空间,但通过利用上面提到的第一项,则可以达到相同效果。把内核空间地址与用户空间的虚拟地址映射到同一个物理地址,这样,DMA硬件(只能访问物理内存地址)就可以填充对内核与用户空间进程同时可见的缓冲区(见图1-3)。

1-3. 内存空间多重映射

  这样真是太好了,省去了内核与用户空间的往来拷贝,但前提条件是,内核与用户缓冲区必须使用相同的页对齐,缓冲区的大小还必须是磁盘控制器块大小(通常为512字节磁盘扇区)的倍数。操作系统把内存地址空间划分为页,即固定大小的字节组。内存页的大小总是磁盘块大小的倍数,通常为2次幂(这样可简化寻址操作)。典型的内存页为1,0242,0484,096字节。虚拟和物理内存页的大小总是相同的。图1-4显示了来自多个虚拟地址的虚拟内存页是如何映射到物理内存的。

1-4. 内存页

1.4.3 内存页面调度

  为了支持虚拟内存的第二个特性(寻址空间大于物理内存),就必须进行虚拟内存分页(经常称为交换,虽然真正的交换是在进程层面完成,而非页层面)。依照该方案,虚拟内存空间的页面能够继续存在于外部磁盘存储,这样就为物理内存中的其他虚拟页面腾出了空间。从本质上说,物理内存充当了分页区的高速缓存;而所谓分页区,即从物理内存置换出来,转而存储于磁盘上的内存页面。

  图1-5显示了分属于四个进程的虚拟页面,其中每个进程都有属于自己的虚拟内存空间。进程A有五个页面,其中两个装入内存,其余存储于磁盘。

1-5. 用于分页区高速缓存的物理内存

  把内存页大小设定为磁盘块大小的倍数,这样内核就可直接向磁盘控制硬件发布命令,把内存页写入磁盘,在需要时再重新装入。结果是,所有磁盘I/O都在页层面完成。对于采用分页技术的现代操作系统而言,这也是数据在磁盘与物理内存之间往来的唯一方式。

  现代CPU包含一个称为内存管理单元(MMU)的子系统,逻辑上位于CPU与物理内存之间。该设备包含虚拟地址向物理内存地址转换时所需映射信息。当CPU引用某内存地址时,MMU负责确定该地址所在页(往往通过对地址值进行移位或屏蔽位操作实现),并将虚拟页号转换为物理页号(这一步由硬件完成,速度极快)。如果当前不存在与该虚拟页形成有效映射的物理内存页,MMU会向CPU提交一个页错误。

  页错误随即产生一个陷阱(类似于系统调用),把控制权移交给内核,附带导致错误的虚拟地址信息,然后内核采取步骤验证页的有效性。内核会安排页面调入操作,把缺失的页内容读回物理内存。这往往导致别的页被移出物理内存,好给新来的页让地方。在这种情况下,如果待移出的页已经被碰过了(自创建或上次页面调入以来,内容已发生改变),还必须首先执行页面调出,把页内容拷贝到磁盘上的分页区。

  如果所要求的地址不是有效的虚拟内存地址(不属于正在执行的进程的任何一个内存段),则该页不能通过验证,段错误随即产生。于是,控制权转交给内核的另一部分,通常导致的结果就是进程被强令关闭。

  一旦出错的页通过了验证,MMU随即更新,建立新的虚拟到物理的映射(如有必要,中断被移出页的映射),用户进程得以继续。造成页错误的用户进程对此不会有丝毫察觉,一切都在不知不觉中进行。

1.4.4 文件I/O

  文件I/O属文件系统范畴,文件系统与磁盘迥然不同。磁盘把数据存在扇区上,通常一个扇区512字节。磁盘属硬件设备,对何谓文件一无所知,它只是提供了一系列数据存取窗口。在这点上,磁盘扇区与内存页颇有相似之处:都是统一大小,都可作为大的数组被访问。

文件系统是更高层次的抽象,是安排、解释磁盘(或其他随机存取块设备)数据的一种独特方式。您所写代码几乎无一例外地要与文件系统打交道,而不是直接与磁盘打交道。是文件系统定义了文件名、路径、文件、文件属性等抽象概念。

  前一节讲到,所有I/O都是通过请求页面调度完成的。您应该还记得,页面调度是非常底层的操作,仅发生于磁盘扇区与内存页之间的直接传输。而文件I/O则可以任意大小、任意定位。那么,底层的页面调度是如何转换为文件I/O的?

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

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

  操作系统还有个页的概念,其大小或者与基本内存页一致,或者是其倍数。典型的操作系统页从2,0488,192字节不等,且始终是基本内存页大小的倍数。

  采用分页技术的操作系统执行I/O的全过程可总结为以下几步:

·         确定请求的数据分布在文件系统的哪些页(磁盘扇区组)。磁盘上的文件内容和元数据可能跨越多个文件系统页,而且这些页可能也不连续。

·         在内核空间分配足够数量的内存页,以容纳得到确定的文件系统页。

·         在内存页与磁盘上的文件系统页之间建立映射。

·         为每一个内存页产生页错误。

·         虚拟内存系统俘获页错误,安排页面调入,从磁盘上读取页内容,使页有效。

·         一旦页面调入操作完成,文件系统即对原始数据进行解析,取得所需文件内容或属性信息。

  需要注意的是,这些文件系统数据也会同其他内存页一样得到高速缓存。对于随后发生的I/O请求,文件数据的部分或全部可能仍旧位于物理内存当中,无需再从磁盘读取即可重复使用。

  大多数操作系统假设进程会继续读取文件剩余部分,因而会预读额外的文件系统页。如果内存争用情况不严重,这些文件系统页可能在相当长的时间内继续有效。这样的话,当稍后该文件又被相同或不同的进程再次打开,可能根本无需访问磁盘。这种情况您可能也碰到过:当重复执行类似的操作,如在几个文件中进行字符串检索,第二遍运行得似乎快多了。

  类似的步骤在写文件数据时也会采用。这时,文件内容的改变(通过write( ))将导致文件系统页变脏,随后通过页面调出,与磁盘上的文件内容保持同步。文件的创建方式是,先把文件映射到空闲文件系统页,在随后的写操作中,再将文件系统页刷新到磁盘。

1.4.4.1 内存映射文件

  传统的文件I/O是通过用户进程发布read( )write( )系统调用来传输数据的。为了在内核空间的文件系统页与用户空间的内存区之间移动数据,一次以上的拷贝操作几乎总是免不了的。这是因为,在文件系统页与用户缓冲区之间往往没有一一对应关系。但是,还有一种大多数操作系统都支持的特殊类型的I/O操作,允许用户进程最大限度地利用面向页的系统I/O特性,并完全摒弃缓冲区拷贝。这就是内存映射I/O,如图1-6所示。

1-6. 用户内存到文件系统页的映射

  内存映射I/O使用文件系统建立从用户空间直到可用文件系统页的虚拟内存映射。这样做有几个好处:

·         用户进程把文件数据当作内存,所以无需发布read( )write( )系统调用。

·         当用户进程碰触到映射内存空间,页错误会自动产生,从而将文件数据从磁盘读进内存。如果用户修改了映射内存空间,相关页会自动标记为脏,随后刷新到磁盘,文件得到更新。

·         操作系统的虚拟内存子系统会对页进行智能高速缓存,自动根据系统负载进行内存管理。

·         数据总是按页对齐的,无需执行缓冲区拷贝。

·         大型文件使用映射,无需耗费大量内存,即可进行数据拷贝。

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

1.4.4.2 文件锁定

  文件锁定机制允许一个进程阻止其他进程存取某文件,或限制其存取方式。通常的用途是控制共享信息的更新方式,或用于事务隔离。在控制多个实体并行访问共同资源方面,文件锁定是必不可少的。数据库等复杂应用严重信赖于文件锁定。

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

  文件锁定有两种方式:共享的和独占的。多个共享锁可同时对同一文件区域发生作用;独占锁则不同,它要求相关区域不能有其他锁定在起作用。

  共享锁和独占锁的经典应用,是控制最初用于读取的共享文件的更新。某个进程要读取文件,会先取得该文件或该文件部分区域的共享锁。第二个希望读取相同文件区域的进程也会请求共享锁。两个进程可以并行读取,互不影响。但是,假如有第三个进程要更新该文件,它会请求独占锁。该进程会处于阻滞状态,直到既有锁定(共享的、独占的)全部解除。一旦给予独占锁,其他共享锁的读取进程会处于阻滞状态,直到独占锁解除。这样,更新进程可以更改文件,而其他读取进程不会因为文件的更改得到前后不一致的结果。图1-7和图1-8描述了这一过程。

1-7. 共享锁阻断独占锁请求

1-8. 独占锁阻断共享锁请求

  文件锁有建议使用和强制使用之分。建议型文件锁会向提出请求的进程提供当前锁定信息,但操作系统并不要求一定这样做,而是由相关进程进行协调并关注锁定信息。多数Unix和类Unix操作系统使用建议型锁,有些也使用强制型锁或兼而有之。

  强制型锁由操作系统或文件系统强行实施,不管进程对锁的存在知道与否,都会阻止其对文件锁定区域的访问。微软的操作系统往往使用的是强制型锁。假定所有文件锁均为建议型,并在访问共同资源的各个应用程序间使用一致的文件锁定,是明智之举,也是唯一可行的跨平台策略。依赖于强制文件锁定的应用程序,从根子上讲就是不可移植的。

1.4.5 I/O

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

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

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

1.5 小结

  本章概述了系统层面的I/O,只是一带而过,肯定很不全面。如果需要更加详尽地了解相关内容,可以找本好的参考书,有不少的。我过去的老板Avi Silberschatz所著《操作系统概念》(第六版)(Operating System Concepts, Sixth Edition [John Wiley & Sons])是本权威的操作系统教科书,从这本书出发是个不错的选择。

  通过前面的讲述,您应当对后面各章所涵盖内容有了大致的了解。带上您的知识储备,随我前往核心主题:Java New I/ONIO)。学习了新的NIO抽象,也别忘了脑子里的具体概念。理解了这些基本概念,才能更容易地识别新类所模拟之I/O性能。

  NIO学习之旅即将启程。发动机在轰鸣,一切都已就绪。快上来吧,找个位子,舒舒服服坐好,让我们立刻开拔。

分享到:
评论
1 楼 miroku 2011-02-01  
拜读了博主的翻译,先对博主的辛勤工作表示感谢,也期待博主的后续文章

相关推荐

    Java NIO 电子书

    - **第一章:简介** - 1.1 输入输出时间与 CPU 时间 - 1.2 不再受限于 CPU - 1.3 进入主题 - 1.4 I/O 概念 - 介绍了 I/O 基础知识,包括不同类型的 I/O 操作(例如阻塞 I/O 和非阻塞 I/O)、I/O 设备如何与程序...

    Pro Java 7 NIO.2.pdf

    根据提供的文件信息,“Pro Java 7 NIO.2.pdf”由Anghel Leonard于2011年编写,主要介绍了Java 7中的新输入/输出(NIO)API,特别是NIO.2(JSR 203)所带来的增强功能。这本书通过一系列章节详细讲解了如何使用NIO.2...

    Java第八章参考资料

    尽管描述提到的是“accc7.0第一章到第七章部分代码”,但我们的重点将放在Java语言的核心概念以及如何在实际项目中应用它们。这个压缩包可能包含了从基础到进阶的Java编程示例,旨在帮助学习者巩固前七章所学知识,...

    java NIO 简介(中文)

    第一章 简介 .......................................................................................................................... 10 1.1 I/O 与 CPU 时间的比较 .......................................

    J2SE第一章到第五章习题

    第一章通常会介绍Java语言的基础概念,包括: 1. **Java的历史与特性**:Java由Sun Microsystems开发,具有跨平台性、面向对象、安全性、健壮性等特征。 2. **Java环境搭建**:讲解如何安装JDK(Java Development ...

    java课程1到7章课件java课程1到7章课件

    在这一章中,我们将介绍Java的历史、特点以及它为何成为如此流行的语言。学习者将学会安装Java开发环境,包括JDK(Java Development Kit)和集成开发环境(IDE),如Eclipse或IntelliJ IDEA。此外,还将讲解Java的...

    从NIO到Netty,编程实战出租车905协议-08172347.pdf

    第2章,介绍在Socket编程过程中一些基础知识,让大家建立起对这块知识内容的一个整体轮廓; 第3章,结合905.4-2014协议的基本内容,动手实现NIO长连接服务端的实现,以及协议内容的设计和实现思路; 第4章,实现长...

    《Java语言程序设计(进阶篇)》 课后习题第21章代码chapter21.rar

    《Java语言程序设计(进阶篇)》是深入学习Java编程的一本重要教材,其中第21章的课后习题代码集包含了丰富的Java高级特性应用实例,旨在帮助读者巩固和提升在面向对象编程、异常处理、多线程、网络编程、IO流等方面...

    第一行代码Java源代码第12章课程代码Java网络编

    【标题】"第一行代码Java源代码第12章课程代码Java网络编"涉及的是Java编程语言在网络编程方面的知识,这是Java开发中的一个重要领域。在这一章节中,开发者通常会学习如何利用Java API来实现网络通信,包括客户端与...

    java课件1到12章

    1. **第一章:Java简介** - Java的历史和发展背景 - Java的主要特点:平台无关性、安全性、健壮性、高性能 - Java的生态环境:JVM(Java虚拟机)、JDK(Java开发工具包) 2. **第二章:Java环境配置** - 下载与...

    《Java语言程序设计(进阶篇)》 课后习题第25章代码chapter25.rar

    在本压缩包“chapter25.rar”中,包含的是《Java语言程序设计(进阶篇)》一书的第25章课后习题的源代码。这是一份宝贵的资源,对于正在学习Java编程,尤其是深入阶段的学生来说,是提高编程技能和理解Java高级特性...

    java编程入门第八章输入与输出

    学习这一章的内容,对于初学者来说,主要是理解和掌握Java I/O的基本概念、流的分类和使用,以及如何进行文件操作。随着经验的积累,进一步深入学习NIO,可以提升对大规模数据处理和网络通信的理解和应用。

    《Java语言程序设计(进阶篇)》 课后习题第23章代码chapter23.rar

    《Java语言程序设计(进阶篇)》是深入学习Java编程的一本教材,其第23章的内容可能涉及了高级Java特性和编程实践。在这个压缩包"chapter23.rar"中,我们很可能会找到与这一章节相关的源代码示例,用于帮助读者理解...

    JAVA程序设计第七章 实验代码 实验答案

    在本实验中,我们将深入探讨Java程序设计的第七章,这一章主要涵盖了Java编程的高级特性和实践应用。实验代码经过测试,确保了其正确性和可用性,这为我们提供了宝贵的实践经验,有助于提升对Java语言的理解。 一、...

    JAVA书本复习题答案整合

    2. **面向对象编程**:Java的核心是面向对象编程,复习题可能包括类的创建、对象的实例化、接口、抽象类、访问修饰符、构造函数、this关键字、static关键字、以及继承和多态的深入应用。通过解答这些题目,你可以更...

    JAVA语言程序设计-第十六章 网络编程

    在Java编程领域,网络编程是不可或缺的一部分,它允许应用程序通过网络进行通信,实现数据的传输。本章将深入探讨Java中的网络编程技术,包括基本概念、API使用以及常见问题的解决。 1. Java网络编程基础 Java提供...

    Java编程入门第1章测验Java开发Java经验技巧共2

    其中包含的"Java编程入门第1章测验"部分,旨在帮助学习者检验和巩固他们在学习Java编程第一章节后的理解程度。 在Java编程的初级阶段,理解基础语法和概念至关重要。这包括但不限于: 1. **Java环境搭建**:学习...

    java第14章java-chapter14.rar

    本章将深入探讨Java的第14章,这一章节可能涵盖了Java的高级特性,如多线程、集合框架的深入理解、输入/输出流、网络编程以及可能的异常处理和反射机制。 1. **多线程**:Java中的多线程允许程序同时执行多个任务。...

    JAVA学习PPT,想学JAVA的看看啊

    第一章:JAVA简介 本章主要介绍JAVA的历史背景、特点以及其在软件开发中的广泛应用。内容可能包括JAVA的跨平台特性、“一次编写,到处运行”的理念,以及JAVA与其他编程语言的比较。 第二章:JAVA环境搭建 这一章将...

Global site tag (gtag.js) - Google Analytics