操作系統很早就開始使用內存映射文件(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之內存映射文件共享內存
https://github.com/geffzhang/QuickIPC