内存映射文件

内存映射文件包含虚拟内存中文件的内容。 文件与内存空间之间的这种映射使应用程序(包括多个进程)能够通过直接读取和写入内存来修改文件。 可以使用托管代码来像使用本机 Windows 函数那样访问内存映射文件,正如在管理 Memory-Mapped 文件中所述。

有两种类型的内存映射文件:

  • 持久化内存映射文件

    持久化文件是与磁盘上的源文件关联的内存映射文件。 最后一个进程处理完文件后,数据将保存到磁盘上的源文件。 这些内存映射文件适用于处理非常大的源文件。

  • 非持久化内存映射文件

    非持久化文件是未与磁盘上的文件关联的内存映射文件。 当最后一个进程完成处理文件后,数据将丢失,文件由垃圾回收回收。 这些文件适用于为进程间通信(IPC)创建共享内存。

进程、视图和管理内存

可以在多个进程中共享内存映射文件。 进程可以使用创建该文件的进程分配的公用名映射到同一内存映射文件。

若要使用内存映射文件,必须创建整个内存映射文件或其一部分的视图。 还可以为内存映射文件的同一部分创建多个视图,从而创建并发内存。 若要使两个视图保持并发状态,必须从同一内存映射文件创建它们。

如果文件大于应用程序可用于内存映射的逻辑内存空间的大小(32 位计算机上的 2 GB),则可能需要多个视图。

有两种类型的视图:流访问视图和随机访问视图。 建议使用流访问视图对文件进行顺序访问,这种方法适用于非持久化文件和进程间通信(IPC)。 随机访问视图是使用持久化文件的首选视图。

内存映射文件通过操作系统的内存管理器进行访问,因此该文件会自动分区为多个页面,并按需访问。 无需自行处理内存管理。

下图显示了多个进程如何同时对同一内存映射文件具有多个重叠视图。

下图显示了内存映射文件的多个重叠视图:

显示内存映射文件的视图的屏幕截图。

使用 Memory-Mapped 文件编程

下表提供了使用内存映射文件对象及其成员的指南。

任务 要使用的方法或属性
获取一个 MemoryMappedFile 对象,该对象代表磁盘上的文件中的持久内存映射文件。 MemoryMappedFile.CreateFromFile 方法。
获取一个对象,该对象表示非持久化的内存映射文件(即未与磁盘上的文件关联)。 MemoryMappedFile.CreateNew 方法。

- 或 -

MemoryMappedFile.CreateOrOpen 方法。
获取现有内存映射文件(持久化或非持久化)的 MemoryMappedFile 对象。 MemoryMappedFile.OpenExisting 方法。
获取内存映射文件的顺序访问视图的 UnmanagedMemoryStream 对象。 MemoryMappedFile.CreateViewStream 方法。
获取内存映射文件的随机访问视图的 UnmanagedMemoryAccessor 对象。 MemoryMappedFile.CreateViewAccessor 方法。
获取要与非托管代码结合使用的 SafeMemoryMappedViewHandle 对象。 MemoryMappedFile.SafeMemoryMappedFileHandle 属性将其完全禁用。

- 或 -

MemoryMappedViewAccessor.SafeMemoryMappedViewHandle 属性将其完全禁用。

- 或 -

MemoryMappedViewStream.SafeMemoryMappedViewHandle 属性将其完全禁用。
延迟分配内存,直到创建视图(仅限非持久化文件)。

