【Java基础】 磁盘I/O工作机制(二)

2 篇文章 0 订阅

磁盘I/O工作机制

1.几种访问文件的方式

读取和写入文件I/O操作都需要调用操作系统提供的接口,因为磁盘设备是通过操作系统管理的,应用程序访问物理设备只能通过系统调用的方式来工作。读和写分别对应read()和write(),只要是系统调用就可能存在内核空间地址和用户空间地址切换的问题,这是操作系统为了保护系统本身的运行安全,而将内核程序运行使用的内存空间和用户程序运行的内存空间进行隔离造成的。这样虽然可以增加了内核程序运行的安全性,也必然存在数据可能需要从内核空间向用户空间复制的问题。

磁盘I/O,数据从磁盘复制到内核空间,然后又从内核空间复制到用户空间,将会非常慢。操作系统为了加速I/O的访问,在内核空间使用缓存机制,将从磁盘读取的文件按照一定的组织方式进行缓存,如果用户程序访问的是同一段磁盘地址的空间数据,操作系统将从内核缓存中直接读取出返回给用户程序,减少I/O的响应时间。

1.1标准访问文件的方式

标准访问文件的方式就是当应用程序调用read()接口时,操作系统检查在内核的高速缓存中有没有需要的数据,如果已经有缓存,就直接从缓存中返回,如果没有,则从磁盘中读取,然后缓存在操作系统的缓存中。
写入的的方式,用户的应用程序调用write()接口将数据从用户地址空间复制到内核地址空间缓存中。这时对用户程序来说写的操作已经完成,至于什么时候再写到磁盘中由操作系统决定,除非调用了sync同步命令。
标准访问文件的方式

1.2.直接I/O的方式

直接I/O的方式就是应用程序直接访问磁盘数据,而不经过操作系统内核数据缓冲区,这样做的目的就是减少一次从内核缓冲区到用户程序缓存的数据复制。这种访问文件的方式通常是在对数据的缓存管理由应用程序实现的数据库管理系统中。
如在数据库管理系统中,系统明确地知道应该缓存哪些数据,应该失效哪些数据,还可以对一些热点数据做预加载,提前将热点数据加载到内存,可以加速数据的访问效率。
但是直接I/O也有负面影响,如果访问的数据不在应用程序缓存中,那么每次数据都会直接从磁盘进行加载,这种直接加载会非常缓慢。通常直接I/O与异步I/O结合使用,会得到比较好的性能。
直接I/O的方式

1.3.同步访问文件的方式

同步访问文件的方式比较容易理解,就是数据的读取和写入都是同步操作的,它与标准访问文件的方式不同的是,只有当数据被成功写到磁盘时才返回给应用程序成功的标志。这种访问文件的方式性能比较差,只有在一些对数据安全性要求比较高的场景中才会使用,而且通常这种操作方式的硬件都是定制的。
同步访问文件的方式

1.4.异步访问文件的方式

异步访问文件的方式就是当访问数据的线程发出请求之后,线程会接着去处理其他事情,而不是阻塞等待,当请求的数据返回后继续处理下面的操作。这种访问文件的方式可以明显地提高应用程序的效率,但是不会改变访问文件的效率。
异步访问文件的方式

1.5.内存映射的方式

内存映射的方式是指操作系统将内存中的某一块区域与磁盘中的文件关联起来,当要访问内存中的一段数据时,转换为访问文件的某一段数据。这种方式的目的同样是减少数据从内核空间缓存到用户空间缓存的数据复制操作,因为这两个空间的数据是共享的。
内存映射的方式

2.Java访问磁盘文件

