您的位置:首页 > 编程语言 > C#

C#内存映射文件学习总结

2017-05-05 22:43 405 查看

C#内存映射文件学习

http://www.cnblogs.com/flyant/p/4443187.html

内存映射文件是由一个文件到进程地址空间的映射。

        C#提供了允许应用程序把文件映射到一个进程的函(MemoryMappedFile.CreateOrOpen)。内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。另外,实际工程中的系统往往需要在多个进程之间共享数据,如果数据量小,处理方法是灵活多变的,如果共享数据容量巨大,那么就需要借助于内存映射文件来进行。实际上,内存映射文件正是解决本地多个进程间数据共享的最有效方法。

        共享内存是内存映射文件的一种特殊情况,内存映射的是一块内存,而非磁盘上的文件。共享内存的主语是进程(Process),操作系统默认会给每一个进程分配一个内存空间,每一个进程只允许访问操作系统分配给它的哪一段内存,而不能访问其他进程的。而有时候需要在不同进程之间访问同一段内存,怎么办呢?操作系统给出了创建访问共享内存的API,需要共享内存的进程可以通过这一组定义好的API来访问多个进程之间共有的内存,各个进程访问这一段内存就像访问一个硬盘上的文件一样。而.Net 4.0中引入了System.IO.MemoryMappedFiles命名空间,这个命名空间的类对windows
共享内存相关API做了封装,使.Net程序员可以更方便的使用内存映射文件。

内存映射文件实现进程间通讯

        内存映射文件是实现进程通讯的又一种方法,我们可以通过共享剪贴板、共享物理文件来实现进程间的数据共享,这里我们还可以通过内存映射文件来实现共享,这样,文件内的数据就可以用内存读/写指令来访问,而不是用ReadFile和WriteFile这样的I/O系统函数,从而提高了文件存取速度。这种方式更加快捷高效,最适用于需要读取文件并且对文件内包含的信息做语法分析的应用程序,如:对输入文件进行语法分析的彩色语法编辑器,编译器等。这种数据共享是让两个或多个进程映射同一文件映射对象的视图,即它们在共享同一物理存储页。这样,当一个进程向内存映射文件的一个视图写入数据时,其他的进程立即在自己的视图中看到变化。

注意:

        对文件映射对象要使用同一名字。

        是让两个或多个进程映射同一文件映射对象的视图,即它们在共享同一物理存储页。这样,当一个进程向内存映射文件的一个视图写入数据时,其他的进程立即在自己的视图中看到变化。但要注意,对文件映射对象要使用同一名字。

 内存映射文件使用实例:

1.      在同一进程内同时读写同一内存映射文件

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;

using System.IO;

using System.IO.MemoryMappedFiles;

namespace UseMMFInProcess

{

    public partial class frmMain : Form

    {

        public frmMain()

        {

            InitializeComponent();

            CreateMemoryMapFile();

        }

        private const int FILE_SIZE = 512;

        /// <summary>

        /// 引用内存映射文件

        /// </summary>

        private MemoryMappedFile memoryFile = null;

        /// <summary>

        /// 用于访问内存映射文件的存取对象

        /// </summary>

        private MemoryMappedViewAccessor accessor1, accessor2,accessor;

        /// <summary>

        /// 创建内存映射文件

        /// </summary>

        private void CreateMemoryMapFile()

        {

            try

            {     

                memoryFile = MemoryMappedFile.CreateFromFile("MyFile.dat", FileMode.OpenOrCreate, "MyFile", FILE_SIZE);               

                //访问文件前半段

                accessor1 = memoryFile.CreateViewAccessor(0, FILE_SIZE / 2);            

                //访问文件后半段

                accessor2 = memoryFile.CreateViewAccessor(FILE_SIZE / 2, FILE_SIZE / 2);              

                //访问全部文件

                accessor = memoryFile.CreateViewAccessor();

                //InitFileContent();

                lblInfo.Text = "内存文件创建成功";

                ShowFileContents();

            }

            catch (Exception ex)

            {

                lblInfo.Text = ex.Message;

            }

        }

        /// <summary>

        /// 关闭并释放资源

        /// </summary>

        private void DisposeMemoryMapFile()

        {

            if (accessor1 != null)

                accessor1.Dispose();

            if (accessor2 != null)

                accessor2.Dispose();

            if (memoryFile != null)

                memoryFile.Dispose();

        }

        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)

        {

            DisposeMemoryMapFile();

        }

        private void btnWrite1_Click(object sender, EventArgs e)

        {

            if (textBox1.Text.Length == 0)

            {

                lblInfo.Text = "请输入一个字符";

                return;

            }

            char[] chs = textBox1.Text.ToCharArray();

            char ch = chs[0];

           

            for (int i = 0; i < FILE_SIZE / 2; i += 2)

                accessor1.Write(i, ch);

            

            lblInfo.Text = "字符“" + ch + "”已写到文件前半部份";

            ShowFileContents();

        }

        private void btnShow_Click(object sender, EventArgs e)

        {

            ShowFileContents();

        }

        /// <summary>

        /// 初始化文件内容为可视的字符“0”

        /// </summary>

        private void InitFileContent()

        {

            for (int i = 0; i < FILE_SIZE; i += 2) 

                accessor.Write(i,'0');

        }

        /// <summary>

        /// 显示文件内容

        /// </summary>

        private void ShowFileContents()

        {

            StringBuilder sb = new StringBuilder(FILE_SIZE);

            sb.Append("上半段内容:\n");

            int j = 0;

            for (int i = 0; i < FILE_SIZE / 2; i += 2)

            {

                sb.Append("\t");

                char ch = accessor.ReadChar(i);

                sb.Append(j);

                sb.Append(":");

                sb.Append(ch);

                j++;

            }

            sb.Append("\n下半段内容:\n");

            for (int i = FILE_SIZE / 2; i < FILE_SIZE; i += 2)

            {

                sb.Append("\t");

                char ch = accessor.ReadChar(i);

                sb.Append(j);

                sb.Append(":");

                sb.Append(ch);

                j++;

            }

            richTextBox1.Text = sb.ToString();

        }

        private void btnWrite2_Click(object sender, EventArgs e)

        {

            if (textBox2.Text.Length == 0)

            {

                lblInfo.Text = "请输入一个字符";

                return;

            }

            char[] chs = textBox2.Text.ToCharArray();

            char ch = chs[0];

            for (int i = 0; i < FILE_SIZE / 2; i += 2)

                accessor2.Write(i, ch);

            lblInfo.Text = "字符“" + ch + "”已写到文件后半部份";

            ShowFileContents();

        }

    }

}

2.      使用内存映射文件在进程间传送值类型数据

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace UseMMFBetweenProcess

{

    /// <summary>

    /// 要共享的数据结构,注意,其成员不能是引用类型

    /// </summary>

    public struct MyStructure

    {

        public int IntValue

        {

            get;

            set;

        }

        public float FloatValue

        {

            get;

            set;

        } 

    } 

}

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;

using System.IO.MemoryMappedFiles;

using System.IO;

namespace UseMMFBetweenProcess

{

    public partial class frmMain : Form

    {

        public frmMain()

        {

            InitializeComponent();

            InitMemoryMappedFile();

        }

        /// <summary>

        /// 内存映射文件的容量

        /// </summary>

        private const int FileSize = 1024 * 1024;

        private MemoryMappedFile file = null;

        private MemoryMappedViewAccessor accessor = null;

        /// <summary>

        /// 初始化内存映射文件

        /// </summary>

        private void InitMemoryMappedFile()

        {

            file = MemoryMappedFile.CreateOrOpen("UseMMFBetweenProcess", FileSize);

            accessor = file.CreateViewAccessor();

            lblInfo.Text = "内存文件创建或连接成功";         

        }

        /// <summary>

        /// 要共享的数据对象

        /// </summary>

        private MyStructure data;

        /// <summary>

        /// 显示数据到窗体上

        /// </summary>

        private void ShowData()

        {

            textBox1.Text = data.IntValue.ToString();

            textBox2.Text = data.FloatValue.ToString();

        }

        /// <summary>

        /// 根据用户输入更新数据

        /// </summary>

        private void UpdateData()

        {

            data.IntValue = int.Parse(textBox1.Text);

            data.FloatValue = float.Parse(textBox2.Text);

        }

        private void btnSave_Click(object sender, EventArgs e)

        {

            try

            {

                UpdateData();

                accessor.Write<MyStructure>(0, ref data);

                lblInfo.Text = "数据已经保存到内存文件中";

            }

            catch (Exception ex)

            {

                lblInfo.Text = ex.Message;

            }

        }

        private void btnLoad_Click(object sender, EventArgs e)

        {

            accessor.Read<MyStructure>(0, out data);

            ShowData();

            lblInfo.Text = "成功从内存文件中提取了数据";

        }

        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)

        {

            if (accessor != null)

                accessor.Dispose();

            if (file != null)

                file.Dispose();

        }

    }

}

3.      利用序列化技术通过内存映射文件实现进程通讯

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;

using System.IO;

using System.IO.MemoryMappedFiles;

using System.Runtime.Serialization;

using System.Runtime.Serialization.Formatters.Binary;

namespace UseMMFBetweenProcess2

{

    public partial class frmMain : Form

    {

        public frmMain()

        {

            InitializeComponent();

            InitMemoryMappedFile();

        }

       

        /// <summary>

        /// 图片

        /// </summary>

        private Image bmp

        {

            get

            {

                return pictureBox1.Image;

            }

            set

            {

                pictureBox1.Image = value;

            }

        }

       

       /// <summary>

       /// 图片说明

       /// </summary>

        private string info

        {

            get

            {

                return txtImageInfo.Text;

            }

            set

            {

                txtImageInfo.Text = value;

            }

        }

        private MemoryMappedFile memoryFile = null;

        private MemoryMappedViewStream stream = null;

        /// <summary>

        /// 最大容量:10M

        /// </summary>

        private const int FileSize = 1024 * 1024 * 10;  

        /// <summary>

        /// 创建内存映射文件,获取其读写流

        /// </summary>

        private void InitMemoryMappedFile()

        {

            try

            {

  memoryFile = MemoryMappedFile.CreateOrOpen("UseMMFBetweenProcess2", FileSize);

            stream = memoryFile.CreateViewStream();

            }

            catch (Exception ex )

            {

                MessageBox.Show(ex.Message);

                Close();

            }

        }

        /// <summary>

        /// 释放相关资源

        /// </summary>

        private void DisposeMemoryMappedFile()

        {

            if (stream != null)

                stream.Close();

            if (memoryFile != null)

                memoryFile.Dispose();

        }

        private void btnLoadPic_Click(object sender, EventArgs e)

        {

            ChooseImageFile();

        }

