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參數值相同。