专栏首页张善友的专栏在Linux和Windows平台上操作MemoryMappedFile(简称MMF)

在Linux和Windows平台上操作MemoryMappedFile(简称MMF)

操作系统很早就开始使用内存映射文件(Memory Mapped File)来作为进程间的共享存储区,这是一种非常高效的进程通讯手段。.NET 4.0新增加了一个System.IO. MemoryMappedFiles命名空间,其中添加了几个类和相应的枚举类型,从而使我们可以很方便地创建内存映射文件。Mono 3.2也有这个类来操作Linux下的内存映射文件,《MemoryMappedFile 在 Mono in Linux 的开发笔记》详细的介绍了Mono和.NET 4的实现区别,为了让代码能够在Linux和Windows平台都正常运行,建议统一使用

MemoryMappedFile.CreateFromFile(     FileStream fileStream,     String mapName,     Int64 capacity,     MemoryMappedFileAccess access,     System.IO.MemoryMappedFiles.MemoryMappedFileSecurity memoryMappedFileSecurity,     HandleInheritability inheritability,     Boolean leaveOpen)

方法来创建MMF,并且在调用前确保指定的文件流大小与capacity参数值相同。

下面我给出在Windows和Linux下都运行正常的代码:

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections.Generic;
using System.Text;
using System.Security.AccessControl;
using System.Configuration;

namespace ManagedMMF
{
    class Program
    {
        static void Main(string[] args)
        {
            // Build a sample object and report records
            HikingDatabase hikingData = BuildDatabase(5000, 50);
            Console.WriteLine("Dummy database object created with " + hikingData.hikes.Length + " records.");
            string mmfile = ConfigurationManager.AppSettings["mmf"];
            // Write object to MMF
            WriteObjectToMMF(mmfile, hikingData);

            // Clear object and report
            hikingData = null;
            Console.WriteLine("Database object has been destroyed.");

            // Read new object from MMF and report records
            hikingData = ReadObjectFromMMF(mmfile) as HikingDatabase;
            Console.WriteLine("Dummy database object re-loaded from MMF with " + hikingData.hikes.Length + " records.");

            // Wait for input and terminate
            Console.ReadLine();
        }

        #region Generic MMF read/write object functions

        static void WriteObjectToMMF(string mmfFile, object objectData)
        {
            string mapName = "MyFile";
            if (IsMono())
            {
                mapName = mmfFile;
            }
            // Convert .NET object to byte array
            byte[] buffer = ObjectToByteArray(objectData);
            using (FileStream fs = new FileStream(mmfFile, FileMode.Create, FileAccess.ReadWrite))
            {
                fs.SetLength(buffer.Length);
                // Create a new memory mapped file
                using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(fs, mapName, buffer.Length,
                    MemoryMappedFileAccess.ReadWrite, new MemoryMappedFileSecurity() { }, HandleInheritability.Inheritable, true))
                {
                    // Create a view accessor into the file to accommmodate binary data size
                    using (MemoryMappedViewAccessor mmfWriter = mmf.CreateViewAccessor(0, buffer.Length))
                    {
                        // Write the data
                        mmfWriter.WriteArray<byte>(0, buffer, 0, buffer.Length);
                    }
                }
            }
        }

        static object ReadObjectFromMMF(string mmfFile)
        {
            string mapName = "MyFile";
            if (IsMono())
            {
                mapName = mmfFile;
            }
            using (FileStream fs = new FileStream(mmfFile, FileMode.Open, FileAccess.ReadWrite))
            {                
                // Get a handle to an existing memory mapped file
                using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(fs, mapName, fs.Length,
                    MemoryMappedFileAccess.ReadWrite, new MemoryMappedFileSecurity() { }, HandleInheritability.Inheritable, true))
                {
                    // Create a view accessor from which to read the data
                    using (MemoryMappedViewAccessor mmfReader = mmf.CreateViewAccessor())
                    {
                        // Create a data buffer and read entire MMF view into buffer
                        byte[] buffer = new byte[mmfReader.Capacity];
                        mmfReader.ReadArray<byte>(0, buffer, 0, buffer.Length);

                        // Convert the buffer to a .NET object
                        return ByteArrayToObject(buffer);
                    }
                }
            }
        }

        static bool IsMono()
        {
            Type t = Type.GetType("Mono.Runtime");
            return t != null;
        }

        #endregion

        #region Object/Binary serialization

        static object ByteArrayToObject(byte[] buffer)
        {
            BinaryFormatter binaryFormatter = new BinaryFormatter();    // Create new BinaryFormatter
            MemoryStream memoryStream = new MemoryStream(buffer);       // Convert byte array to memory stream, set position to start
            return binaryFormatter.Deserialize(memoryStream);           // Deserializes memory stream into an object and return
        }

        static byte[] ObjectToByteArray(object inputObject)
        {
            BinaryFormatter binaryFormatter = new BinaryFormatter();    // Create new BinaryFormatter
            MemoryStream memoryStream = new MemoryStream();             // Create target memory stream
            binaryFormatter.Serialize(memoryStream, inputObject);       // Convert object to memory stream
            return memoryStream.ToArray();                              // Return memory stream as byte array
        }

        #endregion

        static HikingDatabase BuildDatabase(int recordCount, int gpsCoordCount)
        {
            Random rand = new Random();

            HikingDatabase hikingData = new HikingDatabase();
            hikingData.Description = "My hikes, 2010 to 2012";
            hikingData.hikes = new Hike[recordCount];
            for (int i = 0; i < hikingData.hikes.Length; i++)
            {
                hikingData.hikes[i] = new Hike();
                hikingData.hikes[i].Description = "This is a description of this particular record. ";
                hikingData.hikes[i].Date = DateTime.Now.ToLongDateString();
                hikingData.hikes[i].GPSTrack = new Coord[gpsCoordCount];
                for (int j = 0; j < hikingData.hikes[i].GPSTrack.Length; j++)
                {
                    hikingData.hikes[i].GPSTrack[j] = new Coord();
                    hikingData.hikes[i].GPSTrack[j].x = rand.NextDouble() * 1000000;
                    hikingData.hikes[i].GPSTrack[j].y = rand.NextDouble() * 1000000;
                    hikingData.hikes[i].GPSTrack[j].z = rand.NextDouble() * 1000;
                }
            }
            return hikingData;
        }
    }

    #region Sample object for I/O

    [Serializable]
    public class HikingDatabase
    {
        public string Description;
        public Hike[] hikes;
    }

    [Serializable]
    public class Hike
    {
        public string Description;
        public string Date;
        public Coord[] GPSTrack;
    }

    [Serializable]
    public class Coord
    {
        public double x;
        public double y;
        public double z;
    }
    #endregion
}

所谓内存映射文件,其实就是在内存中开辟出一块存放数据的专用区域,这区域往往与硬盘上特定的文件相对应。进程将这块内存区域映射到自己的地址空间中,访问它就象是访问普通的内存一样。

在.NET中,使用MemoryMappedFile对象表示一个内存映射文件,通过它的CreateFromFile()方法根据磁盘现有文件创建内存映射文件,调用这一方法需要提供一个与磁盘现有文件相对应的FileStream对象。

当MemoryMappedFile对象创建之后,我们并不能直接对其进行读写,必须通过一个MemoryMappedViewAccessor对象来访问这个内存映射文件。MemoryMappedFile. CreateViewAccessor()方法可以创建MemoryMappedViewAccessor对象,而此对象提供了一系列读写的方法,用于向内存映射文件中读取和写入数据。

