缓冲区操作

进程执行I/O操作,归结起来就是向操作系统发出请求,它要么把缓存区例的数据排干(写),要么用数据把数据区填满(读)。进程使用这一机制处理所有数据进出操作。



进程使用read()系统调用,要求其缓存区被填满。内核随即向磁盘控制器发出命令,要求其从磁盘读取数据。通过DMA技术直接将磁盘中的数据写入内核内存缓存区,一旦磁盘控制器把缓存区填满,内存立即把数据从内核空间的里你是缓冲区拷贝到进程执行read()调用时指定的缓冲区。

发散/汇聚

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

利用虚拟内存避免一些拷贝

前面提到设备控制器不能使用DMA直接存储到用户空间,需要从内核空间拷贝到用户空间。但在使用内存多重映射技术可以避免这种拷贝。

现代操作系统都使用虚拟内存,它有极大优点:

  1. 多个虚拟地址可以映射到同一个物理地址。
  2. 虚拟内存空间可能大于实际可用的硬件内存。



借助虚拟内存的特点,将内核空间中的缓冲区的虚拟地址和用户空间的缓冲区虚拟地址映射到一个物理地址(即内存多重映射技术)。

但这也是有前提的,内核与用户缓冲区必须使用相同的页对齐,缓冲区的大小还必须是磁盘控制块大小的倍数。

采用分页技术的操作系统执行IO操作

  1. 确定请求的数据分布在文件系统的哪些页,这些页不一定都是连续的
  2. 在内核空间种分配足够的页,以容纳文件系统页
  3. 在内存页与磁盘上的文件系统页之间建立映射
  4. 为每个页产生一个缺页异常
  5. 虚拟内存系统俘获缺页异常,调用相应的缺页处理程序,将文件系统页调入主存
  6. 页面调入成功后,文件系统队原始数据进行解析,获取文件内容和属性信息。

文件系统页也会和其它内存页一样被缓存在主存z

内存映射文件



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

当用户进行触碰到映射内存空间时,会自动产生页错误,从而将文件系统从磁盘读进主存。如果用户修改了映射内存空间时,相应的页会被标记为脏页,随后就会将更改持久化到磁盘。

其优点:

  1. 用户进程直接将文件数据当作内存。
  2. 自动产生页错误,将文件数据从磁盘读入主存
  3. 操作系统的虚拟内存子系统可以对这些页进行智能高速缓存
  4. 数据总是按页对齐的
  5. 大型文件使用映射可以节约内存。

文件锁定机制

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

文件锁定的锁定区域可以是整个文件也可ui细致到单个字节。

共享锁和独占锁

多个共享锁可以同时对同一文件区域发生作用;独占锁要求相关区域不能有其它锁定在起作用。

共享锁和独占锁的经典应用 --- 控制读取共享文件的更新

某个进程要读取文件,就要先取得相关区域的共享锁。其它希望读取相同文件区域的进程也会请求共享锁。多个进程得以并行读取,互不影响。如果在此时有其它进行想要更新文件,那么它需要请求独占锁,然后该进行会进入阻滞状态,直到既有锁定(共享的,独占的)全部解除它才能拿到独占锁。一旦该进程拿到了独占锁,其它所有的共享锁读取线程间进入阻滞状态,直到独占锁解除。

流I/O

并非所有的I/O都是面向块的,也有流I/O,其原理模仿了通道。I/O流字节必须顺序读取。流的传输一般比块设备慢,进程用于间歇性输入。

Buffer类

一个Buffer对象是固定容量的数据的容器。在这里数据可以被存储并在之后用于检索。

Buffer类的层次图

属性

    private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
  1. 容量(capacity)
  2. 上界(limit)

    缓冲区第一个不能被读或写的元素。或者说是现存元素的计数。其指明了缓冲区中有效内容末端的位置。
  3. 标记(mark)

    一个备忘的位置。使用mark()来设定mark=postion.调用reset()设定position=mark

    这四个属性之间遵循的关系为:

    0<= mark <= position <= limit <= capacity

重要方法

put()

put方法就是将元素加入缓冲区,值得注意的是,put方法只改变position,不会改变limit和capacity。

flip()

    public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}

filp()方法将一个能够继续添加数据的填充状态的缓冲区翻转为一个准备读出元素的释放状态。根据代码filp()所作的工作不言而喻。

rewind()

rewind方法和flip方法非常类似,但是它不会影响上界属性,可以利用rewind方法来重新读已经被翻转的缓冲区中的数据。

hasRemaining()

        //切换到读模式
buffer.flip();
int count=buffer.remaining();
System.out.println("当前位置距离上界还有:"+count); while (buffer.hasRemaining()){
System.out.print(buffer.get()+" ");
}

hasRemaining()可以判断当前位置是否已经达到buffer的上界。remaining()可以获取当前位置与上界的距离。

compact()

当需要从buffer中释放一部分已经被读取过的数据时,可以使用compact方法,他会将为读的数据元素下移动使得第一个元素的索引为0.该方法在复制数据的场景下,比使用get方法和put方法更加的高效。

关于标记的一些注意点

在未设置标记之前,mark是等于-1的。此时如果调用reset()会抛出InvalidMarkExceptioin异常。值得注意的是由许多方法都是会将mark重置为-1的。如:rewind(),flip(),clear()等。

缓冲区相等

两个缓冲区相等的条件:

  1. 对象类型相同
  2. 两个对象剩余同样的元素(剩余是指position到limit之间的元素)

被认为相等的两个缓冲区

批量的移动

以CharBuffer为例子

public CharBuffer get(char[] dst)
//offset参数是填充dst的起点位置
public CharBuffer get(char[] dst,int offset,int lenth); public final CharBuffer put(char[] src)
public CharBuffer put(char[] src,int offset,int length)
public CharBUffer put(CharBuffer src)

可以使用以下方法高效的读取处理数据

    buffer.flip();
int[] arr=new int[10];
while (buffer.hasRemaining()){
int len=Math.min(arr.length,buffer.remaining());
buffer.get(arr,0,len);
//处理数据
processData(arr,len);
}

缓存区创建的两个关键方法

public static CharBuffer allocate(int capacity)

public static CharBuffer Wrap(char[] array)
//offset参数用来初始化position参数,length参数用来初始化limit参数
public static CharBuffer wrap(char[] array,int offset,int length)

创建新的缓存区有两种方式,分别是分配或包装操作。

allocate方法采用的分配的方式,他会分类一个数组来存储数据。而wrap方法是将一个数组包装为一个缓冲区。这意味着对这个数组的任何改动都会对这个缓冲区可见。

复制缓冲区

public abstract CharBuffer duplicate();
public abstract CharBuffer asReadOnlyBuffer();
public abstract CharBuffer slice();

使用duplicate方法可以创建一个和原缓冲区共享数据元素的副本缓冲区。它们共享数据元素但是拥有各自的位置,上界和标记属性。

使用asReadOnlyBuffer方法可以创建一个只读的副本缓冲区。它所创建的副本是不允许使用put方法的。

使用slice方法可以创建一个position到limit的元素的一个副本。

值得注意的是以上三种方法都不会在堆中重新分配空间用来存储数据。所以它们都是复制缓冲区的方法。

字节缓冲区

字节顺序

字节顺序分为大端存储和小端存储

大端:

高位存放在内存的低地址位

小端:

低位存放在内存的低地址位

使用ByteOrder order()方法可以获得该缓冲区的字节顺序;使用ByteBuffer order(ByteOrder bo) 方法可以修改缓冲区的字节顺序。

直接缓冲区

字节缓冲区的一大特点就是它可以是直接缓冲区。它可以成为通道所执行的I/O的源头或目标。

非直接缓冲区:非直接缓冲区将缓冲区建立在JVM内存在中。

非直接缓冲区:直接将缓冲区建立在物理内存中,可以提高效率。

        //分配直接缓冲区
ByteBuffer bf=ByteBuffer.allocateDirect(10);
//判断其是否是直接缓冲区,结果是true
System.out.println(bf.isDirect());

视图缓冲区

ByteBuffer类允许创建视图来将byte型缓冲区字节数据映射位其它的原始数据类型。视图对象维护它自己的容量、位置、上界和标记,但是和原来的缓冲区共享数据元素。

        ByteBuffer bf=ByteBuffer.allocate(10);
IntBuffer intBuffer=bf.asIntBuffer();

Java NIO 学习笔记一的更多相关文章

  1. Java NIO学习笔记

    Java NIO学习笔记 一 基本概念 IO 是主存和外部设备 ( 硬盘.终端和网络等 ) 拷贝数据的过程. IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成. 所有语言运行时系统提供执 ...

  2. 零拷贝详解 Java NIO学习笔记四(零拷贝详解)

    转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...

  3. Java NIO 学习笔记(七)----NIO/IO 的对比和总结

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  4. Java NIO 学习笔记(六)----异步文件通道 AsynchronousFileChannel

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  5. Java NIO 学习笔记(五)----路径、文件和管道 Path/Files/Pipe

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  6. Java NIO 学习笔记(四)----文件通道和网络通道

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  7. Java NIO 学习笔记(三)----Selector

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  8. Java NIO 学习笔记(二)----聚集和分散,通道到通道

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  9. Java NIO 学习笔记(一)----概述,Channel/Buffer

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  10. JAVA NIO学习笔记1 - 架构简介

    最近项目中遇到不少NIO相关知识,之前对这块接触得较少,算是我的一个盲区,打算花点时间学习,简单做一点个人学习总结. 简介 NIO(New IO)是JDK1.4以后推出的全新IO API,相比传统IO ...

随机推荐

  1. 解决报错Fatal error in launcher

    换电脑重装python,打算安装第三方库的时候出现错误: Fatal error in launcher 然而在网上搜到的大多数是解决 —— Fatal error in launcher: Unab ...

  2. Java文章翻译

    一.基础 1.String 使用" "还是构造函数创建字符串? 画图说明字符串的不变性 在Java中字符串为什么是不可变的 Java中的字符串是按引用传递? 排名前十的Java字符 ...

  3. SpringBoot异步编程

    异步调用:当我们执行一个方法时,假如这个方法中有多个耗时的任务需要同时去做,而且又不着急等待这个结果时可以让客户端立即返回然后,后台慢慢去计算任务.当然你也可以选择等这些任务都执行完了,再返回给客户端 ...

  4. 如何使用 Issue 管理软件项目?

    软件开发(尤其是商业软件)离不开项目管理,Issue 是最通用的管理工具之一.

  5. 《MIT 6.828 Lab 1 Exercise 11》实验报告

    本实验的网站链接:MIT 6.828 Lab 1 Exercise 11. 题目 The above exercise should give you the information you need ...

  6. 自然语言处理工具HanLP-基于层叠HMM地名识别

    本篇接上一篇内容<HanLP-基于HMM-Viterbi的人名识别原理介绍>介绍一下层叠隐马的原理. 首先说一下上一篇介绍的人名识别效果对比: 1. 只有Jieba识别出的人名 准确率极低 ...

  7. orcale数据库授权码

    Product Code:4t46t6vydkvsxekkvf3fjnpzy5wbuhphqzserial Number:601769password:xs374ca

  8. IDEA下tomcat启动后 server乱码,Tomcat Catalina Log乱码问题的解决

    如果你初接触Idea,一定会遇到控制台乱码的问题,这里和eclipse有点不一样,看如下办法: 乱码的根本原因:Windows系统的cmd是GBK编码的,所以IDEA的下方log输出的部分的编码也是G ...

  9. @WebServlet注解

    @WebServlet("/LoginServlet") jsp页面: <form action="LoginServlet" method = &quo ...

  10. # localhost 、217.0.0.1 、本机IP

    localhost .217.0.0.1 .本机IP localhost是一个域名,性质跟 "www.baidu.com" 差不多,指向127.0.0.1这个IP地址,在windo ...