        //选择图片

        private void ChooseImageFile()

        {

            if (openFileDialog1.ShowDialog() == DialogResult.OK)

            {

                bmp = new Bitmap(openFileDialog1.FileName);

            }

        }

        //根据用户设定的信息创建对象

        private MyPic CreateMyPicObj()

        {

            MyPic obj = new MyPic();

            obj.pic = bmp;

            obj.picInfo = info;

            return obj;

        }

        /// <summary>

        /// 将MyPic对象保存到内存映射文件中

        /// </summary>

        private void SaveToMMF()

        {

            try

            {

            MyPic obj = CreateMyPicObj();

            IFormatter formatter = new BinaryFormatter();

            stream.Seek(0, SeekOrigin.Begin);

            formatter.Serialize(stream, obj);

            MessageBox.Show("对象已保存到内存映射文件中");

            }

            catch (Exception ex)

            {

                MessageBox.Show(ex.Message);

            }

        }

 private void LoadFromMMF()

        {

            try

            {

           // CreateMyPicObj();

            IFormatter formatter = new BinaryFormatter();

            stream.Seek(0, SeekOrigin.Begin);

            MyPic obj =   formatter.Deserialize(stream) as MyPic;

            if (obj != null)

            {

                bmp = obj.pic;

                info = obj.picInfo;

            }

          }

          catch (Exception ex)

          {

              MessageBox.Show(ex.Message);

          }

        }

        private void btnExit_Click(object sender, EventArgs e)

        {

            Close();

        }

        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)

        {

            DisposeMemoryMappedFile();

        }

        private void btnSaveToMMF_Click(object sender, EventArgs e)

        {

            SaveToMMF();

        }

        private void btnLoadFromMMF_Click(object sender, EventArgs e)

        {

            LoadFromMMF();

        }

    }

}

========

C# 用内存映射文件读取大文件(.txt)

http://www.cnblogs.com/criedshy/archive/2010/06/13/1757826.html

  网上有好多这类的文章,大部分都是用C/C++写的,也有部分C#写的,都思想都是一样的,调用win32 API。

  至于什么是内存映射文件,相信还是有好多人不知道是怎么一回事的,我也是偶然看window 核心编程了解到的。

  C# 读取大文件的方法也是用的用StreamReader一次读出来,再用MemoryStream放在内存,再用StreamReader一行行的读出来,速度也挺快的,16M的文本大概也就8秒左右,算起来差不多算快了。不过还是不能满足大文件(我没测试)。

string content = string.Empty;

              using (StreamReader sr = new StreamReader(op.FileName))

                {

                    content = sr.ReadToEnd();//一次性读入内存

                }

                MemoryStream ms = new MemoryStream(Encoding.GetEncoding("GB2312").GetBytes(content));//放入内存流,以便逐行读取

                long line = 0;

                using (StreamReader sr = new StreamReader(ms))

                {

                    while (sr.Peek() > -1)

                    {

                        this.richTextBox1.AppendText(sr.ReadLine() + "\r\n");

                        Application.DoEvents();

                    }

               }

 内存映射文件概述

  内存文件映射也是Windows的一种内存管理方法,提供了一个统一的内存管理特征,使应用程序可以通过内存指针对磁盘上的文件进行访问,其过程就如同对加载了文件的内存的访问。通过文件映射这种使磁盘文件的全部或部分内容与进程虚拟地址空间的某个区域建立映射关联的能力,可以直接对被映射的文件进行访问,而不必执行文件I/O操作也无需对文件内容进行缓冲处理。内存文件映射的这种特性是非常适合于用来管理大尺寸文件的。

  在使用内存映射文件进行I/O处理时,系统对数据的传输按页面来进行。至于内部的所有内存页面则是由虚拟内存管理器来负责管理,由其来决定内存页面何时被分页到磁盘,哪些页面应该被释放以便为其它进程提供空闲空间,以及每个进程可以拥有超出实际分配物理内存之外的多少个页面空间等等。由于虚拟内存管理器是以一种统一的方式来处理所有磁盘I/O的(以页面为单位对内存数据进行读写),因此这种优化使其有能力以足够快的速度来处理内存操作。

  使用内存映射文件时所进行的任何实际I/O交互都是在内存中进行并以标准的内存地址形式来访问。磁盘的周期性分页也是由操作系统在后台隐蔽实现的,对应用程序而言是完全透明的。内存映射文件的这种特性在进行大文件的磁盘事务操作时将获得很高的效益。

  需要说明的是,在系统的正常的分页操作过程中,内存映射文件并非一成不变的,它将被定期更新。如果系统要使用的页面目前正被某个内存映射文件所占用,系统将释放此页面,如果页面数据尚未保存,系统将在释放页面之前自动完成页面数据到磁盘的写入。

  对于使用页虚拟存储管理的Windows操作系统,内存映射文件是其内部已有的内存管理组件的一个扩充。由可执行代码页面和数据页面组成的应用程序可根据需要由操作系统来将这些页面换进或换出内存。如果内存中的某个页面不再需要,操作系统将撤消此页面原拥用者对它的控制权,并释放该页面以供其它进程使用。只有在该页面再次成为需求页面时,才会从磁盘上的可执行文件重新读入内存。同样地,当一个进程初始化启动时,内存的页面将用来存储该应用程序的静态、动态数据,一旦对它们的操作被提交,这些页面也将被备份至系统的页面文件,这与可执行文件被用来备份执行代码页面的过程是很类似的。图1展示了代码页面和数据页面在磁盘存储器上的备份过程:

图1 进程的代码页、数据页在磁盘存储器上的备份

  显然,如果可以采取同一种方式来处理代码和数据页面,无疑将会提高程序的执行效率,而内存映射文件的使用恰恰可以满足此需求。

对大文件的管理

  内存映射文件对象在关闭对象之前并没有必要撤销内存映射文件的所有视图。在对象被释放之前,所有的脏页面将自动写入磁盘。通过 CloseHandle()关闭内存映射文件对象,只是释放该对象,如果内存映射文件代表的是磁盘文件,那么还需要调用标准文件I/O函数来将其关闭。在处理大文件处理时,内存映射文件将表示出卓越的优势,只需要消耗极少的物理资源,对系统的影响微乎其微。下面先给出内存映射文件的一般编程流程框图:

图2 使用内存映射文件的一般流程

  而在某些特殊行业,经常要面对十几GB乃至几十GB容量的巨型文件,而一个32位进程所拥有的虚拟地址空间只有232 = 4GB,显然不能一次将文件映像全部映射进来。对于这种情况只能依次将大文件的各个部分映射到进程中的一个较小的地址空间。这需要对上面的一般流程进行适当的更改:

  1)映射文件开头的映像。

  2)对该映像进行访问。

  3)取消此映像

  4)映射一个从文件中的一个更深的位移开始的新映像。

  5)重复步骤2,直到访问完全部的文件数据。

   下面是用C#写的代码,大部分代码转自网上,自己在原来的基础上改了一改。

C#内存映射文件代码

 

     经过测试16M的文本4秒可以读出来。

     现在是有两个问题还没有解决:

1.就是编码的问题,用Unicode解码的时候,文件大会很慢,而用ANSI和ASCII就很快。不知道为什么,望知情者告之。

2.怎么知道文件的编码是什么?用win32 IsTestUnicode只能判断两种,而且还不保证是对。

========

c#实现内存映射文件共享内存

http://blog.csdn.net/wangtiewei/article/details/51112668  

内存映射文件是利用虚拟内存把文件映射到进程的地址空间中去,在此之后进程操作文件,就像操作进程空间里的地址一样了,比如使用C语言的 memcpy等内存操作的函数。这种方法能够很好的应用在需要频繁处理一个文件或者是一个大文件的场合,这种方式处理IO效率比普通IO效率要高

共享内存是内存映射文件的一种特殊情况,内存映射的是一块内存,而非磁盘上的文件。共享内存的主语是进程(Process),操作系统默认会给每一 个进程分配一个内存空间,每一个进程只允许访问操作系统分配给它的哪一段内存,而不能访问其他进程的。而有时候需要在不同进程之间访问同一段内存,怎么办 呢?操作系统给出了创建访问共享内存的API,需要共享内存的进程可以通过这一组定义好的API来访问多个进程之间共有的内存,各个进程访问这一段内存就 像访问一个硬盘上的文件一样。而.Net 4.0中引入了System.IO. MemoryMappedFiles命名空间,这个命名空间的类对windows
共享内存相关API做了封装,使.Net程序员可以更方便的使用内存映射文件。

在C#中使用共享内存。以下App1的代码让用户输入一行文本到共享内存中;App2不停的刷新控制台,输出最新的共享内存内容;App3实现的功能和App2相同,但读取方法不同。

App1代码:  

using System;  

using System.Collections.Generic;android从资源文件中读取文件流显示  

using System.Linq;  

using System.Text;  

  

using System.IO;  

  

//引用内存映射文件命名空间  

using System.IO.MemoryMappedFiles;  

  

namespace App1  

{  

    class Program  

    {  

        static void Main(string[] args)  

        {  

            long capacity = 1<<10<<10;  

  

            //创建或者打开共享内存  

            using (var mmf = MemoryMappedFile.CreateOrOpen("testMmf", capacity, MemoryMappedFileAccess.ReadWrite))  

            {  

                //通过MemoryMappedFile的CreateViewAccssor方法获得共享内存的访问器  

                var viewAccessor = mmf.CreateViewAccessor(0, capacity);  

                //循环写入,使在这个进程中可以向共享内存中写入不同的字符串值  

                while (true)  

                {  

                    Console.WriteLine("请输入一行要写入共享内存的文字:");  

  

                    string input = Console.ReadLine();  

  

                    //向共享内存开始位置写入字符串的长度  

                    viewAccessor.Write(0, input.Length);  

  

                    //向共享内存4位置写入字符  

                    viewAccessor.WriteArray<char>(4, input.ToArray(), 0, input.Length);  

                }  

  

            }  

              

        }  

    }  

}  

App2代码:  

using System;  

using System.Collections.Generic;  

using System.Linq;  

using System.Text;  

using System.Threading;  

  

//引用使用内存映射文件需要的命名空间  

using System.IO.MemoryMappedFiles;  

  

namespace App2  

{  

    class Program  

    {  

        static void Main(string[] args)  

        {  

              long capacity = 1<<10<<10;  

  

              using (var mmf = MemoryMappedFile.OpenExisting("testMmf"))  

              {  

                  MemoryMappedViewAccessor viewAccessor = mmf.CreateViewAccessor(0, capacity);  

  

                  //循环刷新共享内存字符串的值  

                  while (true)  

                  {  

                      //读取字符长度  

                      int strLength = viewAccessor.ReadInt32(0);                        

                      char[] charsInMMf = new char[strLength];  

                      //读取字符  

                      viewAccessor.ReadArray<char>(4, charsInMMf, 0, strLength);  

                      Console.Clear();  

                      Console.Write(charsInMMf);  

                      Console.Write("\r");  

                      Thread.Sleep(200);  

                  }  

              }  

        }  

    }  

}  

App3代码:  

using System;  

using System.Collections.Generic;  

using System.Linq;  

using System.Text;  

  

using System.IO.MemoryMappedFiles;  

using System.IO;  

  

namespace App3  

{  

    class Program  

    {  

        static void Main(string[] args)  

        {  

            long capacity = 1 << 10 << 10;  

            //打开共享内存  

            using (var mmf = MemoryMappedFile.OpenExisting("testMmf"))  

            {  

                //使用CreateViewStream方法返回stream实例  

                using (var mmViewStream = mmf.CreateViewStream(0, capacity))  

                {  

                    //这里要制定Unicode编码否则会出问题  

                    using (BinaryReader rdr = new BinaryReader(mmViewStream,Encoding.Unicode))  

                    {  

                        while (true)  

                        {  

                            mmViewStream.Seek(0, SeekOrigin.Begin);  

  

                            int length = rdr.ReadInt32();  

  

                            char[] chars = rdr.ReadChars(length);  

  

                            Console.Write(chars);  

                            Console.Write("\r");  

  

                            System.Threading.Thread.Sleep(200);  

                            Console.Clear();  

                        }  

                    }  

                }  

            }  

        }  

    }  

}  

在读数据时用了2种方法。

因为在之前很少会用到进程之间的通信,所以此方法只是想初步的认识下。此程序写的过于简陋,有很多东西都没有去判断。比如说是怎么创建了一个共享内存怎么取删除它等等。。。

========

用C#实现的内存映射

http://blog.csdn.net/linux7985/article/details/5853358

当文件过大时,无法一次性载入内存时,就需要分次,分段的载入文件

主要是用了以下的WinAPI

LPVOID MapViewOfFile(HANDLE hFileMappingObject,

  DWORD dwDesiredAccess,

  DWORD dwFileOffsetHigh,

  DWORD dwFileOffsetLow,

  DWORD dwNumberOfBytesToMap);

 

  MapViewOfFile() 函数负责把文件数据映射到进程的地址空间,参数hFileMappingObject 为 CreateFileMapping()返回的文件映像对象句柄。参数dwDesiredAccess则再次指定了对文件数据的访问方式,而且同样要与 CreateFileMapping()函数所设置的保护属性相匹配。虽然这里一再对保护属性进行重复设置看似多余,但却可以使应用程序能更多的对数据的保护属性实行有效控制。MapViewOfFile()函数允许全部或部分映射文件,在映射时,需要指定数据文件的偏移地址以及待映射的长度。其中,文件的偏移地址由DWORD型的参数dwFileOffsetHigh和dwFileOffsetLow组成的64位值来指定,而且必须是操作系统的分配粒度的整数倍,对于Windows操作系统,分配粒度固定为64KB。当然,也可以通过如下代码来动态获取当前操作系统的分配粒度:

  SYSTEM_INFO sinf;

  GetSystemInfo(&sinf);

  DWORD dwAllocationGranularity = sinf.dwAllocationGranularity;

  参数dwNumberOfBytesToMap指定了数据文件的映射长度,这里需要特别指出的是,对于Windows 9x操作系统,如果MapViewOfFile()无法找到足够大的区域来存放整个文件映射对象,将返回空值(NULL);但是在Windows 2000下,MapViewOfFile()只需要为必要的视图找到足够大的一个区域即可,而无须考虑整个文件映射对象的大小。

由此看出,分页映射文件时,每页的起始位置startpos,必须为64K的整数倍。

以下贴出源代码,防止忘记了

using System;  

using System.Collections.Generic;  

using System.Text;  

using System.Runtime.InteropServices;  

  

namespace BlueVision.SaYuan.FileMapping  

{  

    public class ShareMemory  

    {  

        [DllImport( "user32.dll", CharSet = CharSet.Auto )]  

        public static extern IntPtr SendMessage( IntPtr hWnd, int Msg, int wParam, IntPtr lParam );  

  

        [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )]  

        public static extern IntPtr CreateFileMapping( IntPtr hFile, IntPtr lpAttributes, uint flProtect, uint dwMaxSizeHi, uint dwMaxSizeLow, string lpName );  

  

        [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )]  

        public static extern IntPtr OpenFileMapping( int dwDesiredAccess, [MarshalAs( UnmanagedType.Bool )] bool bInheritHandle, string lpName );  

  

        [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )]  

        public static extern IntPtr MapViewOfFile( IntPtr hFileMapping, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap );  

  

        [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )]  

        public static extern bool UnmapViewOfFile( IntPtr pvBaseAddress );  

  

        [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )]  

        public static extern bool CloseHandle( IntPtr handle );  

  

        [DllImport( "kernel32", EntryPoint = "GetLastError" )]  

        public static extern int GetLastError();  

  

        [DllImport( "kernel32.dll" )]  

        static extern void GetSystemInfo( out SYSTEM_INFO lpSystemInfo );  

  

        [StructLayout( LayoutKind.Sequential )]  

        public struct SYSTEM_INFO  

        {  

            public ushort processorArchitecture;  

            ushort reserved;  

            public uint pageSize;  

            public IntPtr minimumApplicationAddress;  

            public IntPtr maximumApplicationAddress;  

            public IntPtr activeProcessorMask;  

            public uint numberOfProcessors;  

            public uint processorType;  

            public uint allocationGranularity;  

            public ushort processorLevel;  

            public ushort processorRevision;  

        }  

        /// <summary>  

        /// 获取系统的分配粒度  

        /// </summary>  

        /// <returns></returns>  

        public static uint GetPartitionsize()  

        {  

            SYSTEM_INFO sysInfo;  

            GetSystemInfo( out sysInfo );  

            return sysInfo.allocationGranularity;  

        }  

  

        const int ERROR_ALREADY_EXISTS = 183;  

  

        const int FILE_MAP_COPY = 0x0001;  

        const int FILE_MAP_WRITE = 0x0002;  

        const int FILE_MAP_READ = 0x0004;  

        const int FILE_MAP_ALL_ACCESS = 0x0002 | 0x0004;  

  

        const int PAGE_READONLY = 0x02;  

        const int PAGE_READWRITE = 0x04;  

        const int PAGE_WRITECOPY = 0x08;  

        const int PAGE_EXECUTE = 0x10;  

        const int PAGE_EXECUTE_READ = 0x20;  

        const int PAGE_EXECUTE_READWRITE = 0x40;  

  

        const int SEC_COMMIT = 0x8000000;  

        const int SEC_IMAGE = 0x1000000;  

        const int SEC_NOCACHE = 0x10000000;  

        const int SEC_RESERVE = 0x4000000;  

  

        IntPtr m_fHandle;  

  

        IntPtr m_hSharedMemoryFile = IntPtr.Zero;  

        IntPtr m_pwData = IntPtr.Zero;  

        bool m_bAlreadyExist = false;  

        bool m_bInit = false;  

        uint m_MemSize = 0x1400000;//20M  

        long m_offsetBegin = 0;  

        long m_FileSize = 0;  

        FileReader File = new FileReader();  

    

        /// <summary>  

        ///  初始化文件  

        /// </summary>  

        /// <param name="MemSize">缓冲大小</param>  

        public ShareMemory( string filename, uint memSize )  

        {  

            // 分页映射文件时,每页的起始位置startpos,必须为64K的整数倍。  

            // memSize即缓存区的大小必须是系统分配粒度的整倍说,window系统的分配粒度是64KB  

            this.m_MemSize = memSize;  

            Init( filename );  

        }  

    

        /// <summary>  

        /// 默认映射20M缓冲  

        /// </summary>  

        /// <param name="filename"></param>  

        public ShareMemory( string filename )  

        {  

            this.m_MemSize = 0x1400000;  

            Init( filename );  

        }  

  

        ~ShareMemory()  

        {  

            Close();  

        }  

  

        /// <summary>  

        /// 初始化共享内存  

        ///   

        /// 共享内存名称  

        /// 共享内存大小  

        /// </summary>  

        /// <param name="strName"></param>  

        protected void Init( string strName )  

        {  

            //if (lngSize <= 0 || lngSize > 0x00800000) lngSize = 0x00800000;  

  

            if ( !System.IO.File.Exists( strName ) ) throw new Exception( "未找到文件" );  

  

            System.IO.FileInfo f = new System.IO.FileInfo( strName );    

            m_FileSize = f.Length;    

            m_fHandle = File.Open( strName );  

  

            if ( strName.Length > 0 )  

            {  

                //创建文件映射  

                m_hSharedMemoryFile = CreateFileMapping( m_fHandle, IntPtr.Zero, ( uint )PAGE_READONLY, 0, ( uint )m_FileSize, "mdata" );  

                if ( m_hSharedMemoryFile == IntPtr.Zero )  

                {  

                    m_bAlreadyExist = false;  

                    m_bInit = false;  

                    throw new Exception( "CreateFileMapping失败LastError=" + GetLastError().ToString() );  

                }  

                else  

                    m_bInit = true;  

  

                ////映射第一块文件  

                //m_pwData = MapViewOfFile(m_hSharedMemoryFile, FILE_MAP_READ, 0, 0, (uint)m_MemSize);  

                //if (m_pwData == IntPtr.Zero)  

                //{  

                //    m_bInit = false;  

                //    throw new Exception("m_hSharedMemoryFile失败LastError=" + GetLastError().ToString());  

                //}  

  

            }  

        }  

        /// <summary>  

        /// 获取高32位  

        /// </summary>  

        /// <param name="intValue"></param>  

        /// <returns></returns>  

        private static uint GetHighWord( UInt64 intValue )  

        {  

            return Convert.ToUInt32( intValue >> 32 );  

        }  

        /// <summary>  

        /// 获取低32位  

        /// </summary>  

        /// <param name="intValue"></param>  

        /// <returns></returns>  

        private static uint GetLowWord( UInt64 intValue )  

        {  

  

            return Convert.ToUInt32( intValue & 0x00000000FFFFFFFF );  

        }  

  

        /// <summary>  

        /// 获取下一个文件块 块大小为20M  

        /// </summary>  

        /// <returns>false 表示已经是最后一块文件</returns>  

        public uint GetNextblock()  

        {  

            if ( !this.m_bInit ) throw new Exception( "文件未初始化。" );  

            //if ( m_offsetBegin + m_MemSize >= m_FileSize ) return false;  

  

            uint m_Size = GetMemberSize();  

            if ( m_Size == 0 ) return m_Size;  

  

            // 更改缓冲区大小  

            m_MemSize = m_Size;  

  

            //卸载前一个文件  

            //bool l_result = UnmapViewOfFile( m_pwData );  

            //m_pwData = IntPtr.Zero;  

  

  

            m_pwData = MapViewOfFile( m_hSharedMemoryFile, FILE_MAP_READ, GetHighWord( ( UInt64 )m_offsetBegin ), GetLowWord( ( UInt64 )m_offsetBegin ), m_Size );  

            if ( m_pwData == IntPtr.Zero )  

            {  

                m_bInit = false;  

                throw new Exception( "映射文件块失败" + GetLastError().ToString() );  

            }  

            m_offsetBegin = m_offsetBegin + m_Size;  

  

            return m_Size; //创建成功  

        }  

        /// <summary>  

        /// 返回映射区大小  

        /// </summary>  

        /// <returns></returns>  

        private uint GetMemberSize()  

        {  

            if ( m_offsetBegin >= m_FileSize )  

            {  

                return 0;  

            }  

            else if ( m_offsetBegin + m_MemSize >= m_FileSize )  

            {  

                long temp = m_FileSize - m_offsetBegin;  

                return ( uint )temp;  

            }  

            else  

                return m_MemSize;  

        }  

  

        /// <summary>  

        /// 关闭内存映射  

        /// </summary>  

        public void Close()  

        {  

            if ( m_bInit )  

            {  

                UnmapViewOfFile( m_pwData );  

                CloseHandle( m_hSharedMemoryFile );  

                File.Close();  

            }  

        }  

  

        /// <summary>  

        /// 从当前块中获取数据  

        /// </summary>  

        /// <param name="bytData">数据</param>  

        /// <param name="lngAddr">起始数据</param>  

        /// <param name="lngSize">数据长度,最大值=缓冲长度</param>  

        /// <param name="Unmap">读取完成是否卸载缓冲区</param>  

        /// <returns></returns>  

        public void Read( ref byte[] bytData, int lngAddr, int lngSize, bool Unmap )  

        {  

            if ( lngAddr + lngSize > m_MemSize )  

                throw new Exception( "Read操作超出数据区" );  

            if ( m_bInit )  

            {  

                // string bb = Marshal.PtrToStringAuto(m_pwData);//  

                Marshal.Copy( m_pwData, bytData, lngAddr, lngSize );  

            }  

            else  

            {  

                throw new Exception( "文件未初始化" );  

            }  

  

            if ( Unmap )  

            {  

                bool l_result = UnmapViewOfFile( m_pwData );  

                if ( l_result )  

                    m_pwData = IntPtr.Zero;  

            }  

        }  

  

        /// <summary>  

        /// 从当前块中获取数据  

        /// </summary>  

        /// <param name="bytData">数据</param>  

        /// <param name="lngAddr">起始数据</param>  

        /// <param name="lngSize">数据长度,最大值=缓冲长度</param>  

        /// <exception cref="Exception: Read操作超出数据区"></exception>  

        /// <exception cref="Exception: 文件未初始化"></exception>  

        /// <returns></returns>  

        public void Read( ref byte[] bytData, int lngAddr, int lngSize )  

        {  

            if ( lngAddr + lngSize > m_MemSize )  

                throw new Exception( "Read操作超出数据区" );  

            if ( m_bInit )  

            {  

                Marshal.Copy( m_pwData, bytData, lngAddr, lngSize );  

            }  

            else  

            {  

                throw new Exception( "文件未初始化" );  

            }  

        }  

  

        /// <summary>  

        /// 从当前块中获取数据  

        /// </summary>  

        /// <param name="lngAddr">缓存区偏移量</param>  

        /// <param name="byteData">数据数组</param>  

        /// <param name="StartIndex">数据数组开始复制的下标</param>  

        /// <param name="lngSize">数据长度,最大值=缓冲长度</param>  

        /// <exception cref="Exception: 起始数据超过缓冲区长度"></exception>  

        /// <exception cref="Exception: 文件未初始化"></exception>  

        /// <returns>返回实际读取值</returns>  

        public uint ReadBytes( int lngAddr, ref byte[] byteData, int StartIndex, uint intSize )  

        {  

            if ( lngAddr >= m_MemSize )  

                throw new Exception( "起始数据超过缓冲区长度" );  

  

            if ( lngAddr + intSize > m_MemSize )  

                intSize = m_MemSize - ( uint )lngAddr;  

  

            if ( m_bInit )  

            {  

                IntPtr s = new IntPtr( ( long )m_pwData + lngAddr ); // 地址偏移  

                Marshal.Copy( s, byteData, StartIndex, ( int )intSize );  

            }  

            else  

            {  

                throw new Exception( "文件未初始化" );  

            }  

  

            return intSize;  

        }  

  

        /// <summary>  

        /// 写数据  

        /// </summary>  

        /// <param name="bytData">数据</param>  

        /// <param name="lngAddr">起始地址</param>  

        /// <param name="lngSize">个数</param>  

        /// <returns></returns>  

        private int Write( byte[] bytData, int lngAddr, int lngSize )  

        {  

            if ( lngAddr + lngSize > m_MemSize ) return 2; //超出数据区  

            if ( m_bInit )  

            {  

                Marshal.Copy( bytData, lngAddr, m_pwData, lngSize );  

            }  

            else  

            {  

                return 1; //共享内存未初始化  

            }  

            return 0; //写成功  

        }  

    }  

    internal class FileReader  

    {  

        const uint GENERIC_READ = 0x80000000;  

        const uint OPEN_EXISTING = 3;  

        System.IntPtr handle;  

  

        [DllImport( "kernel32", SetLastError = true )]  

        public static extern System.IntPtr CreateFile(  

            string FileName,          // file name  

            uint DesiredAccess,       // access mode  

            uint ShareMode,           // share mode  

            uint SecurityAttributes,  // Security Attributes  

            uint CreationDisposition, // how to create  

            uint FlagsAndAttributes,  // file attributes  

            int hTemplateFile         // handle to template file  

        );  

  

        [System.Runtime.InteropServices.DllImport( "kernel32", SetLastError = true )]  

        static extern bool CloseHandle  

        (  

            System.IntPtr hObject // handle to object  

        ); 

        public IntPtr Open( string FileName )  

        {  

            // open the existing file for reading         

            handle = CreateFile  

            (  

                FileName,  

                GENERIC_READ,  

                0,  

                0,  

                OPEN_EXISTING,  

                0,  

                0  

            );  

  

            if ( handle != System.IntPtr.Zero )  

            {  

                return handle;  

            }  

            else  

            {  

                throw new Exception( "打开文件失败" );  

            }  

        }  

  

        public bool Close()  

        {  

            return CloseHandle( handle );  

        }  

    }  

}  

========

.NET Framework自带的文件内存映射类

http://www.cnblogs.com/briny/archive/2012/11/25/2787188.html

最近一直为文件内存映射发愁,整个两周一直折腾这个东西。在64位系统和32位系统还要针对内存的高低位进行计算。好麻烦。。还是没搞定

偶然从MSDN上发现.NET 4.0把内存文件映射加到了.NET类库中。。好像方便了很多啊。。比用C#直接调用WINDOWS API方便多了。所以

这个必须果断记录之。。。项目马上要用,为了加强内存数据交换的效率。。这个。。。必须啊。。

任务

使用的方法或属性

从磁盘上的文件中获取表示持久内存映射文件的 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 方法。

 

持久文件内存映射:

CreateFromFile 方法基于磁盘上的现有文件创建一个内存映射文件。

 1 using System;

 2 using System.IO;

 3 using System.IO.MemoryMappedFiles;

 4 using System.Runtime.InteropServices;

 5 

 6 class Program

 7 {

 8     static void Main(string[] args)

 9     {

10         long offset = 0x10000000; // 256 megabytes

11         long length = 0x20000000; // 512 megabytes

12 

13         // Create the memory-mapped file.

14         using (var mmf = MemoryMappedFile.CreateFromFile(@"c:\ExtremelyLargeImage.data", FileMode.Open,"ImgA"))

15         {

16             // Create a random access view, from the 256th megabyte (the offset)

17             // to the 768th megabyte (the offset plus length).

18             using (var accessor = mmf.CreateViewAccessor(offset, length))

19             {

20                 int colorSize = Marshal.SizeOf(typeof(MyColor));

21                 MyColor color;

22 

23                 // Make changes to the view.

24                 for (long i = 0; i < length; i += colorSize)

25                 {

26                     accessor.Read(i, out color);

27                     color.Brighten(10);

28                     accessor.Write(i, ref color);

29                 }

30             }

31         }

32     }

33 }

34 

35 public struct MyColor

36 {

37     public short Red;

38     public short Green;

39     public short Blue;

40     public short Alpha;

41 

42     // Make the view brigher.

43     public void Brighten(short value)

44     {

45         Red = (short)Math.Min(short.MaxValue, (int)Red + value);

46         Green = (short)Math.Min(short.MaxValue, (int)Green + value);

47         Blue = (short)Math.Min(short.MaxValue, (int)Blue + value);

48         Alpha = (short)Math.Min(short.MaxValue, (int)Alpha + value);

49     }

50 }

 非持久文件内存映射:

CreateNew 和 CreateOrOpen 方法创建一个未映射到磁盘上的现有文件的内存映射文件。

 1 using System;

 2 using System.IO;

 3 using System.IO.MemoryMappedFiles;

 4 using System.Threading;

 5 

 6 class Program

 7 {

 8     // Process A:

 9     static void Main(string[] args)

10     {

11         using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("testmap", 10000))

12         {

13             bool mutexCreated;

14             Mutex mutex = new Mutex(true, "testmapmutex", out mutexCreated);

15             using (MemoryMappedViewStream stream = mmf.CreateViewStream())

16             {

17                 BinaryWriter writer = new BinaryWriter(stream);

18                 writer.Write(1);

19             }

20             mutex.ReleaseMutex();

21 

22             Console.WriteLine("Start Process B and press ENTER to continue.");

23             Console.ReadLine();

24 

25             Console.WriteLine("Start Process C and press ENTER to continue.");

26             Console.ReadLine();

27 

28             mutex.WaitOne();

29             using (MemoryMappedViewStream stream = mmf.CreateViewStream())

30             {

31                 BinaryReader reader = new BinaryReader(stream);

32                 Console.WriteLine("Process A says: {0}", reader.ReadBoolean());

33                 Console.WriteLine("Process B says: {0}", reader.ReadBoolean());

34                 Console.WriteLine("Process C says: {0}", reader.ReadBoolean());

35             }

36             mutex.ReleaseMutex();

37         }

38     }

39 }

========
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  .net c# 内存映射文件