在创建内存映射文件访问对象需要指定它所能访问的内存映射文件的内容范围,这个“范围”称为“内存映射视图(Memory Mapped View)”。可以将它与“放大镜”类比,当使用一个放大镜阅读书籍时,一次只能放大指定部分的文字。类似地,我们只能在内存映射视图所规定的范围内存取内存映射文件。

如果要向内存映射文件中序列化对象,必须将内存映射文件转换为可顺序读取的流。幸运的是,MemoryMappedFile类的CreateViewStream()方法可以创建一个MemoryMappedViewStream对象,通过它即可序列化对象。这个对象允许序列访问映射视图;这个可能是使用映射视图流(mapped view streams)与使用允许随即访问的accessor对象相比的最大缺点。A quick (low-latency) IPC channel for .NET (Using MemoryMappedFile and Event) https://github.com/geffzhang/QuickIPC

相关文章:

Memory Mapped File Interoperability with .NET Objects

Programming Memory-Mapped Files with the .NET Framework

.Net Framework 4.0開始有包好的MemoryMappedFile的類別了

Working with memory mapped files in .NET 4

MemoryMappedFile 在 Mono in Linux 的开发笔记

MemoryMappedFile使用小结

System.IO之内存映射文件共享内存

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 开源软件的最近几个更新值得注意

    1、开源版本控制系统Subversion 1.3 正式发布 :Subversion开发小组近日宣布Subversion 1.3 正式发布。SubVersion是...

    张善友
  • Silverlight 2 应用程序部署到任意HTML页面

    Silverlight 2 应用程序部署到任意HTML页面,可以采用ActiveX的方法,在页面中放入object标签,设置相应的属性即可。使用 object ...

    张善友
  • Mono 2.8发布:C#4.0和更好的性能

    在社区很多人不看好的微软.NET开源实现Mono发布了Mono 2.8,这是一个重要的版本更新,有着显著的改善,Mono 2.8包括C#4.0的支持(也是现在的...

    张善友
  • 漫画小百科:什么是 “智猪博弈” ?

    话说在一个猪圈里,住着一只大猪和一只小猪。猪圈的一侧有个食槽,相反一侧是一个按钮,每次按下按钮(一段时间内只允许按一次),食槽里就会出现10份猪食。

    博文视点Broadview
  • 怼就完事了,总结几种验证码的解决方案

    截止到今天咸鱼已经写了很多期关于 Js 逆向的文章,不过这么多的文章都有一个共同点,都是关于加密参数或者密码加密的解析,很多读者在后台私信希望能够出一些关于滑动...

    咸鱼学Python
  • 漫画:什么是 “智猪博弈” ?

    话说在一个猪圈里,住着一只大猪和一只小猪。猪圈的一侧有个食槽,相反一侧是一个按钮,每次按下按钮(一段时间内只允许按一次),食槽里就会出现10份猪食。

    华章科技
  • JAVA8 JVM的变化: 元空间(Metaspace)

    本文将会分享至今为至我收集的关于永久代(Permanent Generation )的替代者:元空间(Metaspace)的信息。我也会比较在执行JAVA 程序...

    哲洛不闹
  • 为什么你的自定义View wrap_content不起作用?

    在使用自定义View时,View宽 / 高的wrap_content属性不起自身应有的作用,而且是起到与match_parent相同作用。

    Carson.Ho
  • 新版支付宝集成公众服务和IM,该如何看待

    自从来往败兴而归之后,阿里一直没有放弃移动IM这块肥肉,此次卷土重来,从影响力最大的支付宝平台入手,再入国内IM战局,一是想抵抗微信的垄断地位,二是阿里想在IM...

    人称T客
  • 推荐系统遇上深度学习(九十六)-[第四范式]考虑域内信息和多模块非线性融合的NON模型

    本文介绍的论文是SIGIR20上第四范式发表的一篇文章,针对当前推荐系统模型没有利用域内信息,不同数据泛化性较差等问题,提出了Network On Networ...

    guichen1013

扫码关注云+社区

领取腾讯云代金券