内存映射文件

内存映射文件

内存映射文件简介

内存映射文件(Memory-Mapped File)是一种将文件或其他对象映射到进程地址空间的机制,使得应用程序可以像访问内存一样访问文件内容。以下是内存映射文件的主要特点和工作原理:

主要特点

  1. 直接内存访问
    • 应用程序可以通过指针直接访问文件内容,而不需要通过传统的读写操作。这提高了文件访问的效率。
  2. 共享内存
    • 内存映射文件可以被多个进程共享,使得它们可以同时访问同一个文件。这在进程间通信(IPC)中非常有用。
  3. 延迟加载
    • 文件的内容并不是立即加载到内存中,而是在实际访问时才加载。这可以节省内存资源。
  4. 自动同步
    • 对内存映射区域的修改会自动反映到文件中,反之亦然。这意味着对内存的更改会直接影响到文件内容。

工作原理

  1. 创建内存映射文件

    • 操作系统提供的 API(如 Windows 的 CreateFileMappingMapViewOfFile)用于创建内存映射文件。
  2. 映射文件到内存

    • 通过将文件的某个部分映射到进程的虚拟地址空间,进程可以使用指针来访问文件内容。
  3. 访问和修改

    • 应用程序可以像访问普通数组一样访问映射的内存区域。对该区域的写入操作会直接修改文件内容。
  4. 解除映射

    • 当不再需要访问文件时,可以使用 API 解除映射,并释放相关资源。

优点

  1. **高性能**:由于数据直接在内存中访问,避免了频繁的磁盘I/O操作,因此读写速度非常快。
  2. **简单易用**:使用内存映射文件可以像操作普通内存一样操作文件内容,简化了代码逻辑。
  3. **节省内存**:只有实际访问的数据才会被加载到内存中,未访问的部分不会占用内存。
  4. **共享内存**:多个进程可以同时映射同一文件的不同部分,实现进程间的共享内存通信。

缺点

  1. **内存限制**:虽然内存映射文件可以映射大文件,但实际使用的物理内存仍然受限于系统的可用内存。
  2. **复杂性**:相对于传统的文件读写操作,内存映射文件的使用可能稍微复杂一些,需要理解虚拟内存和页面管理的概念。
  3. **数据一致性**:在多进程或多线程环境中,需要额外的同步机制来保证数据的一致性。

应用场景

  • 大文件处理:在处理大文件时,内存映射文件可以避免将整个文件加载到内存中。
  • 数据库:许多数据库系统使用内存映射文件来提高数据访问速度。
  • 进程间通信:通过共享内存映射文件,多个进程可以高效地交换数据。

示例

读取内存映射文件

using System;
using System.IO;
using System.IO.MemoryMappedFiles;

class Program
{
    static void Main()
    {
        string filePath = "path/to/your/file.txt";
        long fileSize = new FileInfo(filePath).Length;

        // 创建内存映射文件
        using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open))
        {
            // 创建视图访问器
            using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor(0, fileSize))
            {
                // 读取文件内容
                byte[] buffer = new byte[fileSize];
                accessor.ReadArray(0, buffer, 0, buffer.Length);

                // 将字节数组转换为字符串
                string content = System.Text.Encoding.UTF8.GetString(buffer);
                Console.WriteLine(content);
            }
        }
    }
}

写入内存映射文件

using System;
using System.IO;
using System.IO.MemoryMappedFiles;

class Program
{
    static void Main()
    {
        string filePath = "path/to/your/file.txt";
        string content = "Hello, Memory-Mapped Files!";

        // 创建内存映射文件
        using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(filePath, FileMode.Create, maximumSize: (long)content.Length))
        {
            // 创建视图访问器
            using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor(0, content.Length))
            {
                // 将字符串转换为字节数组
                byte[] buffer = System.Text.Encoding.UTF8.GetBytes(content);

                // 写入数据
                accessor.WriteArray(0, buffer, 0, buffer.Length);
            }
        }
    }
}

这段代码演示了如何创建和使用内存映射文件来存储和读取数据。

读取测试

这里我们分别使用IO的方式和 MMF方式来读取一个1G多的模型文件。IO方式耗时2000ms,MMF方式560ms

测试代码

[Button("TestReadByMemoryMappedFile")]
    void TestReadByMemoryMappedFile()
    {
        using (CustomTimer timer = new CustomTimer("ReadByMemoryMappedFile"))
        {
            ReadByMemoryMappedFile();
        }

        using (CustomTimer timer = new CustomTimer("ReadByIO"))
        {
            ReadByIO();
        }
    }

    private void ReadByIO()
    {
        string filePath = "E:\\A.ply";

        // 确保文件存在
        if (!System.IO.File.Exists(filePath))
        {
            Debug.Log("文件不存在: " + filePath);
            return;
        }

        // 读取文件内容
        byte[] buffer = System.IO.File.ReadAllBytes(filePath);

        // 将字节数组转换为字符串并输出
        string fileContent = System.Text.Encoding.UTF8.GetString(buffer);
        Debug.Log(fileContent.Length);
    }


    void ReadByMemoryMappedFile()
    {
        string filePath = "E:\\A.ply";

        // 确保文件存在
        if (!System.IO.File.Exists(filePath))
        {
            Debug.Log("文件不存在: " + filePath);
            return;
        }

        // 获取文件大小
        long fileSize = new System.IO.FileInfo(filePath).Length;

        // 创建内存映射文件
        using var mmf = System.IO.MemoryMappedFiles.MemoryMappedFile.CreateFromFile(filePath, System.IO.FileMode.Open, "PlyMap", fileSize);
        // 创建视图流
        using var stream = mmf.CreateViewStream();
        // 读取文件内容
        byte[] buffer = new byte[fileSize];
        stream.Read(buffer, 0, buffer.Length);

        // 将字节数组转换为字符串并输出
        string fileContent = System.Text.Encoding.UTF8.GetString(buffer);
        Debug.Log(fileContent.Length);
    }

测试结果

内存映射文件读取文件

内存映射文件与传统IO方式的区别

1. 访问方式

  • 传统读取
    • 你需要通过文件操作函数(如 Open, Read, Write, Close)逐步读取文件。
    • 每次读取时,操作系统会从磁盘将数据加载到内存,过程较为繁琐。
  • 内存映射文件
    • 文件的内容被直接映射到进程的地址空间,你可以像访问普通数组一样访问文件内容。
    • 你只需简单地使用指针或索引来读取和修改数据。

2. 性能

  • 传统读取
    • 每次读取都涉及系统调用,开销较大,尤其是处理大文件时会频繁进行磁盘 I/O 操作,可能导致性能瓶颈。
  • 内存映射文件
    • 通过内存直接访问,减少了磁盘 I/O 的次数,通常能提供更好的性能,特别是在处理大文件或频繁读写时。

3. 内存管理

  • 传统读取
    • 你需要手动管理内存,读取的数据可能需要存储在一个数组或缓冲区中,使用完后需要释放。
  • 内存映射文件
    • 操作系统自动管理内存映射区域,内存不足时会自动将不常用的数据换出,保持高效的内存使用。

4. 共享和进程间通信

  • 传统读取
    • 如果多个进程需要访问同一文件,通常需要使用文件锁或其他机制来进行协调,复杂度较高。
  • 内存映射文件
    • 可以轻松实现多个进程共享同一个内存映射文件,进程间可以直接访问同一块内存区域,简化了进程间通信。

5. 延迟加载

  • 传统读取
    • 通常需要一次性读取整个文件或分块读取,可能浪费内存。
  • 内存映射文件
    • 只有在实际访问时,相关数据才会被加载到内存中,避免了不必要的内存占用。

读书

将内存映射文件和传统 I/O 比作读一本书,可以通过以下几个方面来理解它们之间的区别:

1. 获取方式

  • 内存映射文件
    • 就像把整本书的内容直接放在桌面上,你可以随时打开、查看任何一页,而不需要去书架上拿书。
    • 这种方式允许你直接访问内存中的数据,速度快。
  • 传统 I/O
    • 需要每次去书架上取书,查阅时逐页翻阅。
    • 每次读取内容都需要进行系统调用,速度较慢。

2. 访问方式

  • 内存映射文件
    • 你可以随机访问书中的任意一页,随时跳转到任何章节,效率高。
    • 内存中的数据可以被多个进程共享,方便快速访问。
  • 传统 I/O
    • 需要按顺序逐页查阅,不能快速跳转,效率低。
    • 每次读取都需要从磁盘加载数据,可能会造成延迟。

3. 性能

  • 内存映射文件
    • 由于数据已经在内存中,访问速度快,减少了磁盘 I/O 操作。
    • 适合处理大文件和需要频繁访问的场景。
  • 传统 I/O
    • 每次读取都涉及到磁盘操作,性能较低,尤其是在处理大文件时。
    • 适合小文件或简单的读取操作。

4. 资源管理

  • 内存映射文件
    • 操作系统管理内存映射,自动处理数据的加载和卸载。
    • 更高效地利用内存,减少了内存和磁盘之间的复制。
  • 传统 I/O
    • 程序员需要手动管理文件的打开、读取和关闭,容易出错。
    • 可能导致资源浪费,例如未及时关闭文件。

总结

内存映射文件

内存映射文件百科

内存映射文件视频

Microsoft MMP

posted @   世纪末の魔术师  阅读(40)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
  1. 1 ありがとう··· KOKIA
ありがとう··· - KOKIA
00:00 / 00:00
An audio error has occurred.

作词 : KOKIA

作曲 : KOKIA

编曲 : 日向敏文

作词 : KOKIA

作曲 : KOKIA

誰もが気付かぬうちに

誰もが気付かぬうちに

何かを失っている

フッと気付けばあなたはいない

思い出だけを残して

せわしい時の中

言葉を失った人形達のように

街角に溢れたノラネコのように

声にならない叫びが聞こえてくる

もしも もう一度あなたに会えるなら

もしも もう一度あなたに会えるなら

たった一言伝えたい

ありがとう

ありがとう

時には傷つけあっても

時には傷つけあっても

あなたを感じていたい

思い出はせめてもの慰め

いつまでもあなたはここにいる

もしも もう一度あなたに会えるなら

もしも もう一度あなたに会えるなら

たった一言伝えたい

ありがとう

ありがとう

もしも もう一度あなたに会えるなら

もしも もう一度あなたに会えるなら

たった一言伝えたい

もしも もう一度あなたに会えるなら

たった一言伝えたい

ありがとう

ありがとう

時には傷つけあっても

時には傷つけあっても

あなたを感じてたい

点击右上角即可分享
微信分享提示