C#MemoryMappedFile扩展?

时间:2015-02-08 08:43:35

标签: c# memory ipc

我使用MemoryMappedFile在两个进程之间交换数据。所以我在这两个过程中创建/打开了这样的文件:

private readonly MemoryMappedFile m_MemoryMappedFile = MemoryMappedFile.CreateOrOpen("Demo", 8);

文件访问本身在两个进程中都使用全局互斥锁进行保护。现在,当我向文件写入大于定义的8字节长度的数据时, NOT 会出现异常。

        var random = new Random();
        var testData = new byte[55];
        random.NextBytes(testData);
        using (var contentAccessor = m_MemoryMappedFile.CreateViewStream())
        {
            contentAccessor.Write(testData, 0, testData.Length);
        }

所以也许我在这里弄错了,但我想如果我创建一个具有指定容量的非持久内存映射文件(在我的情况下为8个字节),则不允许写入超过8个字节的更多数据?或者我通过上面的电话破坏了记忆?任何解释都会很棒吗?

干杯, 弗朗兹

2 个答案:

答案 0 :(得分:1)

在MSDN文章中特别提到了CreateViewStream():

  

要创建内存映射文件的完整视图,请为size参数指定0(零)。如果这样做,视图的大小可能小于或大于磁盘上源文件的大小。这是因为视图以系统页面为单位提供,并且视图的大小向上舍入到下一个系统页面大小。

哪个有小错误,它不小。确实向上舍入到页面大小。最好的办法是使用方法重载来设置视图大小:

    using (var contentAccessor = m_MemoryMappedFile.CreateViewStream(0, 8))
    {
        contentAccessor.Write(testData, 0, testData.Length);
    }

答案 1 :(得分:0)

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 Passant points out,视图流的大小实际上是 Environment.SystemPageSize 的倍数,根据 the Wikipedia page for Page Size 传统上的固定值是 4,096 字节。< /p>

您可以通过检查 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# 包装器,例如CreateFileMappingWOpenFileMappingW,Microsoft 文档中有大量相关文档。

例如,在 Creating a File Mapping Object 上它说(强调我的):

<块引用>

文件映射大小

文件映射对象的大小与被映射文件的大小无关。但是,如果文件映射对象大于文件,系统会在 CreateFileMapping 返回之前扩展文件。如果文件映射对象小于文件,则系统只从文件中映射指定的字节数。

CreateFileMappingdwMaximumSizeHighdwMaximumSizeLow 参数允许您指定要从文件映射的字节数:

  • 如果您不想更改文件大小(例如,映射只读文件时),请调用 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 字节)。


相关问题: