内存映射文件Memory-Mapped Files

内存映射文件包含虚拟内存中文件的内容。A memory-mapped file contains the contents of a file in virtual memory. 借助文件和内存空间之间的这种映射,应用(包括多个进程)可以直接对内存执行读取和写入操作,从而修改文件。This mapping between a file and memory space enables an application, including multiple processes, to modify the file by reading and writing directly to the memory. 从 .NET Framework 4 开始,可以使用托管代码访问内存映射文件,就像本机 Windows 函数访问内存映射文件(如管理内存映射文件所述)一样。Starting with the .NET Framework 4, you can use managed code to access memory-mapped files in the same way that native Windows functions access memory-mapped files, as described in Managing Memory-Mapped Files.

内存映射文件分为两种类型:There are two types of memory-mapped files:

  • 持久化内存映射文件Persisted memory-mapped files

    持久化文件是与磁盘上的源文件相关联的内存映射文件。Persisted files are memory-mapped files that are associated with a source file on a disk. 当最后一个进程处理完文件时,数据保存到磁盘上的源文件中。When the last process has finished working with the file, the data is saved to the source file on the disk. 此类内存映射文件适用于处理非常大的源文件。These memory-mapped files are suitable for working with extremely large source files.

  • 非持久化内存映射文件Non-persisted memory-mapped files

    非持久化文件是不与磁盘上的文件相关联的内存映射文件。Non-persisted files are memory-mapped files that are not associated with a file on a disk. 当最后一个进程处理完文件时,数据会丢失,且文件被垃圾回收器回收。When the last process has finished working with the file, the data is lost and the file is reclaimed by garbage collection. 此类文件适合创建共享内存,以进行进程内通信 (IPC)。These files are suitable for creating shared memory for inter-process communications (IPC).

进程、视图和管理内存Processes, Views, and Managing Memory

可以跨多个进程共享内存映射文件。Memory-mapped files can be shared across multiple processes. 进程可以映射到相同的内存映射文件,只需使用文件创建进程分配的通用名称即可。Processes can map to the same memory-mapped file by using a common name that is assigned by the process that created the file.

必须创建整个或部分内存映射文件的视图,才能使用内存映射文件。To work with a memory-mapped file, you must create a view of the entire memory-mapped file or a part of it. 还可以为内存映射文件的同一部分创建多个视图,从而创建并发内存。You can also create multiple views to the same part of the memory-mapped file, thereby creating concurrent memory. 若要让两个视图一直处于并发状态,必须通过同一个内存映射文件创建它们。For two views to remain concurrent, they have to be created from the same memory-mapped file.

如果文件大于可用于内存映射的应用逻辑内存空间(在 32 位计算机中为 2GB),可能也有必要使用多个视图。Multiple views may also be necessary if the file is greater than the size of the application’s logical memory space available for memory mapping (2 GB on a 32-bit computer).

视图分为以下两种类型:流访问视图和随机访问视图。There are two types of views: stream access view and random access view. 使用流访问视图,可以顺序访问文件;建议对非持久化文件和 IPC 使用这种类型。Use stream access views for sequential access to a file; this is recommended for non-persisted files and IPC. 随机访问视图是处理持久化文件的首选类型。Random access views are preferred for working with persisted files.

由于内存映射文件是通过操作系统的内存管理程序进行访问,因此文件会被自动分区到很多页面,并根据需要进行访问。Memory-mapped files are accessed through the operating system’s memory manager, so the file is automatically partitioned into a number of pages and accessed as needed. 无需自行处理内存管理。You do not have to handle the memory management yourself.

下图展示了多个进程如何同时对同一个内存映射文件有多个重叠视图。The following illustration shows how multiple processes can have multiple and overlapping views to the same memory-mapped file at the same time.

下图显示了内存映射文件的多个重叠视图:The following image shows multiple and overlapped views to a memory-mapped file:

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

使用内存映射文件编程Programming with Memory-Mapped Files

下表列出了与使用内存映射文件对象及其成员相关的指南。The following table provides a guide for using memory-mapped file objects and their members.

任务Task 要使用的方法或属性Methods or properties to use
从磁盘上的文件获取表示持久化内存映射文件的 MemoryMappedFile 对象。To obtain a MemoryMappedFile object that represents a persisted memory-mapped file from a file on disk. MemoryMappedFile.CreateFromFile 方法。MemoryMappedFile.CreateFromFile method.
获取表示非持久化内存映射文件的 MemoryMappedFile 对象(未与磁盘上的文件关联)。To obtain a MemoryMappedFile object that represents a non-persisted memory-mapped file (not associated with a file on disk). MemoryMappedFile.CreateNew 方法。MemoryMappedFile.CreateNew method.

- 或 -- or -

MemoryMappedFile.CreateOrOpen 方法。MemoryMappedFile.CreateOrOpen method.
获取现有内存映射文件(持久化或非持久化)的 MemoryMappedFile 对象。To obtain a MemoryMappedFile object of an existing memory-mapped file (either persisted or non-persisted). MemoryMappedFile.OpenExisting 方法。MemoryMappedFile.OpenExisting method.
获取内存映射文件的顺序访问视图的 UnmanagedMemoryStream 对象。To obtain a UnmanagedMemoryStream object for a sequentially accessed view to the memory-mapped file. MemoryMappedFile.CreateViewStream 方法。MemoryMappedFile.CreateViewStream method.
获取内存映射文件的随机访问视图的 UnmanagedMemoryAccessor 对象。To obtain a UnmanagedMemoryAccessor object for a random access view to a memory-mapped fie. MemoryMappedFile.CreateViewAccessor 方法。MemoryMappedFile.CreateViewAccessor method.
获取要与非托管代码结合使用的 SafeMemoryMappedViewHandle 对象。To obtain a SafeMemoryMappedViewHandle object to use with unmanaged code. MemoryMappedFile.SafeMemoryMappedFileHandle 属性。MemoryMappedFile.SafeMemoryMappedFileHandle property.

- 或 -- or -

MemoryMappedViewAccessor.SafeMemoryMappedViewHandle 属性。MemoryMappedViewAccessor.SafeMemoryMappedViewHandle property.

- 或 -- or -

MemoryMappedViewStream.SafeMemoryMappedViewHandle 属性。MemoryMappedViewStream.SafeMemoryMappedViewHandle property.
将内存分配一直延迟到视图创建完成(仅限非持久化文件)。To delay allocating memory until a view is created (non-persisted files only).

(若要确定当前系统页面大小,请使用 Environment.SystemPageSize 属性。)(To determine the current system page size, use the Environment.SystemPageSize property.)
值为 MemoryMappedFileOptions.DelayAllocatePagesCreateNew 方法。CreateNew method with the MemoryMappedFileOptions.DelayAllocatePages value.

- 或 -- or -

MemoryMappedFileOptions 枚举用作参数的 CreateOrOpen 方法。CreateOrOpen methods that have a MemoryMappedFileOptions enumeration as a parameter.

安全性Security

可以在创建内存映射文件时应用访问权限,具体操作是运行以下需要将 MemoryMappedFileAccess 枚举用作参数的方法:You can apply access rights when you create a memory-mapped file, by using the following methods that take a MemoryMappedFileAccess enumeration as a parameter:

若要指定打开现有内存映射文件所需的访问权限,可以运行需要将 MemoryMappedFileRights 用作参数的 OpenExisting 方法。You can specify access rights for opening an existing memory-mapped file by using the OpenExisting methods that take an MemoryMappedFileRights as a parameter.

另外,还可以添加包含预定义访问规则的 MemoryMappedFileSecurity 对象。In addition, you can include a MemoryMappedFileSecurity object that contains predefined access rules.

若要将新的或更改后的访问规则应用于内存映射文件,请使用 SetAccessControl 方法。To apply new or changed access rules to a memory-mapped file, use the SetAccessControl method. 若要从现有文件检索访问或审核规则,请使用 GetAccessControl 方法。To retrieve access or audit rules from an existing file, use the GetAccessControl method.

示例Examples

持久化内存映射文件Persisted Memory-Mapped Files

CreateFromFile 方法通过磁盘上的现有文件创建内存映射文件。The CreateFromFile methods create a memory-mapped file from an existing file on disk.

下面的示例为极大文件的一部分创建内存映射视图,并控制其中一部分。The following example creates a memory-mapped view of a part of an extremely large file and manipulates a portion of it.

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

下面的示例为另一个进程打开相同的内存映射文件。The following example opens the same memory-mapped file for another process.

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

非持久化内存映射文件Non-Persisted Memory-Mapped Files

CreateNewCreateOrOpen 方法创建未映射到磁盘上现有文件的内存映射文件。The CreateNew and CreateOrOpen methods create a memory-mapped file that is not mapped to an existing file on disk.

下面的示例包含三个独立进程(控制台应用),以将布尔值写入内存映射文件。The following example consists of three separate processes (console applications) that write Boolean values to a memory-mapped file. 各操作按下面的顺序发生:The following sequence of actions occur:

  1. Process A 创建内存映射文件,并向其中写入值。Process A creates the memory-mapped file and writes a value to it.

  2. Process B 打开内存映射文件,并向其中写入值。Process B opens the memory-mapped file and writes a value to it.

  3. Process C 打开内存映射文件,并向其中写入值。Process C opens the memory-mapped file and writes a value to it.

  4. Process A 读取并显示内存映射文件中的值。Process A reads and displays the values from the memory-mapped file.

  5. Process A 处理完内存映射文件后,此文件立即被垃圾回收器回收。After Process A is finished with the memory-mapped file, the file is immediately reclaimed by garbage collection.

若要运行此示例,请按照以下步骤操作:To run this example, do the following:

  1. 编译应用并打开三个命令提示符窗口。Compile the applications and open three Command Prompt windows.

  2. 在第一个命令提示符窗口中,运行 Process AIn the first Command Prompt window, run Process A.

  3. 在第二个命令提示符窗口中,运行 Process BIn the second Command Prompt window, run Process B.

  4. 返回到 Process A,再按 Enter。Return to Process A and press ENTER.

  5. 在第三个命令提示符窗口中,运行 Process CIn the third Command Prompt window, run Process C.

  6. 返回到 Process A,再按 Enter。Return to Process A and press ENTER.

Process A 的输出如下所示:The output of Process A is as follows:

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  

Process AProcess 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: {0}", reader.ReadBoolean());
                Console.WriteLine("Process B says: {0}", reader.ReadBoolean());
                Console.WriteLine("Process C says: {0}", 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

Process BProcess 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

Process CProcess 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

请参阅See also