(若要确定当前系统页面大小,请使用 Environment.SystemPageSize 属性。
值为 CreateNewMemoryMappedFileOptions.DelayAllocatePages 方法。

- 或 -

CreateOrOpenMemoryMappedFileOptions 枚举为参数的方法。

安全

可以在创建内存映射文件时应用访问权限,具体操作是运行以下需要将 MemoryMappedFileAccess 枚举用作参数的方法:

若要指定打开现有内存映射文件所需的访问权限,可以运行需要将 OpenExisting 用作参数的 MemoryMappedFileRights 方法。

此外,还可以包含 MemoryMappedFileSecurity 包含预定义访问规则的对象。

若要对内存映射文件应用新的或更改的访问规则,请使用 SetAccessControl 该方法。 若要从现有文件检索访问或审核规则,请使用 GetAccessControl 该方法。

例子

持久化内存映射文件

这些 CreateFromFile 方法从磁盘上的现有文件创建内存映射文件。

以下示例创建一个极其大型文件部分的内存映射视图,并操作其中的一部分。

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;

class Program
{
    static void Main(string[] args)
    {
        long offset = 0x10000000; // 256 megabytes
        long length = 0x20000000; // 512 megabytes

        // Create the memory-mapped file.
        using (var mmf = MemoryMappedFile.CreateFromFile(@"c:\ExtremelyLargeImage.data", FileMode.Open,"ImgA"))
        {
            // Create a random access view, from the 256th megabyte (the offset)
            // to the 768th megabyte (the offset plus length).
            using (var accessor = mmf.CreateViewAccessor(offset, length))
            {
                int colorSize = Marshal.SizeOf(typeof(MyColor));
                MyColor color;

                // Make changes to the view.
                for (long i = 0; i < length; i += colorSize)
                {
                    accessor.Read(i, out color);
                    color.Brighten(10);
                    accessor.Write(i, ref color);
                }
            }
        }
    }
}

public struct MyColor
{
    public short Red;
    public short Green;
    public short Blue;
    public short Alpha;

    // Make the view brighter.
    public void Brighten(short value)
    {
        Red = (short)Math.Min(short.MaxValue, (int)Red + value);
        Green = (short)Math.Min(short.MaxValue, (int)Green + value);
        Blue = (short)Math.Min(short.MaxValue, (int)Blue + value);
        Alpha = (short)Math.Min(short.MaxValue, (int)Alpha + value);
    }
}
Imports System.IO
Imports System.IO.MemoryMappedFiles
Imports System.Runtime.InteropServices

Class Program

    Sub Main()
        Dim offset As Long = &H10000000 ' 256 megabytes
        Dim length As Long = &H20000000 ' 512 megabytes

        ' Create the memory-mapped file.
        Using mmf = MemoryMappedFile.CreateFromFile("c:\ExtremelyLargeImage.data", FileMode.Open, "ImgA")
            ' Create a random access view, from the 256th megabyte (the offset)
            ' to the 768th megabyte (the offset plus length).
            Using accessor = mmf.CreateViewAccessor(offset, length)
                Dim colorSize As Integer = Marshal.SizeOf(GetType(MyColor))
                Dim color As MyColor
                Dim i As Long = 0

                ' Make changes to the view.
                Do While (i < length)
                    accessor.Read(i, color)
                    color.Brighten(10)
                    accessor.Write(i, color)
                    i += colorSize
                Loop
            End Using
        End Using
    End Sub
End Class

Public Structure MyColor
    Public Red As Short
    Public Green As Short
    Public Blue As Short
    Public Alpha As Short

    ' Make the view brighter.
    Public Sub Brighten(ByVal value As Short)
        Red = CType(Math.Min(Short.MaxValue, (CType(Red, Integer) + value)), Short)
        Green = CType(Math.Min(Short.MaxValue, (CType(Green, Integer) + value)), Short)
        Blue = CType(Math.Min(Short.MaxValue, (CType(Blue, Integer) + value)), Short)
        Alpha = CType(Math.Min(Short.MaxValue, (CType(Alpha, Integer) + value)), Short)
    End Sub
End Structure

以下示例为另一个进程打开相同的内存映射文件。

using System;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;

class Program
{
    static void Main(string[] args)
    {
        // Assumes another process has created the memory-mapped file.
        using (var mmf = MemoryMappedFile.OpenExisting("ImgA"))
        {
            using (var accessor = mmf.CreateViewAccessor(4000000, 2000000))
            {
                int colorSize = Marshal.SizeOf(typeof(MyColor));
                MyColor color;

                // Make changes to the view.
                for (long i = 0; i < 1500000; i += colorSize)
                {
                    accessor.Read(i, out color);
                    color.Brighten(20);
                    accessor.Write(i, ref color);
                }
            }
        }
    }
}

public struct MyColor
{
    public short Red;
    public short Green;
    public short Blue;
    public short Alpha;

    // Make the view brigher.
    public void Brighten(short value)
    {
        Red = (short)Math.Min(short.MaxValue, (int)Red + value);
        Green = (short)Math.Min(short.MaxValue, (int)Green + value);
        Blue = (short)Math.Min(short.MaxValue, (int)Blue + value);
        Alpha = (short)Math.Min(short.MaxValue, (int)Alpha + value);
    }
}
Imports System.IO.MemoryMappedFiles
Imports System.Runtime.InteropServices

Class Program
    Public Shared Sub Main(ByVal args As String())
        ' Assumes another process has created the memory-mapped file.
        Using mmf = MemoryMappedFile.OpenExisting("ImgA")
            Using accessor = mmf.CreateViewAccessor(4000000, 2000000)
                Dim colorSize As Integer = Marshal.SizeOf(GetType(MyColor))
                Dim color As MyColor

                ' Make changes to the view.
                Dim i As Long = 0
                While i < 1500000
                    accessor.Read(i, color)
                    color.Brighten(30)
                    accessor.Write(i, color)
                    i += colorSize
                End While
            End Using
        End Using
    End Sub
End Class

Public Structure MyColor
    Public Red As Short
    Public Green As Short
    Public Blue As Short
    Public Alpha As Short

    ' Make the view brigher.
    Public Sub Brighten(ByVal value As Short)
        Red = CShort(Math.Min(Short.MaxValue, CInt(Red) + value))
        Green = CShort(Math.Min(Short.MaxValue, CInt(Green) + value))
        Blue = CShort(Math.Min(Short.MaxValue, CInt(Blue) + value))
        Alpha = CShort(Math.Min(Short.MaxValue, CInt(Alpha) + value))
    End Sub
End Structure

非持久化内存映射文件

CreateNewCreateOrOpen方法创建一个内存映射文件,该文件未映射到磁盘上的现有文件。

以下示例由三个单独的进程(控制台应用程序)组成,这些进程将布尔值写入内存映射的文件。 各操作按下面的顺序发生:

  1. Process A 创建内存映射文件并将值写入其中。

  2. Process B 打开内存映射文件并将值写入其中。

  3. Process C 打开内存映射文件并将值写入其中。

  4. Process A 读取并显示内存映射文件中的值。

  5. Process A 处理完内存映射文件后,此文件立即被垃圾回收器回收。

若要运行此示例,请执行以下作:

  1. 编译应用程序并打开三个命令提示符窗口。

  2. 在第一个命令提示符窗口中,运行 Process A

  3. 在第二个命令提示符窗口中,运行 Process B

  4. 返回到 Process A 并按 Enter。

  5. 在第三个命令提示符窗口中,运行 Process C

  6. 返回到 Process A 并按 Enter。

Process A输出如下所示:

Start Process B and press ENTER to continue.  
Start Process C and press ENTER to continue.  
Process A says: True  
Process B says: False  
Process C says: True  

进程 A

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

class Program
{
    // Process A:
    static void Main(string[] args)
    {
        using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("testmap", 10000))
        {
            bool mutexCreated;
            Mutex mutex = new Mutex(true, "testmapmutex", out mutexCreated);
            using (MemoryMappedViewStream stream = mmf.CreateViewStream())
            {
                BinaryWriter writer = new BinaryWriter(stream);
                writer.Write(1);
            }
            mutex.ReleaseMutex();

            Console.WriteLine("Start Process B and press ENTER to continue.");
            Console.ReadLine();

            Console.WriteLine("Start Process C and press ENTER to continue.");
            Console.ReadLine();

            mutex.WaitOne();
            using (MemoryMappedViewStream stream = mmf.CreateViewStream())
            {
                BinaryReader reader = new BinaryReader(stream);
                Console.WriteLine($"Process A says: {reader.ReadBoolean()}");
                Console.WriteLine($"Process B says: {reader.ReadBoolean()}");
                Console.WriteLine($"Process C says: {reader.ReadBoolean()}");
            }
            mutex.ReleaseMutex();
        }
    }
}
Imports System.IO
Imports System.IO.MemoryMappedFiles
Imports System.Threading

Module Module1

    ' Process A:
    Sub Main()
        Using mmf As MemoryMappedFile = MemoryMappedFile.CreateNew("testmap", 10000)
            Dim mutexCreated As Boolean
            Dim mTex As Mutex = New Mutex(True, "testmapmutex", mutexCreated)
            Using Stream As MemoryMappedViewStream = mmf.CreateViewStream()
                Dim writer As BinaryWriter = New BinaryWriter(Stream)
                writer.Write(1)
            End Using
            mTex.ReleaseMutex()
            Console.WriteLine("Start Process B and press ENTER to continue.")
            Console.ReadLine()

            Console.WriteLine("Start Process C and press ENTER to continue.")
            Console.ReadLine()

            mTex.WaitOne()
            Using Stream As MemoryMappedViewStream = mmf.CreateViewStream()
                Dim reader As BinaryReader = New BinaryReader(Stream)
                Console.WriteLine("Process A says: {0}", reader.ReadBoolean())
                Console.WriteLine("Process B says: {0}", reader.ReadBoolean())
                Console.WriteLine("Process C says: {0}", reader.ReadBoolean())
            End Using
            mTex.ReleaseMutex()

        End Using

    End Sub

End Module

进程 B

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

class Program
{
    // Process B:
    static void Main(string[] args)
    {
        try
        {
            using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap"))
            {

                Mutex mutex = Mutex.OpenExisting("testmapmutex");
                mutex.WaitOne();

                using (MemoryMappedViewStream stream = mmf.CreateViewStream(1, 0))
                {
                    BinaryWriter writer = new BinaryWriter(stream);
                    writer.Write(0);
                }
                mutex.ReleaseMutex();
            }
        }
        catch (FileNotFoundException)
        {
            Console.WriteLine("Memory-mapped file does not exist. Run Process A first.");
        }
    }
}
Imports System.IO
Imports System.IO.MemoryMappedFiles
Imports System.Threading

