tl;dr:是的 - C# MemoryMappedFile 将扩展 - 但只会扩展到 Environment.SystemPageSize 的下一个最大倍数(typically 4,096 字节)。一旦其大小超过该倍数,就会引发异常。
我遇到这个问题是因为我正在寻找使用MemoryMappedFile 的方法来解决MemoryStream(其支持字段is a byte array)仅限于int.MaxValue 值的事实。
我看到了与您在超出指定容量的情况下可以写入/读取数据的行为相同的行为,并注意到the documentation for MemoryMappedFile.CreateOrOpen(string mapName, long capacity) 明确表示(强调我的):
capacity
Int64
分配给内存映射文件的最大大小(以字节为单位)。
这显然与我们看到的不符。
作为Hans Passantpoints out,视图流的大小实际上是Environment.SystemPageSize 的倍数,根据the Wikipedia page for Page Size 传统上的固定值是4,096 字节。
您可以通过检查MemoryMappedViewStream 上的一些属性来确认这一点,例如:
var pageSize = Environment.SystemPageSize;
Console.WriteLine($"System page size: {pageSize} bytes");
Console.WriteLine();
var capacity = 1;
Console.WriteLine($"Creating MemoryMappedFile with capacity of {capacity} byte(s)");
using ( var memoryMappedFile = MemoryMappedFile.CreateOrOpen("Demo", capacity) )
using ( var viewStream = memoryMappedFile.CreateViewStream() )
{
Console.WriteLine($"View stream capacity: {viewStream.Capacity} bytes");
Console.WriteLine($"View stream length: {viewStream.Length} bytes");
Console.WriteLine();
var randomBytes = new byte[pageSize];
new Random().NextBytes(randomBytes);
viewStream.Write(randomBytes, offset: 0, count: randomBytes.Length);
Console.WriteLine($"{randomBytes.Length} bytes written to view stream");
viewStream.Position = 0;
for ( int index = 0; index < randomBytes.Length; index++ )
{
var writtenByte = randomBytes[index];
var readByte = viewStream.ReadByte();
if ( readByte != writtenByte )
throw new Exception($"Read byte at index {index} ({readByte}) was not the same as written ({writtenByte})");
}
Console.WriteLine($"{randomBytes.Length} bytes successfully read and verified from view stream");
Console.WriteLine();
// Attempt to write another byte (this should throw a NotSupportedException )
try
{
viewStream.WriteByte(0);
throw new InvalidOperationException("An extra byte was written to the view stream when it should not have");
}
catch ( NotSupportedException ex )
{
Console.WriteLine($"Unable to write additional bytes to view stream:{Environment.NewLine} {ex.Message}");
}
}
哪个输出:
System page size: 4096 bytes
Creating MemoryMappedFile with capacity of 1 byte(s)
View stream capacity: 4096 bytes
View stream length: 4096 bytes
4096 bytes written to view stream
4096 bytes successfully read and verified from view stream
Unable to write additional bytes to view stream:
Unable to expand length of this stream beyond its capacity.
注意:C# System.IO.MemoryMappedFiles namespace 中的类型实际上是 Windows API 调用的 C# 包装器,例如CreateFileMappingW 和 OpenFileMappingW,Microsoft 文档中有相当数量的文档。
例如,在Creating a File Mapping Object 它说(强调我的):
文件映射大小
文件映射对象的大小与被映射文件的大小无关。但是,如果文件映射对象大于文件,系统会在CreateFileMapping 返回之前展开文件。如果文件映射对象小于文件,系统只映射文件中指定的字节数。
CreateFileMapping 的 dwMaximumSizeHigh 和 dwMaximumSizeLow 参数允许您指定要从文件映射的字节数:
- 当您不想改变文件的大小时(例如,映射只读文件时),调用
CreateFileMapping 并为 dwMaximumSizeHigh 和 dwMaximumSizeLow。这样做会创建一个与文件大小完全相同的文件映射对象。 否则,您必须计算或估计完成文件的大小,因为文件映射对象的大小是静态的;一旦创建,它们的大小就不能增加或减少。
Managing Memory-Mapped Files 上面写着:
内存映射文件必须提供什么?
使用 MMF I/O 的一个优点是系统在 4K 数据页中为其执行所有数据传输。
这与the documentation for MemoryMappedFile.CreateViewStream 一致,它说(强调我的):
要创建内存映射文件的完整视图,请将 size 参数指定为 0(零)。如果这样做,视图的大小可能会大于磁盘上源文件的大小。这是因为视图是以系统页面为单位提供的,并且视图的大小会四舍五入到下一个系统页面大小。
换句话说,作为MemoryMappedFile 的实现细节(即它是Windows API 调用的包装器),传递给静态构造函数的capacity 有效地向上取整为系统页面大小的下一个最大倍数因为虚拟地址空间被分成页面(typically 4,096 字节)。
相关问题: