内存映射文件

简介: 内存映射文件

基本思想

进程通过系统调用将一个文件或文件的部分映射到进程的虚拟地址空间的一部分,访问这个文件就像访问内存中的一个大数组,而不是对文件进行读写。

实现

在多数实现中,在影射共享的页面时不会涉及读入页面的内容,而是在访问页面时页面才会被每次一页读入,磁盘文件则被当做后备存储。

分类

内存映射文件主要有两个分类:持久化(Persisted)和非持久化(Non-persisted)

持久化

持久化文件时,当进程退出或显示地解除文件映射时,所有修改页面会写回映射的文件中。

非持久化

非持久化时,文件并不关联硬盘文件。当关闭内存映射文件时,所有数据被抛弃。非持久化适用于进程通信的共享内存。

在Windows操作系统中,不需要调用CreateFile。调用CreateFileMapping时,将INVALID_HANDLE_VALUE作为hFile参数传入,这表示创建的文件映射不是磁盘文件,而是页交换文件。

图例

Image.png

应用场景

大文件读写

如果是小文件,那么内存映射文件会导致碎片空间浪费,因为内存映射以页为单位加载数据,一般内存中一页通常至少要4KB。因此一个5KB的文件需要映射到内存需要两页,也就是8KB,从而浪费了3KB内存空间。

操作系统加载进程

多进程共享内存

Java对内存映射的支持

FileChannel
代码示例:

public class DiskHelper {
    private final String fileName;
    private final long fileSize;
    private FileChannel fileChannel;
    private MappedByteBuffer mappedByteBuffer;

    public DiskHelper(final String fileName, final long fileSize) throws IOException {
        this.fileName = fileName;
        this.fileSize = fileSize;

        // 内存映射
        File file = new File(fileName);
        try {
            this.fileChannel = new RandomAccessFile(file, "rw").getChannel();
            this.mappedByteBuffer = this.fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileChannel != null) {
                this.fileChannel.close();
            }

        }
    }

    public byte[] read(long offset, int length) {
        ByteBuffer buffer = mappedByteBuffer.slice();
        buffer.position((int) offset);
        byte[] bytes = new byte[length];
        buffer.get(bytes);
        buffer.clear();
        return bytes;
    }

    public void write(byte[] bytes, long offset) {
        ByteBuffer buffer = mappedByteBuffer.slice();
        buffer.position((int) offset);
        buffer.put(bytes);
        mappedByteBuffer.force();
    }

    public void write(byte[] bytes, int offset, int length) {
        ByteBuffer buffer = mappedByteBuffer.slice();
        buffer.position(offset);
        buffer.put(bytes, 0, length);
        mappedByteBuffer.force();
    }

    public void shutdown() {
        try {
            if (fileChannel != null) {
                fileChannel.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

测试用例

public class DiskHelperTest {

    @Test
    public void testCreateReadWrite() throws IOException {
        DiskHelper diskHelper = new DiskHelper("test", 1024L * 1024);
        String test = "abcd";
        diskHelper.write(test.getBytes(StandardCharsets.UTF_8), 0L);

        byte[] content = diskHelper.read(0, 4);
        System.out.println(new String(content, StandardCharsets.UTF_8));
        diskHelper.shutdown();
    }
}

参考

  1. https://zh.wikipedia.org/wiki/%E5%86%85%E5%AD%98%E6%98%A0%E5%B0%84%E6%96%87%E4%BB%B6
  2. 操作系统原理-内存映射文件 陈向群
目录
相关文章
|
24天前
|
Python
python3获取内存和cpu利用率记录日志文件psutil
python3获取内存和cpu利用率记录日志文件psutil
30 1
|
9天前
|
Java 程序员 Linux
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
14 0
|
9天前
|
监控 Linux
深入了解Linux的pmap命令:进程内存映射的利器
`pmap`是Linux下分析进程内存映射的工具,显示内存区域、权限、大小等信息。通过`/proc/[pid]/maps`获取数据,特点包括详细、实时和灵活。参数如`-x`显示扩展信息,`-d`显示设备。示例:`pmap -x 1234`查看进程1234的映射。注意权限、实时性和准确性。结合其他工具定期监控,排查内存问题。
有 3 个进程 P1、P2、P3 协作解决文件打印问题。P1 将文件记录从磁盘读入内存的缓冲区 1,每执行一次读一个记录 ;P2 将缓冲区 1 中的内容复制到缓冲区 2 中,每执行一次复制一个记录 ;
有 3 个进程 P1、P2、P3 协作解决文件打印问题。P1 将文件记录从磁盘读入内存的缓冲区 1,每执行一次读一个记录 ;P2 将缓冲区 1 中的内容复制到缓冲区 2 中,每执行一次复制一个记录 ;
|
2月前
|
Windows
虚拟机内存越用越少,即使文件都永久删除了!!!
虚拟机内存越用越少,即使文件都永久删除了!!!
|
2月前
内存映射mmap拓展
内存映射mmap拓展
|
2月前
内存映射实现无血缘关系进程间通信
内存映射实现无血缘关系进程间通信
|
2月前
内存映射实现父子进程通信
内存映射实现父子进程通信
|
2月前
|
存储 算法 内存技术
深入理解操作系统内存管理:从虚拟内存到物理内存的映射
【4月更文挑战第30天】 在现代操作系统中,内存管理是一个复杂而关键的功能。它不仅确保了系统资源的有效利用,还为每个运行的程序提供了独立的地址空间,保障了程序之间的隔离性和安全性。本文将探讨操作系统如何通过分页机制和虚拟内存技术实现内存的抽象化,以及这些技术是如何影响应用程序性能的。我们将详细解析虚拟地址到物理地址的转换过程,并讨论操作系统在此过程中扮演的角色。文章的目的是为读者提供一个清晰的框架,以便更好地理解内存管理的工作原理及其对系统稳定性和效率的影响。
|
2月前
|
算法 安全 Linux
Linux 下共享内存方式 :System V共享内存、共享文件映射(mmap)、POSIX共享内存对比...
Linux 下共享内存方式 :System V共享内存、共享文件映射(mmap)、POSIX共享内存对比...
48 2