Saturday, May 8, 2010

.Net 4.0 :- Memory Mapped Files

Until now managed code developers were oblivious to the delight of memory mapped files and its performance benifits. Now with the coming of .Net 4.0 CLR , its available to be explored. Managing dynamic memory on Windows NT is traditionally done by using Win32 API's. Various methods are provided in API to manage Global and process heap.

As for memory mapped files methods, they provide us the way to treat files on the disk as they were loaded in dynamic memory. Using memory mapped file we can map part or full file to the memory address in our process space.

Now read/write are as simple as doing it with dynamic memory. All memory related operation internally are handled by the Virtual memory manager.
It uses paging and caching to optimize the io operation.

This leads to the less actual hard disk read/write. Additionally we do not need to load whole file. Memory mapped files can be used to share a common memory between two processes. Common memory object is given a name and name is used by different processes to access it. Win 32 API exposes various method to achieve MMF like ,CreateFileMapping(),OpenFileMapping(),MapViewOfFile() etc.

For managed code developers we got it under System.IO .

using System.IO;
using System.IO.MemoryMappedFiles;

  //Process 1
private static void MMFileWrite()
{
MemoryMappedFileSecurity CustomSecurity =
new MemoryMappedFileSecurity();

MemoryMappedFile memFileObject =
MemoryMappedFile.CreateFromFile(
new FileStream(@"C:\MyMemMapFile.map",
FileMode.Create,FileAccess.ReadWrite,
FileShare.ReadWrite),
"MyMapFile", //Name
1024 * 1024, //Size
MemoryMappedFileAccess.ReadWrite,
CustomSecurity,
HandleInheritability.Inheritable,//Child processes
false);// Dispose filestream

using(MemoryMappedViewAccessor view
= memFileObject.CreateViewAccessor())
{
MyMemContent content =
content = new MyMemContent();
content.Id = 1;
content.High = 45;
content.Low = 39;
content.Bid = 43;
content.Close = 42;
view.Write(1,ref content);
}
}





//Process 2
private static void MMFileRead()
{
MemoryMappedFile memFileObject
= MemoryMappedFile.OpenExisting(
@"MyMapFile",
MemoryMappedFileRights.FullControl,
HandleInheritability.Inheritable);

using (MemoryMappedViewAccessor view
= memFileObject.CreateViewAccessor())
{
MyMemContent content =
content = new MyMemContent();
view.Read(1, out content);
}
}
Memory mapped file should have shared attribute so that , processes accessing it can read / write.
There is another thing , if we dispose the MemoryMappedFile object , then the file is no more accessible for read/write operation. Content to be written to the memory file should be of value type and if we are wrtting the custom structure to file then it should not contain any reference type.

1 comment:

  1. Hi, i create console aplication using your sample... And i have problem, two process not open file. Please can you see?

    using System;
    using System.IO;
    using System.IO.MemoryMappedFiles;
    using System.Threading;

    namespace MemoryFileTest
    {
    class Program
    {
    static void Main(string[] args)
    {
    var path = @"c:\work\mmf.dat";
    var map = "testmap123";
    var fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);

    if (fs.Length == 0)
    {
    fs.SetLength(1024);
    }

    try
    {
    var sec = new MemoryMappedFileSecurity();
    var mem = MemoryMappedFile.CreateFromFile(fs, map, fs.Length, MemoryMappedFileAccess.ReadWrite, sec, HandleInheritability.Inheritable, false);

    WriteRole(mem);
    }
    catch (Exception exp)
    {
    fs.Close();

    var mem = MemoryMappedFile.OpenExisting(map, MemoryMappedFileRights.FullControl, HandleInheritability.Inheritable);

    ReadRole(mem);
    }

    }

    static void WriteRole(MemoryMappedFile mem)
    {
    Console.WriteLine("Begin write role.");

    var ptr = mem.CreateViewAccessor(0, 1024, MemoryMappedFileAccess.ReadWrite);

    while (true)
    {
    int mode;

    ptr.Read(0, out mode);

    if (mode < 2)
    {
    Console.WriteLine("Write new time to my friend.");

    var str = DateTime.Now.ToString();
    var pos = 4l;

    ptr.Write(pos, str.Length);

    pos += 4;

    for (int i = 0; i < str.Length; ++i)
    {
    ptr.Write(pos, str[i]);

    pos += 2;
    }

    ptr.Write(0, 2);
    }

    Thread.CurrentThread.Join(500);
    }
    }

    static void ReadRole(MemoryMappedFile mem)
    {
    Console.WriteLine("Begin read role.");

    var ptr = mem.CreateViewAccessor(0, 1024, MemoryMappedFileAccess.ReadWrite);

    while (true)
    {
    int mode;

    ptr.Read(0, out mode);

    if (mode == 2)
    {
    var pos = 4l;
    var len = ptr.ReadInt32(pos);
    var arr = new char[len];

    pos += 4;

    for (int i = 0; i < len; ++i)
    {
    arr[i] = ptr.ReadChar(pos);

    pos += 2;
    }

    var str = new string(arr);

    Console.WriteLine(str);

    ptr.Write(0, 1);
    }

    Thread.CurrentThread.Join(500);
    }
    }

    }
    }

    ReplyDelete