【微软技术栈】C#.NET 内存映射文件

本文内容

  1. 进程、视图和管理内存
  2. 使用内存映射文件编程
  3. 示例

内存映射文件包含虚拟内存中文件的内容。 借助文件和内存空间之间的这种映射,应用(包括多个进程)可以直接对内存执行读取和写入操作,从而修改文件。 可以使用托管代码访问内存映射文件,就像本机 Windows 函数访问内存映射文件(如管理内存映射文件中所述)一样。

内存映射文件分为两种类型:

  • 持久化内存映射文件

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

  • 非持久化内存映射文件

    非持久化文件是不与磁盘上的文件相关联的内存映射文件。 当最后一个进程处理完文件时,数据会丢失,且文件被垃圾回收器回收。 此类文件适合创建共享内存,以进行进程内通信 (IPC)。

1、进程、视图和管理内存

可以跨多个进程共享内存映射文件。 进程可以映射到相同的内存映射文件,只需使用文件创建进程分配的通用名称即可。

必须创建整个或部分内存映射文件的视图,才能使用内存映射文件。 还可以为内存映射文件的同一部分创建多个视图,从而创建并发内存。 若要让两个视图一直处于并发状态,必须通过同一个内存映射文件创建它们。

如果文件大于可用于内存映射的应用程序逻辑内存空间(在 32 位计算机中为 2 GB),可能也有必要使用多个视图。

视图分为以下两种类型:流访问视图和随机访问视图。 使用流访问视图,可以顺序访问文件;建议对非持久化文件和 IPC 使用这种类型。 随机访问视图是处理持久化文件的首选类型。

由于内存映射文件是通过操作系统的内存管理程序进行访问,因此文件会被自动分区到很多页面,并根据需要进行访问。 无需自行处理内存管理。

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

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

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

2、使用内存映射文件编程

下表列出了与使用内存映射文件对象及其成员相关的指南。

任务要使用的方法或属性
从磁盘上的文件获取表示持久化内存映射文件的 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 方法。

2.1 安全性

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

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

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

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

3、示例

3.1 持久化内存映射文件

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

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

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

3.2 非持久化内存映射文件

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  

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

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

Process 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.");
        }
    }
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C#.NET提供了许多用于磁盘管理和文件操作的类和方法。下面是一些常用的类和方法: 1. `DriveInfo`类:它允许您获取和操作驱动器的信息,如名称、大小、可用空间等等。以下是一个示例: ```csharp DriveInfo[] allDrives = DriveInfo.GetDrives(); foreach (DriveInfo d in allDrives) { Console.WriteLine("Drive {0}", d.Name); Console.WriteLine(" File type: {0}", d.DriveType); if (d.IsReady == true) { Console.WriteLine(" Volume label: {0}", d.VolumeLabel); Console.WriteLine(" File system: {0}", d.DriveFormat); Console.WriteLine(" Available space to current user:{0, 15} bytes", d.AvailableFreeSpace); Console.WriteLine(" Total available space: {0, 15} bytes", d.TotalFreeSpace); Console.WriteLine(" Total size of drive: {0, 15} bytes ", d.TotalSize); } } ``` 2. `Directory`类:它允许您创建、移动、复制和删除文件夹,以及获取文件夹中文件的列表。以下是一些示例: ```csharp // 创建一个新文件夹 Directory.CreateDirectory(@"C:\test"); // 移动文件夹 Directory.Move(@"C:\test", @"C:\newTest"); // 复制文件夹 Directory.Copy(@"C:\test", @"C:\testCopy"); // 删除文件夹 Directory.Delete(@"C:\test"); // 获取文件夹中的文件列表 string[] files = Directory.GetFiles(@"C:\test"); foreach (string file in files) { Console.WriteLine(file); } ``` 3. `File`类:它允许您创建、移动、复制和删除文件,以及读取和写入文件的内容。以下是一些示例: ```csharp // 创建一个新文件 File.Create(@"C:\test.txt"); // 移动文件 File.Move(@"C:\test.txt", @"C:\newTest.txt"); // 复制文件 File.Copy(@"C:\test.txt", @"C:\testCopy.txt"); // 删除文件 File.Delete(@"C:\test.txt"); // 读取文件的内容 string contents = File.ReadAllText(@"C:\test.txt"); Console.WriteLine(contents); // 写入文件的内容 string contents = "Hello, world!"; File.WriteAllText(@"C:\test.txt", contents); ``` 这只是一些C#.NET中可用的磁盘管理和文件操作类和方法的示例。您可以查看MSDN文档或其他教程来学习更多的操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吉特思米(gitusme)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值