内存映射文件

内存映射文件[1][2]

2015-03-31

原理
  有两种类型的内存映射文件
  进程、视图和管理内存
内存映射文件对象及其成员
示例
  示例1:在同一进程内同时读写同一内存映射文件
  示例2:使用内存映射文件在进程间传送值类型数据
  示例3:利用序列化技术通过内存映射文件实现进程通讯
参考

原理[1]


 返回

内存映射文件包含虚拟内存中文件的内容。 利用文件与内存空间之间的映射,应用程序(包括多个进程)可以通过直接在内存中进行读写来修改文件。 从 .NET Framework 4开始,可以使用托管代码按照本机Windows函数访问内存映射文件的方式来访问内存映射文件

内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。[2]

有两种类型的内存映射文件

  • 持久内存映射文件:持久文件是与磁盘上的源文件关联的内存映射文件。  在最后一个进程使用完此文件后,数据将保存到磁盘上的源文件中。 这些内存映射文件适合用来处理非常大的源文件。
  • 非持久内存映射文件:非持久文件是未与磁盘上的源文件关联的内存映射文件。  当最后一个进程使用完此文件后,数据将丢失,并且垃圾回收功能将回收此文件。 这些文件适用于为进程间通信 (IPC) 创建共享内存。

进程、视图和管理内存

  • 内存映射文件可以在多个进程之间进行共享。  进程可以通过使用由创建同一内存映射文件的进程所指派的公用名来映射到此文件。
  • 若要使用一个内存映射文件,则必须创建该内存映射文件的完整视图或部分视图。  还可以创建内存映射文件的同一部分的多个视图,进而创建并发内存。 为了使两个视图能够并发,必须基于同一内存映射文件创建这两个视图。
  • 如果文件大于应用程序用于内存映射的逻辑内存空间(在 32 位计算机上为 2 GB),则还需要使用多个视图。 
  • 有两种类型的视图:流访问视图和随机访问视图。  使用流访问视图可对文件进行顺序访问;对于非持久文件和 IPC,这是建议的方法。 在使用持久文件时,随机访问视图是首选方法。
  • 由于内存映射文件是通过操作系统的内存管理器访问的,因此会自动将此文件分隔为多个页,并根据需要对其进行访问。  您不需要自行处理内存管理。

下图演示多个进程如何同时具有对同一内存映射文件的多个重叠视图:

Figure 1 内存映射文件的多个重叠视图 

内存映射文件对象及其成员


 返回

表1:有关使用内存映射文件对象及其成员

方法 说明
MemoryMappedFile.CreateFromFile 磁盘上的文件中获取表示持久内存映射文件的 MemoryMappedFile 对象。

MemoryMappedFile.CreateNew

或MemoryMappedFile.CreateOrOpen

获取表示非持久内存映射文件(与磁盘上的文件不关联)的 MemoryMappedFile 对象。
MemoryMappedFile.OpenExisting 获取现有内存映射文件(持久文件或非持久文件)的 MemoryMappedFile 对象。
MemoryMappedFile.CreateViewStream 获取针对内存映射文件的顺序访问视图的 UnmanagedMemoryStream 对象。
MemoryMappedFile.CreateViewAccessor 获取针对内存映射文件的随机访问视图的 UnmanagedMemoryAccessor 对象。 

示例[2]


 返回

示例1:在同一进程内同时读写同一内存映射文件

Figure 2 在同一进程内同时读写同一内存映射文件

