內存映射文件

內存映射文件包含虛擬內存中文件的內容。利用文件與內存空間之間的映射,應用程序(包括多個進程)能夠經過直接在內存中進行讀寫來修改文件。從 .NET Framework 4 版開始,可使用託管代碼按照本機 Windows 函數訪問內存映射文件的方式來訪問內存映射文件,如 MSDN Library 中的 Managing Memory-Mapped Files in Win32(管理 Win32 中的內存映射文件)中所述。編程

有兩種類型的內存映射文件:windows

  • 持久內存映射文件安全

    持久文件是與磁盤上的源文件關聯的內存映射文件。 在最後一個進程使用完此文件後,數據將保存到磁盤上的源文件中。 這些內存映射文件適合用來處理很是大的源文件。併發

  • 非持久內存映射文件app

    非持久文件是未與磁盤上的源文件關聯的內存映射文件。 當最後一個進程使用完此文件後,數據將丟失,而且垃圾回收功能將回收此文件。 這些文件適用於爲進程間通訊 (IPC) 建立共享內存。dom

進程、視圖和管理內存

內存映射文件能夠在多個進程之間進行共享。 進程能夠經過使用由建立同一內存映射文件的進程所指派的公用名來映射到此文件。

若要使用一個內存映射文件,則必須建立該內存映射文件的完整視圖或部分視圖。 還能夠建立內存映射文件的同一部分的多個視圖,進而建立併發內存。 爲了使兩個視圖可以併發,必須基於同一內存映射文件建立這兩個視圖。ide

若是文件大於應用程序用於內存映射的邏輯內存空間(在 32 位計算機上爲 2 GB),則還須要使用多個視圖。函數

有兩種類型的視圖:流訪問視圖和隨機訪問視圖。 使用流訪問視圖可對文件進行順序訪問;對於非持久文件和 IPC,這是建議的方法。 在使用持久文件時,隨機訪問視圖是首選方法。ui

因爲內存映射文件是經過操做系統的內存管理器訪問的,所以會自動將此文件分隔爲多個頁,並根據須要對其進行訪問。 您不須要自行處理內存管理。this

下圖演示多個進程如何同時具備對同一內存映射文件的多個重疊視圖。

內存映射文件的多個重疊視圖

使用內存映射文件進行編程

下表提供有關使用內存映射文件對象及其成員的指南。
 

任務

使用的方法或屬性

從磁盤上的文件中獲取表示持久內存映射文件的 MemoryMappedFile 對象。

MemoryMappedFile.CreateFromFile 方法。

獲取表示非持久內存映射文件(與磁盤上的文件不關聯)的 MemoryMappedFile 對象。

MemoryMappedFile.CreateNew 方法。

- 或 -

MemoryMappedFile.CreateOrOpen 方法。

獲取現有內存映射文件(持久文件或非持久文件)的 MemoryMappedFile 對象。

MemoryMappedFile.OpenExisting 方法。

獲取針對內存映射文件的順序訪問視圖的 UnmanagedMemoryStream 對象。

MemoryMappedFile.CreateViewStream 方法。

獲取針對內存映射文件的隨機訪問視圖的 UnmanagedMemoryAccessor 對象。

MemoryMappedFile.CreateViewAccessor 方法。

獲取要用於非託管代碼的 SafeMemoryMappedViewHandle 對象。

MemoryMappedFile.SafeMemoryMappedFileHandle 屬性。

- 或 -

MemoryMappedViewAccessor.SafeMemoryMappedViewHandle 屬性。

- 或 -

MemoryMappedViewStream.SafeMemoryMappedViewHandle 屬性。

將內存分配推遲到建立視圖後進行(僅限於非持久文件)。

(若要肯定當前系統頁大小,請使用 Environment.SystemPageSize 屬性。)

MemoryMappedFileOptions.DelayAllocatePages 值的 CreateNew 方法。

- 或 -

MemoryMappedFileOptions 枚舉做爲參數的 CreateOrOpen 方法。

安全性

在建立內存映射文件時,能夠經過使用如下方法(這些方法採用 MemoryMappedFileAccess 枚舉做爲參數)來應用訪問權限:

經過使用將 MemoryMappedFileRights 用做參數的 OpenExisting 方法,能夠指定用於打開現有內存映射文件的訪問權限。

此外,能夠包含一個 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 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);
		}
	}

}


下面的示例爲另外一個進程打開同一內存映射文件。

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);
    }
}

非持久內存映射文件

CreateNew CreateOrOpen 方法建立一個未映射到磁盤上的現有文件的內存映射文件。

下面的示例由三個單獨的進程(控制檯應用程序)組成,這些進程將布爾值寫入到內存映射文件中。 將發生下面一系列操做:

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

進程 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.");
        }
    }
}

進程 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.");
        }
    }
}
相關文章
相關標籤/搜索