前言

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平台中的创建行为:

  1. 通过文件路径的方式,如果对应的文件不存在则自动创建一个对应指定capacity大小的文件。
  2. 通过文件流的方式,如果对应文件流的大小小于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 的开发笔记的更多相关文章

  1. 嵌入式linux驱动开发 笔记

    @ 目录 首个驱动hellodrv 1.编写源码 2.编译模块 3.加载驱动 首个驱动hellodrv 3.如果下载不到,就自己编写,并编译驱动. 1.编写源码 2.编译模块 1.先写makefile ...

  2. 驱动开发学习笔记. 0.06 嵌入式linux视频开发之预备知识

    驱动开发读书笔记. 0.06  嵌入式linux视频开发之预备知识 由于毕业设计选择了嵌入式linux视频开发相关的项目,于是找了相关的资料,下面是一下预备知识 UVC : UVC,全称为:USB v ...

  3. Linux及Arm-Linux程序开发笔记(零基础入门篇)

    Linux及Arm-Linux程序开发笔记(零基础入门篇)  作者:一点一滴的Beer http://beer.cnblogs.com/ 本文地址:http://www.cnblogs.com/bee ...

  4. 【Linux开发】Linux及Arm-Linux程序开发笔记(零基础入门篇)

    Linux及Arm-Linux程序开发笔记(零基础入门篇) 作者:一点一滴的Beer http://beer.cnblogs.com/ 本文地址:http://www.cnblogs.com/beer ...

  5. 嵌入式linux应用开发完全手册学习笔记一

    2015.3.25星期三 晴 有两个星期没写学习日记了,找个时间把这段时间做的电子词典和ARM小项目总结一下. 下面的知识点总结,U-BOOT:参考PDF文档:嵌入式linux应用开发完全手册 当虚拟 ...

  6. Linux实战教学笔记08:Linux 文件的属性(上半部分)

    第八节 Linux 文件的属性(上半部分) 标签(空格分隔):Linux实战教学笔记 第1章 Linux中的文件 1.1 文件属性概述(ls -lhi) linux里一切皆文件 Linux系统中的文件 ...

  7. Linux实战教学笔记03:操作系统发展历程及系统版本选择

    标签(空格分隔): Linux实战教学笔记-陈思齐 第1章 Linux简介 1.1 什么是操作系统? 简单讲:操作系统就是一个人与计算机硬件的中介. 操作系统,英文名称Operating System ...

  8. Linux实战教学笔记02:计算机系统硬件核心知识

    标签(空格分隔):Linux实战教学笔记-陈思齐 第1章 互联网企业常见服务器介绍 1.1 互联网公司服务器品牌 - DELL(大多数公司,常用) - HP - IBM(百度在用) 浪潮 联想 航天联 ...

  9. storysnail的Linux串口编程笔记

    storysnail的Linux串口编程笔记 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据Ge ...

随机推荐

  1. docker——容器安装tomcat

    写在前面: 继续docker的学习,学习了docker的基本常用命令之后,我在docker上安装jdk,tomcat两个基本的java web工具,这里对操作流程记录一下. 软件准备: 1.jdk-7 ...

  2. mysql 7下载安装及问题解决

    mysql 7安装及问题解决 一.mysql下载 下载地址:https://www.mysql.com/downloads/ Community (GPL) Downloads MySQL Commu ...

  3. pt-mext

    pt-mext实现的功能比较简单,就是将mysqladmin输出的多次迭代的相同status变量值放到同一行输出. 参数很少,除了--help和--version外,只有一个--relative参数 ...

  4. dagger2系列之Scope

    Dagger的Scope注解代表的是作用域,通过实现自定义@Scope注解,标记当前生成对象的使用范围,标识一个类型的注射器只实例化一次,在同一个作用域内,只会生成一个实例, 然后在此作用域内共用一个 ...

  5. AFNetworking 3.0 源码解读(九)之 AFNetworkActivityIndicatorManager

    让我们的APP像艺术品一样优雅,开发工程师更像是一名匠人,不仅需要精湛的技艺,而且要有一颗匠心. 前言 AFNetworkActivityIndicatorManager 是对状态栏中网络激活那个小控 ...

  6. java使用websocket,并且获取HttpSession,源码分析

    转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...

  7. [原]分享一下我和MongoDB与Redis那些事

    缘起:来自于我在近期一个项目上遇到的问题,在Segmentfault上发表了提问 知识背景: 对不是很熟悉MongoDB和Redis的同学做一下介绍. 1.MongoDB数组查询:MongoDB自带L ...

  8. 【夯实Mysql基础】MySQL性能优化的21个最佳实践 和 mysql使用索引

    本文地址 分享提纲: 1.为查询缓存优化你的查询 2. EXPLAIN 你的 SELECT 查询 3. 当只要一行数据时使用 LIMIT 1 4. 为搜索字段建索引 5. 在Join表的时候使用相当类 ...

  9. ABP领域层

    1.实体Entites 1.1 概念 实体是DDD(领域驱动设计)的核心概念之一. 实体是具有唯一标识的ID且存储在数据库总.实体通常被映射成数据库中的一个表. 在ABP中,实体继承自Entity类. ...

  10. ios label 自动计算行高详解

    在OC当中自动计算行高主要调用系统的 p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #ffffff } span ...