MemoryMappedFile.CreateFromFile 为什么不能直接映射部分区域
因为
CreateFromFile默认创建的是「完整文件映射」,底层调用 Windows 的
CreateFileMapping时未指定
dwMaximumSizeHigh/
dwMaximumSizeLow和偏移参数,所以无法控制起始位置和长度。真正支持部分映射的是
CreateFromFile的重载或更底层的
MemoryMappedFile.Create+
CreateViewAccessor组合。
如何用 CreateViewAccessor 映射文件的指定字节范围
必须先创建内存映射对象(可读/写/复制),再通过
CreateViewAccessor指定偏移与大小。关键点在于:视图的偏移必须是系统页面大小(通常是 4096)的整数倍,否则抛出
ArgumentException;长度可以任意,但超出文件实际大小会触发访问异常。
offset参数必须对齐到
Environment.SystemPageSize,例如想从第 8192 字节开始映射,没问题;但从 8200 开始就会报错
capacity是视图可见长度,不是必须等于剩余文件长度,但读写越界会引发
IOException或
AccessViolationException若文件以
FileMode.Append打开或未预分配空间,映射后写入超出当前文件长度的位置,需先调用
FileStream.SetLength扩容,否则写操作失败
using var fs = new FileStream("data.bin", FileMode.Open, FileAccess.ReadWrite);
using var mmf = MemoryMappedFile.CreateFromFile(fs, null, 0, MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, false);
// 从第 16KB 处开始,映射 4KB 视图(offset=16384 必须是 4096 的倍数)
using var accessor = mmf.CreateViewAccessor(16384, 4096, MemoryMappedFileAccess.ReadWrite);
accessor.Write(0, (byte)42); // 写入视图起始位置
映射只读区域时要注意 FileAccess 和 MemoryMappedFileAccess 的匹配
如果原始
FileStream是只读打开的,即使
MemoryMappedFileAccess设为
ReadWrite,
CreateViewAccessor仍会成功,但后续写操作会抛出
UnauthorizedAccessException。Windows 层面的访问权限由底层句柄决定,上层设置无效。 确保
FileStream的
FileAccess与后续
MemoryMappedFileAccess一致,尤其在只读场景下不要误传
ReadWrite若需多个进程共享只读视图,建议显式使用
FileAccess.Read+
MemoryMappedFileAccess.Read,避免运行时权限冲突 映射后修改文件长度(如
SetLength)在只读流下会直接失败,必须用可写流打开
大文件分块映射时为何要避免频繁创建/释放 ViewAccessor
每次调用
CreateViewAccessor都会触发一次
MapViewOfFileEx系统调用,并占用一个虚拟内存区域。在 32 位进程或内存紧张环境下,频繁映射不同区域可能导致
OutOfMemoryException,即使物理内存充足——因为虚拟地址空间碎片化。 优先复用同一个
MemoryMappedFile实例,按需切换
ViewAccessor,而不是为每段都新建
MemoryMappedFile若需同时访问多个不重叠区域,可创建多个
ViewAccessor,但务必及时
Dispose,否则虚拟内存泄漏比托管内存更隐蔽 注意
ViewAccessor不是线程安全的,多线程并发读写同一视图需自行加锁,或为每个线程分配独立视图 实际用的时候,最容易被忽略的是 offset 对齐和文件长度预分配——这两点不满足,程序可能在开发机跑得好好的,一到客户环境就崩溃,而且错误信息往往只显示“尝试读取或写入受保护的内存”,根本看不出是映射参数问题。
