内存映射文件是由一个文件到进程地址空间的映射。
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("上半段内容: "); int j = 0; for (int i = 0; i < FILE_SIZE / 2; i += 2) { sb.Append(" "); char ch = accessor.ReadChar(i); sb.Append(j); sb.Append(":"); sb.Append(ch); j++; } sb.Append(" 下半段内容: "); for (int i = FILE_SIZE / 2; i < FILE_SIZE; i += 2) { sb.Append(" "); 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(); } } }