Java文件读写原理和虚拟内存
1,内核空间和用户空间
上图中的储户是没法直接从金库中存钱获取取钱的,如果这么做了,那么就非法了。这里用户空间相当于储户,内核空间相当于银行职员,而硬盘相当于金库,也就是用户空间中的进程没法直接操作读写硬盘中的数据,我们需要通过内核空间来处理。
空间
2,普通IO操作
下图为普通IO的执行原理:
根据上图,当进程请求一个I/O操作,它会执行一个系统(open() , read() , writer() , close())调用将控制权移交给内核。
当内核以这种方式被调用,它随即采取任何必要步骤,找到进程所需数据,并把数据传送到用户空间内指定的缓冲区中,这时常规进程就可以对缓冲区中的数据处理操作了,而内核试图对数据进行高速缓存或预读取,因此进程所需数据可能已经在内核空间里了,如果是这样,该数据只需简单地拷贝出来即可,如果数据不在内核空间,则进程被挂起,内核着手把数据读进内场。
2.1 DMA是什么
DMA(Direct Memory Access),即直接存储器存取,是一种快速传送数据的机制。数据传递可以从适配卡到内存,从内存到适配卡或从一段内存到另一段内存。
2.2 Java实现文件读取和写入过程解析
需求说明:
实际操作过程中,从D盘根目录下的ak.txt读取文件写入D盘根目录下的hello.txt文件内
实现思路:
写两个方法,一个用于读取目标文件,一个用于写入目标文件–详情见代码注释
代码内容: 文件读取和写入练习
package con.cy.basic;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Lxy {
/**
* IO 测试
*
* @param args
*/
public static void main(String[] args) {
//实例化对象
Lxy lxy = new Lxy();
//用一个byte[]接受数据
byte[] bytes = lxy.read("/Users/longxiangyu/Desktop/CGB2012/Three/Test1.txt");
//将接受到的数据传入写入方法
lxy.write("/Users/longxiangyu/Desktop/CGB2012/Three/Test2.txt", bytes);
}
//读取方法,设定传参是文件的String路径,返回一个byte[]数组
private byte[] read(String str) {
byte[] bytes = new byte[0];
FileInputStream input = null;
try {
input = new FileInputStream(str);
int read;
bytes = new byte[1024];
System.out.println("内容读取中...........");
while ((read = input.read(bytes)) != -1) {
for (int i = 0; i < read; i++) {
System.out.print((char) bytes[i]);
}
System.out.println("\n内容读取完毕");
}
return bytes;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return bytes;
}
//写入方法,需要两个参数,一是写入路径,一是写入内容
private void write(String str, byte[] bytes) {
System.out.println("文件写入中-----");
FileOutputStream output = null;
try {
output = new FileOutputStream(str);
try {
output.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("文件写入完毕");
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行结果
运行前:
运行后:
2.3 Q&A
Q:数据从内核空间拷贝到用户空间似乎多余,为什么不直接让磁盘把数据送到用户空间的缓冲区呢?
- 硬盘通常不能直接访问用户空间
- 磁盘基于块存储的硬件设备操作的固定大小的数据块,用户进程请求的可能是任意大小或者非对齐的数据块,在这两者数据交互过程中内核负责数据的分解、再组合工作,起到一个中间人的角色。
2.4 总结
当应用程序需要读取文件的时候,内核首先通过DMA技术将文件内容从磁盘读入内核中的buffer,然后Java应用进程再从内核的buffer(缓冲区)将数据读取到应用程序的buffer(缓冲区)。也就是有两次的文件复制
3,虚拟内存
3.1 什么是虚拟内存
为了提升I/O效率和处理能力,操作系统采用虚拟内存的机制。虚拟内存意为使用虚假(或虚拟)地址取代物理(硬件RAM)内存地址。
3.2 虚拟内存的好处
- 一个以上的虚拟地址可指向同一个物理内存地址。
- 虚拟内存空间可大于实际可用的硬件内存
这样做的好处是省去了内核与用户空间的往来拷贝。
3.2.1 一个以上的虚拟地址可指向同一个物理内存地址
在进行IO操作时就可以将用户空间的buffer区和内核空间的buffer区指向同一个物理内存。这样用户空间的程序就不需要再去内核空间再取回数据,而是可以直接访问,节省内存空间。
3.2.2 虚拟内存空间可大于实际可用的硬件内存
当用户程序访问内存地址时,一般的操作如下:
首先虚拟内存系统会到物理内存去查找该虚拟地址是否存在。
如果存在,如A1,则直接从物理内存中读取;
如果不存在,如A4则会抛出一个信号。这时虚拟内存系统会去磁盘空间中找,找到后再按一定的策略,将其置入到内存中。
如将B2和A4交换,然后由用户程序就可以使用A4中的数据。这样就保证了用户程序可以读取一些大型的文件。
从本质上说,物理内存充当了分页区的高速缓存;而所谓分页区,即从物理内存置换出来,转而存储于磁盘上的内存页面.
把内存页大小设定为磁盘块大小的倍数,这样内核就可直接向磁盘控制硬件发布命令,把内存页写入磁盘,在需要时再重新装入。结果是,所有磁盘 I/O 都在页层面完成。对于采用分页技术的,现代操作系统而言,这也是数据在磁盘与物理内存之间往来的唯一方式
3.3 虚拟内存的机制
- 虚拟内存需要重新映射到物理内存
- 虚拟地址映射到物理内存中的实地址
- 每次只有进程的少量代码在物理内存中运行
- 大部分进程代码位于存储器(交换区)中
4,内存管理单元
4.1 什么是内存管理单元
负责管理计算机内存系统的计算机硬件称为内存管理单元(MMU)。现代 CPU 包含一个称为内存管理单元(MMU)的子系统,逻辑上位于CPU 与物理内存之间。该设备包含虚拟地址向物理内存地址转换时所需映射信息。
4.2 内存管理单元的作用
该组件充当CPU和系统内存之间的缓冲区,MMU通常是CPU的一部分,本身有少量存储空间存放从虚拟地址到物理地址的匹配表。此表称作TLB(转换旁置缓冲区)。所有数据请求都送往MMU,由MMU决定数据是在RAM内还是在大容量存储器设备内。如果数据不在存储空间内,MMU将产生页面错误中断。
MMU的两个主要功能是:
- 将虚地址转换成物理地址。
- 控制存储器存取允许。MMU关掉时,虚地址直接输出到物理地址总线。 内存管理单元执行的功能通常可分为三个方面:
- 硬件内存管理
- 操作系统内存管理
- 应用程序内存管理
虽然内存管理单元可以是一个独立的芯片组件,但它通常集成在中央处理器(CPU)中。
操作系统利用内存管理单元能够实现以下功能:
- 虚拟内存:有了虚拟内存,可以在处理器上运行比实际物理内存大的应用程序。为了使用虚拟内存,操作系统通常要设置一个交换分区(通常时硬盘),通过将内存中不活跃的数据放入交换分区以腾出物理内存来为其他的程序服务。
- 内存保护:将特定的内存块设置为读、写和可执行属性。
当 CPU 引用某内存地址时,MMU(内存管理单元)负责确定该地址所在页(往往通过对地址值进行移位或屏蔽位操作实现),并将虚拟页号转换为物理页号(这一步由硬件完成,速度极快)。如果当前不存在与该虚拟页形成有效映射的物理内存页,MMU会向CPU 提交一个页错误。
页错误随即产生一个陷阱(类似于系统调用),把控制权移交给内核,附带导致错误的虚拟地址信息,然后内核采取步骤验证页的有效性。内核会安排页面调入操作,把缺失的页内容读回物理内存。这往往导致别的页被移出物理内存,好给新来的页让地方。在这种情况下,如果待移出的页已经被碰过了(自创建或上次页面调入以来,内容已发生改变),还必须首先执行页面调出,把页内容拷贝到磁盘上的分页区。
如果所要求的地址不是有效的虚拟内存地址(不属于正在执行的进程的任何一个内存段),则该页不能通过验证,段错误随即产生。于是,控制权转交给内核的另一部分,通常导致的结果就是进程被强令关闭。
一旦出错的页通过了验证,MMU 随即更新,建立新的虚拟到物理的映射(如有必要,中断被移出页的映射),用户进程得以继续。造成页错误的用户进程对此不会有丝毫察觉,一切都在不知不觉中进行