数据在磁盘中的唯一最小描述就是文件,也就是说上层应用层序只能通过文件来操作磁盘上的数据,文件也是操作系统和磁盘驱动器交互的最小单元。在Java中通常的File并不代表一个真实存在的文件对象,当指定一个路径描述符时,会返回一个代表这个路径的虚拟对象,这可能是一个真实存在的文件或者是包含多个文件的目录。这样设计的原因是因为,大多数情况我们并不关心这个文件是否存在,但是我们关心对这个文件到底如何操作。
从磁盘读取文件
只有真正要读取这个文件时,才回去检查这个文件存不存在。例如FileInputStream类都是操作一个文件的接口,在创建一个FileInputStream对象时会创建一个FileDescriptor,这个对象就是真正代表一个存在的文件对象的描述。当我们在操作一个文件对象时可以通过getFD()方法获取真正操作的与底层操作系统相关联的文件描述。例如,可以通**FileDescriptor.sync()**方法将操作系统缓存中的数据强制刷新到物理磁盘中。
当传入一个文件路径时,将会根据这个路径创建一个File对象来标识这个文件,然后根据这个File对象创建真正读取文件的操作对象,这时将会真正创建一个关联真实存在的磁盘文件的文件描述符FileDescriptor,通过这个对象可以直接控制这个磁盘文件。由于我们需要读取的是字符格式,所以需要StreamDecoder类将byte解码为char格式。至于如何从磁盘驱动器上读取一段数据,操作系统会帮我们完成。至于操作系统是如何将数据持久化到磁盘及如何建立数据结构的,需要根据当前操作系统使用何种文件系统来回答。

标题2.1 java访问磁盘文件过程解析

 //创建FileReader
 FileReader fileReader = new FileReader("");
//查看FileReader构造方法
public class FileReader extends InputStreamReader 
 public FileReader(String fileName) throws FileNotFoundException {
        //使用父类InputStreamReader的构造方法
        super(new FileInputStream(fileName));
 }
class FileInputStream extends InputStream
    public FileInputStream(String name) throws FileNotFoundException {
    	//查看FileInputStream构造器 创建一个虚拟对象new File(name)
        this(name != null ? new File(name) : null);
    }
   
    public FileInputStream(File file) throws FileNotFoundException {
        String name = (file != null ? file.getPath() : null);
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkRead(name);
        }
        if (name == null) {
            throw new NullPointerException();
        }
        //判断文件路径是否有效
        if (file.isInvalid()) {
            throw new FileNotFoundException("Invalid file path");
        }
        // 创建一个真实文件的对象
        fd = new FileDescriptor();
        fd.attach(this);
        path = name;
        open(name);
    }

FileInputStream 创建对象的时候,会对 File 构造器的路径参数进行检查,判断路径对应的文件到底存在与否;并且还会创建一个代表真实文件的 FileDescriptor 对象

//查看InputStreamReader构造器
public class InputStreamReader extends Reader
    public InputStreamReader(InputStream in) {
        //调用父类构造器加锁
        super(in);
        try {
        //StreamDecoder解码对象,将字节解码为字符 在 I/O(一)类库基本架构里有写过
            sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
        } catch (UnsupportedEncodingException e) {
            // The default encoding should always be available
            throw new Error(e);
        }
    }
public abstract class Reader implements Readable, Closeable {
//构造器加锁
    protected Reader(Object lock) {
        if (lock == null) {
            throw new NullPointerException();
        }
        this.lock = lock;
    }

这里我看我看一下FileReader里的read()方法 发现调用的是父类InputStreamReader的read方法

public class InputStreamReader extends Reader
    private final StreamDecoder sd;
    public int read() throws IOException {
        //调用的是StreamDecoder的read方法
        return sd.read();
    }
public class StreamDecoder extends Reader
    public int read() throws IOException {
        //调用read0方法
        return this.read0();
    }
    //字节到字符的转换
        private int read0() throws IOException {
        synchronized(this.lock) {
            if (this.haveLeftoverChar) {
                this.haveLeftoverChar = false;
                return this.leftoverChar;
            } else {
                char[] var2 = new char[2];
                int var3 = this.read(var2, 0, 2);
                switch(var3) {
                case -1:
                    return -1;
                case 0:
                default:
                    assert false : var3;

                    return -1;
                case 2:
                    this.leftoverChar = var2[1];
                    this.haveLeftoverChar = true;
                case 1:
                    return var2[0];
                }
            }
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值