Module Module1
    ' Process B:
    Sub Main()
        Try
            Using mmf As MemoryMappedFile = MemoryMappedFile.OpenExisting("testmap")
                Dim mTex As Mutex = Mutex.OpenExisting("testmapmutex")
                mTex.WaitOne()
                Using Stream As MemoryMappedViewStream = mmf.CreateViewStream(1, 0)
                    Dim writer As BinaryWriter = New BinaryWriter(Stream)
                    writer.Write(0)
                End Using
                mTex.ReleaseMutex()
            End Using
        Catch noFile As FileNotFoundException
            Console.WriteLine("Memory-mapped file does not exist. Run Process A first." & vbCrLf & noFile.Message)
        End Try

    End Sub

End Module

进程 C

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

class Program
{
    // Process C:
    static void Main(string[] args)
    {
        try
        {
            using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap"))
            {

                Mutex mutex = Mutex.OpenExisting("testmapmutex");
                mutex.WaitOne();

                using (MemoryMappedViewStream stream = mmf.CreateViewStream(2, 0))
                {
                    BinaryWriter writer = new BinaryWriter(stream);
                    writer.Write(1);
                }
                mutex.ReleaseMutex();
            }
        }
        catch (FileNotFoundException)
        {
            Console.WriteLine("Memory-mapped file does not exist. Run Process A first, then B.");
        }
    }
}
Imports System.IO
Imports System.IO.MemoryMappedFiles
Imports System.Threading

Module Module1
    ' Process C:
    Sub Main()
        Try
            Using mmf As MemoryMappedFile = MemoryMappedFile.OpenExisting("testmap")
                Dim mTex As Mutex = Mutex.OpenExisting("testmapmutex")
                mTex.WaitOne()
                Using Stream As MemoryMappedViewStream = mmf.CreateViewStream(2, 0)
                    Dim writer As BinaryWriter = New BinaryWriter(Stream)
                    writer.Write(1)
                End Using
                mTex.ReleaseMutex()
            End Using
        Catch noFile As FileNotFoundException
            Console.WriteLine("Memory-mapped file does not exist. Run Process A first, then B." & vbCrLf & noFile.Message)
        End Try

    End Sub

End Module

另请参阅