内存映射文件
内存映射文件简介
内存映射文件(Memory-Mapped File)是一种将文件或其他对象映射到进程地址空间的机制,使得应用程序可以像访问内存一样访问文件内容。以下是内存映射文件的主要特点和工作原理:
主要特点
- 直接内存访问:
- 应用程序可以通过指针直接访问文件内容,而不需要通过传统的读写操作。这提高了文件访问的效率。
- 共享内存:
- 内存映射文件可以被多个进程共享,使得它们可以同时访问同一个文件。这在进程间通信(IPC)中非常有用。
- 延迟加载:
- 文件的内容并不是立即加载到内存中,而是在实际访问时才加载。这可以节省内存资源。
- 自动同步:
- 对内存映射区域的修改会自动反映到文件中,反之亦然。这意味着对内存的更改会直接影响到文件内容。
工作原理
-
创建内存映射文件:
- 操作系统提供的 API(如 Windows 的
CreateFileMapping
和MapViewOfFile
)用于创建内存映射文件。
- 操作系统提供的 API(如 Windows 的
-
映射文件到内存:
- 通过将文件的某个部分映射到进程的虚拟地址空间,进程可以使用指针来访问文件内容。
-
访问和修改:
- 应用程序可以像访问普通数组一样访问映射的内存区域。对该区域的写入操作会直接修改文件内容。
-
解除映射:
- 当不再需要访问文件时,可以使用 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:
- 程序员需要手动管理文件的打开、读取和关闭,容易出错。
- 可能导致资源浪费,例如未及时关闭文件。
总结
标签:文件,读取,映射,System,访问,内存 From: https://www.cnblogs.com/Firepad-magic/p/18559917