示例1代码如下:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.Linq;
  7 using System.Text;
  8 using System.Windows.Forms;
  9 
 10 using System.IO;  
 11 using System.IO.MemoryMappedFiles;
 12 
 13 namespace MemoryMappingFile
 14 {
 15     public partial class Form1 : Form
 16     {
 17         public Form1()  
 18        {  
 19            InitializeComponent();  
 20            CreateMemoryMapFile();  
 21        }  
 22        private const int FILE_SIZE = 512;  
 23        /// <summary>  
 24        /// 引用内存映射文件  
 25        /// </summary>  
 26        private MemoryMappedFile memoryFile = null;  
 27        /// <summary>  
 28        /// 用于访问内存映射文件的存取对象  
 29        /// </summary>  
 30        private MemoryMappedViewAccessor accessor1, accessor2,accessor;  
 31        /// <summary>  
 32        /// 创建内存映射文件  
 33        /// </summary>  
 34        private void CreateMemoryMapFile()  
 35        {  
 36            try  
 37            {       
 38                memoryFile = MemoryMappedFile.CreateFromFile("MyFile.dat", FileMode.OpenOrCreate, "MyFile", FILE_SIZE);                 
 39                //访问文件前半段  
 40                accessor1 = memoryFile.CreateViewAccessor(0, FILE_SIZE / 2);              
 41                //访问文件后半段  
 42                accessor2 = memoryFile.CreateViewAccessor(FILE_SIZE / 2, FILE_SIZE / 2);                
 43                //访问全部文件  
 44                accessor = memoryFile.CreateViewAccessor();  
 45                //InitFileContent();  
 46                lblInfo.Text = "内存文件创建成功";  
 47                ShowFileContents();  
 48            }  
 49            catch (Exception ex)  
 50            {  
 51                lblInfo.Text = ex.Message;  
 52            }  
 53        }  
 54        /// <summary>  
 55        /// 关闭并释放资源  
 56        /// </summary>  
 57        private void DisposeMemoryMapFile()  
 58        {  
 59            if (accessor1 != null)  
 60                accessor1.Dispose();  
 61            if (accessor2 != null)  
 62                accessor2.Dispose();  
 63            if (memoryFile != null)  
 64                memoryFile.Dispose();  
 65        }  
 66  
 67        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)  
 68        {  
 69            DisposeMemoryMapFile();  
 70        }  
 71  
 72        private void btnWrite1_Click(object sender, EventArgs e)  
 73        {  
 74            if (textBox1.Text.Length == 0)  
 75            {  
 76                lblInfo.Text = "请输入一个字符";  
 77                return;  
 78            }  
 79            char[] chs = textBox1.Text.ToCharArray();  
 80            char ch = chs[0];  
 81             
 82            for (int i = 0; i < FILE_SIZE / 2; i += 2)  
 83                accessor1.Write(i, ch);  
 84              
 85            lblInfo.Text = "字符“" + ch + "”已写到文件前半部份";  
 86            ShowFileContents();  
 87        }  
 88  
 89        private void btnShow_Click(object sender, EventArgs e)  
 90        {  
 91            ShowFileContents();  
 92        }  
 93  
 94        /// <summary>  
 95        /// 初始化文件内容为可视的字符“0”  
 96        /// </summary>  
 97        private void InitFileContent()  
 98        {  
 99            for (int i = 0; i < FILE_SIZE; i += 2)   
100                 accessor.Write(i,'0');  
101         }  
102         /// <summary>  
103         /// 显示文件内容  
104         /// </summary>  
105         private void ShowFileContents()  
106         {  
107             StringBuilder sb = new StringBuilder(FILE_SIZE);  
108             sb.Append("上半段内容:\n");  
109   
110             int j = 0;  
111             for (int i = 0; i < FILE_SIZE / 2; i += 2)  
112             {  
113                 sb.Append("\t");  
114                 char ch = accessor.ReadChar(i);  
115                 sb.Append(j);  
116                 sb.Append(":");  
117                 sb.Append(ch);  
118                 j++;  
119             }  
120             sb.Append("\n下半段内容:\n");  
121   
122             for (int i = FILE_SIZE / 2; i < FILE_SIZE; i += 2)  
123             {  
124                 sb.Append("\t");  
125                 char ch = accessor.ReadChar(i);  
126                 sb.Append(j);  
127                 sb.Append(":");  
128                 sb.Append(ch);  
129                 j++;  
130             }  
131             richTextBox1.Text = sb.ToString();  
132         }  
133   
134         private void btnWrite2_Click(object sender, EventArgs e)  
135         {  
136             if (textBox2.Text.Length == 0)  
137             {  
138                 lblInfo.Text = "请输入一个字符";  
139                 return;  
140             }  
141             char[] chs = textBox2.Text.ToCharArray();  
142             char ch = chs[0];  
143   
144             for (int i = 0; i < FILE_SIZE / 2; i += 2)  
145                 accessor2.Write(i, ch);  
146             lblInfo.Text = "字符“" + ch + "”已写到文件后半部份";  
147             ShowFileContents();  
148         }  
149     }
150 }
View Code

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

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

示例2代码如下:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.Linq;
  7 using System.Text;
  8 using System.Windows.Forms;
  9 
 10 using System.IO.MemoryMappedFiles;  
 11 using System.IO;  
 12 
 13 
 14 namespace MemoryMappingFile
 15 {
 16      /// <summary>  
 17      /// 要共享的数据结构,注意,其成员不能是引用类型  
 18      /// </summary>  
 19      public struct MyStructure  
 20      {  
 21          public int IntValue  
 22          {  
 23              get;  
 24              set;  
 25          }  
 26          public float FloatValue  
 27          {  
 28              get;  
 29              set;  
 30          }   
 31      }   
 32 
 33 
 34     public partial class Form2 : Form
 35     {
 36         public Form2()  
 37        {  
 38            InitializeComponent();  
 39            InitMemoryMappedFile();  
 40        }  
 41  
 42        /// <summary>  
 43        /// 内存映射文件的容量  
 44        /// </summary>  
 45        private const int FileSize = 1024 * 1024;  
 46        private MemoryMappedFile file = null;  
 47        private MemoryMappedViewAccessor accessor = null;  
 48  
 49        /// <summary>  
 50        /// 初始化内存映射文件  
 51        /// </summary>  
 52        private void InitMemoryMappedFile()  
 53        {  
 54            file = MemoryMappedFile.CreateOrOpen("UseMMFBetweenProcess", FileSize);  
 55            accessor = file.CreateViewAccessor();  
 56            lblInfo.Text = "内存文件创建或连接成功";           
 57        }  
 58  
 59        /// <summary>  
 60        /// 要共享的数据对象  
 61        /// </summary>  
 62        private MyStructure data;  
 63  
 64        /// <summary>  
 65        /// 显示数据到窗体上  
 66        /// </summary>  
 67        private void ShowData()  
 68        {  
 69            textBox1.Text = data.IntValue.ToString();  
 70            textBox2.Text = data.FloatValue.ToString();  
 71        }  
 72  
 73        /// <summary>  
 74        /// 根据用户输入更新数据  
 75        /// </summary>  
 76        private void UpdateData()  
 77        {  
 78            data.IntValue = int.Parse(textBox1.Text);  
 79            data.FloatValue = float.Parse(textBox2.Text);  
 80        }  
 81  
 82        private void btnSave_Click(object sender, EventArgs e)  
 83        {  
 84            try  
 85            {  
 86                UpdateData();  
 87                accessor.Write<MyStructure>(0, ref data);  
 88                lblInfo.Text = "数据已经保存到内存文件中";  
 89            }  
 90            catch (Exception ex)  
 91            {  
 92                lblInfo.Text = ex.Message;  
 93            }  
 94         }  
 95   
 96         private void btnLoad_Click(object sender, EventArgs e)  
 97         {  
 98             accessor.Read<MyStructure>(0, out data);  
 99             ShowData();  
100             lblInfo.Text = "成功从内存文件中提取了数据";  
101         }  
102   
103         private void frmMain_FormClosing(object sender, FormClosingEventArgs e)  
104         {  
105             if (accessor != null)  
106                 accessor.Dispose();  
107             if (file != null)  
108                 file.Dispose();  
109         }  
110     }
111 }
View Code

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

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

示例3代码如下:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.Linq;
  7 using System.Text;
  8 using System.Windows.Forms;
  9 
 10 using System.IO;
 11 using System.IO.MemoryMappedFiles;
 12 using System.Runtime.Serialization;
 13 using System.Runtime.Serialization.Formatters.Binary;
 14 
 15 
 16 namespace MemoryMappingFile
 17 {
 18     
 19     public partial class Form3 : Form
 20     {
 21         public Form3()
 22         {
 23             InitializeComponent();
 24             InitMemoryMappedFile();
 25         }
 26 
 27         [Serializable]
 28         class MyPic
 29         {
 30             public Image pic;
 31             public string picInfo;
 32         }
 33 
 34         /// <summary>  
 35         /// 图片  
 36         /// </summary>  
 37         private Image bmp
 38         {
 39             get
 40             {
 41                 return pictureBox1.Image;
 42             }
 43             set
 44             {
 45                 pictureBox1.Image = value;
 46             }
 47         }
 48 
 49         /// <summary>  
 50         /// 图片说明  
 51         /// </summary>  
 52         private string info
 53         {
 54             get
 55             {
 56                 return txtImageInfo.Text;
 57             }
 58             set
 59             {
 60                 txtImageInfo.Text = value;
 61             }
 62         }
 63 
 64         private MemoryMappedFile memoryFile = null;
 65 
 66         private MemoryMappedViewStream stream = null;
 67 
 68         /// <summary>  
 69         /// 最大容量:10M  
 70         /// </summary>  
 71         private const int FileSize = 1024 * 1024 * 10;
 72 
 73         /// <summary>  
 74         /// 创建内存映射文件,获取其读写流  
 75         /// </summary>  
 76         private void InitMemoryMappedFile()
 77         {
 78             try
 79             {
 80                 memoryFile = MemoryMappedFile.CreateOrOpen("UseMMFBetweenProcess2", FileSize);
 81                 stream = memoryFile.CreateViewStream();
 82             }
 83             catch (Exception ex)
 84             {
 85                 MessageBox.Show(ex.Message);
 86                 Close();
 87             }
 88         }
 89         /// <summary>  
 90         /// 释放相关资源  
 91         /// </summary>  
 92         private void DisposeMemoryMappedFile()
 93         {
 94             if (stream != null)
 95                 stream.Close();
 96             if (memoryFile != null)
 97                 memoryFile.Dispose();
 98         }
 99 
100         private void btnLoadPic_Click(object sender, EventArgs e)
101         {
102             ChooseImageFile();
103         }
104 
105         //选择图片  
106         private void ChooseImageFile()
107         {
108             if (openFileDialog1.ShowDialog() == DialogResult.OK)
109             {
110                 bmp = new Bitmap(openFileDialog1.FileName);
111             }
112         }
113         //根据用户设定的信息创建对象  
114         private MyPic CreateMyPicObj()
115         {
116             MyPic obj = new MyPic();
117             obj.pic = bmp;
118             obj.picInfo = info;
119             return obj;
120         }
121 
122         /// <summary>  
123         /// 将MyPic对象保存到内存映射文件中  
124         /// </summary>  
125         private void SaveToMMF()
126         {
127             try
128             {
129                 MyPic obj = CreateMyPicObj();
130                 IFormatter formatter = new BinaryFormatter();
131                 stream.Seek(0, SeekOrigin.Begin);
132                 formatter.Serialize(stream, obj);
133                 MessageBox.Show("对象已保存到内存映射文件中");
134             }
135             catch (Exception ex)
136             {
137                 MessageBox.Show(ex.Message);
138             }
139         }
140 
141         private void LoadFromMMF()
142         {
143             try
144             {
145                 // CreateMyPicObj();  
146                 IFormatter formatter = new BinaryFormatter();
147                 stream.Seek(0, SeekOrigin.Begin);
148                 MyPic obj = formatter.Deserialize(stream) as MyPic;
149                 if (obj != null)
150                 {
151                     bmp = obj.pic;
152                     info = obj.picInfo;
153                 }
154             }
155             catch (Exception ex)
156             {
157                 MessageBox.Show(ex.Message);
158             }
159         }
160 
161         private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
162         {
163             DisposeMemoryMappedFile();
164         }
165 
166         private void btnSaveToMMF_Click(object sender, EventArgs e)
167         {
168             SaveToMMF();
169         }
170 
171         private void btnLoadFromMMF_Click(object sender, EventArgs e)
172         {
173             LoadFromMMF();
174         }
175 
176 
177     }
178 }
View Code

 

参考

[1] C#内存映射文件学习

[2] 内存映射文件

posted @ 2015-04-01 11:20  明-Ming  阅读(3126)  评论(0编辑  收藏  举报