MemoryMappedFile 在 Mono in Linux 的开发笔记
前言
MemoryMappedFile(简称MMF)类是.NET中对内存映射文件进行操作的类,内存映射文件是非常高效的本地IO方案,由操作系统提供内存与IO文件之间的映射转换,对内存映射文件的更改由操作系统自动与物理文件进行高效的数据交换。在大文件处理中一般都需要使用到它,同时它也被用来做高效的进程间通讯的底层技术。
正因为它是如此的高效和便捷,所以在服务器程序开发中被广泛使用到。譬如,我们实现的基于Socket网络通讯程序中,在发送大数据时,需要对数据进行拆包组包的操作,这就往往需要对未接收完全的数据包进行缓存,在这个的场景中最好是使用MMF手动来对通讯包数据的缓存管理,倘若直接把这些数据放在.NET内置的集合、列表或字典中,那很可能会把.NET托管内存撑爆的。
当我把基于Socket的通讯类库代码运行在Mono on Linux中的时候,发现其使用到的MMF代码运行时异常了,本文就是对这一问题的处理过程的记录。
问题说明
Mono on Linux
我的代码是通过指定文件路径的方式创建MMF的,譬如文件路径为:/tmp/Zongsoft.Communication.Net#1.cache,在Windows中运行的很正常,但是在Mono on Linux中发生运行时异常:
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时,传入的目标文件必须是已经存在,且该文件长度不能为零。
由于刚才那个文件流的长度正好与创建MMF时capacity参数值相同,那么接下来我再来测试下,当文件流的长度与创建MMF时指定的capacity数值不同时,分别会发生什么情况。
1、文件流的长度大于创建MMF时capacity,成功,并且MMF创建后不会改变对应文件流的大小;
2、文件流的长度小于创建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
.NET on Windows
再简单说明下MemoryMappedFile类在.NET Windows平台中的创建行为:
- 通过文件路径的方式,如果对应的文件不存在则自动创建一个对应指定capacity大小的文件。
- 通过文件流的方式,如果对应文件流的大小小于capacity则成功;如果对应文件流大小大于capacity则失败,提示指定的文件流大小太小。
Mono 源码简查
通过查看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参数值相同。
MemoryMappedFile 在 Mono in Linux 的开发笔记的更多相关文章
- 嵌入式linux驱动开发 笔记
@ 目录 首个驱动hellodrv 1.编写源码 2.编译模块 3.加载驱动 首个驱动hellodrv 3.如果下载不到,就自己编写,并编译驱动. 1.编写源码 2.编译模块 1.先写makefile ...
- 驱动开发学习笔记. 0.06 嵌入式linux视频开发之预备知识
驱动开发读书笔记. 0.06 嵌入式linux视频开发之预备知识 由于毕业设计选择了嵌入式linux视频开发相关的项目,于是找了相关的资料,下面是一下预备知识 UVC : UVC,全称为:USB v ...
- Linux及Arm-Linux程序开发笔记(零基础入门篇)
Linux及Arm-Linux程序开发笔记(零基础入门篇) 作者:一点一滴的Beer http://beer.cnblogs.com/ 本文地址:http://www.cnblogs.com/bee ...
- 【Linux开发】Linux及Arm-Linux程序开发笔记(零基础入门篇)
Linux及Arm-Linux程序开发笔记(零基础入门篇) 作者:一点一滴的Beer http://beer.cnblogs.com/ 本文地址:http://www.cnblogs.com/beer ...
- 嵌入式linux应用开发完全手册学习笔记一
2015.3.25星期三 晴 有两个星期没写学习日记了,找个时间把这段时间做的电子词典和ARM小项目总结一下. 下面的知识点总结,U-BOOT:参考PDF文档:嵌入式linux应用开发完全手册 当虚拟 ...
- Linux实战教学笔记08:Linux 文件的属性(上半部分)
第八节 Linux 文件的属性(上半部分) 标签(空格分隔):Linux实战教学笔记 第1章 Linux中的文件 1.1 文件属性概述(ls -lhi) linux里一切皆文件 Linux系统中的文件 ...
- Linux实战教学笔记03:操作系统发展历程及系统版本选择
标签(空格分隔): Linux实战教学笔记-陈思齐 第1章 Linux简介 1.1 什么是操作系统? 简单讲:操作系统就是一个人与计算机硬件的中介. 操作系统,英文名称Operating System ...
- Linux实战教学笔记02:计算机系统硬件核心知识
标签(空格分隔):Linux实战教学笔记-陈思齐 第1章 互联网企业常见服务器介绍 1.1 互联网公司服务器品牌 - DELL(大多数公司,常用) - HP - IBM(百度在用) 浪潮 联想 航天联 ...
- storysnail的Linux串口编程笔记
storysnail的Linux串口编程笔记 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据Ge ...
随机推荐
- 01.SQLServer性能优化之----强大的文件组----分盘存储
汇总篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql 文章内容皆自己的理解,如有不足之处欢迎指正~谢谢 前天有学弟问逆天:“逆天,有没有一种方 ...
- 测试一下StringBuffer和StringBuilder及字面常量拼接三种字符串的效率
之前一篇里写过字符串常用类的三种方式<java中的字符串相关知识整理>,只不过这个只是分析并不知道他们之间会有多大的区别,或者所谓的StringBuffer能提升多少拼接效率呢?为此写个简 ...
- 07. Web大前端时代之:HTML5+CSS3入门系列~H5 地理位置
Web大前端时代之:HTML5+CSS3入门系列:http://www.cnblogs.com/dunitian/p/5121725.html 源码:https://github.com/duniti ...
- 谈谈一些有趣的CSS题目(九)-- 巧妙的实现 CSS 斜线
开本系列,谈谈一些有趣的 CSS 题目,题目类型天马行空,想到什么说什么,不仅为了拓宽一下解决问题的思路,更涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题 ...
- SignalR SelfHost实时消息,集成到web中,实现服务器消息推送
先前用过两次SignalR,但是中途有段时间没弄了,今天重新弄,发现已经忘得差不多了,做个笔记! 首先创建一个控制台项目Nuget添加引用联机搜索:Microsoft.AspNet.SignalR.S ...
- 一款开源免费的.NET文档操作组件DocX(.NET组件介绍之一)
在目前的软件项目中,都会较多的使用到对文档的操作,用于记录和统计相关业务信息.由于系统自身提供了对文档的相关操作,所以在一定程度上极大的简化了软件使用者的工作量. 在.NET项目中如果用户提出了相关文 ...
- 2016/12/31_Python
今天学习主要内容: Python: 1.with语句(补充昨天的文件操作) 用with打开的文件在脚本结束会自动关闭,以防普通打开方式忘记关闭文件连接 语法: with open("demo ...
- 集合(set)-Python3
set 的 remove() 和 discard() 方法介绍. 函数/方法名 等价操作符 说明 所有集合类型 len(s) 集合基数:集合s中元素个数 set([obj]) 可变集合工 ...
- Android Bitmap 和 ByteArray的互相转换
Android Bitmap 和 ByteArray的互相转换 移动平台图像处理,需要将图像传给native处理,如何传递?将bitmap转换成一个 byte[] 方便传递也方便cpp代码直接处理图像 ...
- OpenGL ES 3.0: 图元重启(Primitive restart)
[TOC] 背景概述 在OpenGL绘制图形时,可能需要绘制多个并不相连的图形.这样的情况下这几个图形没法被当做一个图形来处理.也就需要多次调用 DrawArrays 或 DrawElements. ...