MemoryMappedFile(简称MMF)类是.NET中对内存映射文件进行操做的类,内存映射文件是很是高效的本地IO方案,由操做系统提供内存与IO文件之间的映射转换,对内存映射文件的更改由操做系统自动与物理文件进行高效的数据交换。在大文件处理中通常都须要使用到它,同时它也被用来作高效的进程间通信的底层技术。缓存
正由于它是如此的高效和便捷,因此在服务器程序开发中被普遍使用到。譬如,咱们实现的基于Socket网络通信程序中,在发送大数据时,须要对数据进行拆包组包的操做,这就每每须要对未接收彻底的数据包进行缓存,在这个的场景中最好是使用MMF手动来对通信包数据的缓存管理,假若直接把这些数据放在.NET内置的集合、列表或字典中,那极可能会把.NET托管内存撑爆的。服务器
当我把基于Socket的通信类库代码运行在Mono on Linux中的时候,发现其使用到的MMF代码运行时异常了,本文就是对这一问题的处理过程的记录。网络
个人代码是经过指定文件路径的方式建立MMF的,譬如文件路径为:/tmp/Zongsoft.Communication.Net#1.cache,在Windows中运行的很正常,可是在Mono on Linux中发生运行时异常:app
Unhandled Exception:
System.IO.FileNotFoundException: 没有那个文件或目录 ---> Mono.Unix.UnixIOException: 没有那个文件或目录 [ENOENT].
at Mono.Unix.UnixMarshal.ThrowExceptionForLastError () [0x00000] in :0
at System.IO.MemoryMappedFiles.MemoryMapImpl.Open (System.String path, FileMode mode, Int64 capacity, MemoryMappedFileAccess access) [0x00000] in :0
at System.IO.MemoryMappedFiles.MemoryMappedFile.CreateFromFile (System.String path, FileMode mode, System.String mapName, Int64 capacity, MemoryMappedFileAccess access) [0x00000] in :0
难道在Mono中,须要先建立MMF对应的物理文件?好吧,那我就手动在临时文件夹下建立一个指定名称的空文件后,再来跑一遍,结果报了这个: 测试
Unhandled Exception:
System.ArgumentException: capacity
at System.IO.MemoryMappedFiles.MemoryMapImpl.Open (System.String path, FileMode mode, Int64 capacity, MemoryMappedFileAccess access) [0x00000] in :0
at System.IO.MemoryMappedFiles.MemoryMappedFile.CreateFromFile (System.String path, FileMode mode, System.String mapName, Int64 capacity, MemoryMappedFileAccess access) [0x00000] in :0
难道是Mono对经过文件路径来建立MMF支持不力?好吧,那就试试经过文件流的方式来建立MMF吧,结果仍是不行: 大数据
Unhandled Exception:
System.ArgumentException: capacity
at System.IO.MemoryMappedFiles.MemoryMappedFile.CreateFromFile (System.IO.FileStream fileStream, System.String mapName, Int64 capacity, MemoryMappedFileAccess access, System.IO.MemoryMappedFiles.MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability inheritability, Boolean leaveOpen) [0x00000] in :0
看来,应该是跟capacity与物理文件的大小不匹配所致,好吧,那就在建立完文件流后,再使用文件流的SetLength来指定一个长度后,再经过该特定长度的文件流来建立MMF吧,结果果真建立成功!这说明在Mono中的建立MMF时,传入的目标文件必须是已经存在,且该文件长度不能为零。 spa
因为刚才那个文件流的长度正好与建立MMF时capacity参数值相同,那么接下来我再来测试下,当文件流的长度与建立MMF时指定的capacity数值不一样时,分别会发生什么状况。操作系统
一、文件流的长度大于建立MMF时capacity,成功,而且MMF建立后不会改变对应文件流的大小; 进程
二、文件流的长度小于建立MMF时capacity:内存
Unhandled Exception:
System.ArgumentException: capacity
at System.IO.MemoryMappedFiles.MemoryMappedFile.CreateFromFile (System.IO.FileStream fileStream, System.String mapName, Int64 capacity, MemoryMappedFileAccess access, System.IO.MemoryMappedFiles.MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability inheritability, Boolean leaveOpen) [0x00000] in :0
再简单说明下MemoryMappedFile类在.NET Windows平台中的建立行为:
经过查看Mono-3.0.12的源码,发现MMF类的以下代码:
public static MemoryMappedFile CreateNew(string mapName, long capacity, MemoryMappedFileAccess access,
MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity,
HandleInheritability inheritability)
{
return CreateFromFile(mapName, FileMode.CreateNew, mapName, capacity, access);
}
public static MemoryMappedFile CreateOrOpen(string mapName, long capacity, MemoryMappedFileAccess access)
{
return CreateFromFile(mapName, FileMode.OpenOrCreate, mapName, capacity, access);
}
可见它们是都是经过CreateFromFile方法来处理的,而且将mapName做为文件路径来使用,因此,调用这两个方法要留心了,由于它与.NET(Windows)平台的实现是彻底不一样的,除此之外的其余建立或打开方法未实现,请勿使用!代码以下:
public static MemoryMappedFile OpenExisting(string mapName, MemoryMappedFileRights desiredAccessRights, HandleInheritability inheritability)
{
throw new NotImplementedException();
}
综上所述,为了让代码可以在Linux和Windows平台都正常运行,建议统一使用
MemoryMappedFile. CreateFromFile(
FileStream fileStream,
String mapName,
Int64 capacity,
MemoryMappedFileAccess access,
System.IO.MemoryMappedFiles.MemoryMappedFileSecurity memoryMappedFileSecurity,
HandleInheritability inheritability,
Boolean leaveOpen)
方法来建立MMF,而且在调用前确保指定的文件流大小与capacity参数值相同。