来看下上文介绍的mmap()的函数原型是怎样的:

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

看起来参数好像比较多,但对照着下面这张图看,应该会清晰不少。

其中fd, offsetlength都是用来描述要映射的文件区域的,"fd"是文件描述符,对于匿名映射,fd应该是-1(如果是通过打开/dev/zero这个特殊的文件来创建匿名映射,则它也是有不为-1的正常fd值的)。"offset"是文件中映射的起始位置,"length"是映射的长度。如果访问了超出映射的区域,则有可能触发SIGSEGV异常(segmentation fault)。

为什么说是有可能呢?因为只有访问的地址没有落在进程的任何VMAs(关于VMA请参考这篇文章)里,才应该触发SIGSEGV,如果超出映射的这段区域正好落在另一个VMA里,那就可以侥幸逃过SIGSEGV。其实这样更糟糕,触发SIGSEGV可以让你及时知道事故的第一现场,如果继续运行,然后在某个不可预知的地方崩溃,那这bug排查起来就困难了。

图片来源 - 《Linux环境编程:从应用到内核》

Linux是以page为单位管理内存的,mmap也是以page为单位建立映射的,因此offset必须是按page size对齐的(不对齐的话就会映射失败)。文件是有长度的,offset和offset+length的值都应该小于被映射文件的长度。

然而,mmap()并不会对此作出检查,所以有可能建立的映射区域并不完全在文件的长度范围内。比如一个文件的长度是4KB,而你映射了8KB,那么在访问后面的这4KB内存的时候,就会触发SIGBUS异常,表明你访问的这段内存区域,没有对应的文件。

如果用户给定的length不是按page size对齐的,那么内核会填充一部分长度以保证对齐(可见,offset对齐是用户自己保证的,length对齐是靠内核来保证的)。比如文件长度是10KB,你映射了5KB,那么内核会将其扩充到8KB。如果你访问下图中5120字节到8191字节的这段内存,因为既没有超出映射的内存区域,也没有超出文件的长度,所以是不会触发任何异常的。

你以为只要保证映射的区域完全落在文件的长度范围内就可以高枕无忧了么?事务都是变化的,被映射的文件也不例外,比如它可以通过truncate()/ftruncate()截断,截断之后文件的长度如果减小了(truncate也是可以增大的),然后你刚好访问了被截断的这段区域,依然会触发SIGBUS。

映射区域的大小可以通过mremap()动态调整,事实上,glibc中的realloc()就是调用mremap()实现的。

"prot"是protection的意思,表示的是对内存映射区域的保护,包括PROT_READ(可读),PROT_WRITE(可写)和PROT_EXEC(可执行)。还有一个很特殊的PROT_NONE,就是既不可读也不可写更不可执行,啥操作都不可以,那映射出来干吗?

普通场景下没有用,不代表特殊场景下没用,PROT_NONE可以用于实现防范攻击的guard page。如果攻击者访问了某个guard page,就会触发SIGSEV,作用和地雷是差不多的。在布了地雷的土地上,你不能种田,不能盖房,但是可以防范敌人的入侵,只是,你得记住地雷放的位置,别自己踩着了。

prot属性是可以通过mprotect()动态修改的(mprotect并不局限于操作由mmap映射的内存区域,它可以操作任意区域的内存),这篇文章介绍的JIT实现就是对此一个颇有意思的应用。

"flags"用于指定映射是基于文件的还是匿名(MAP_ANONYMOUS)的,是共享的(MAP_SHARED)还是私有的(MAP_PRIVATE)。上文介绍过,共享映射和私有映射在写操作上是有区别的,前者是直接写,后者是COW,先copy再写。

addr用于指定映射到的VMA的起始地址,这个地址也必须按page size对齐。映射是由内核完成的,但进程可以通过addr参数建议一个它认为的最佳地址(没有这种要求就设置addr为NULL),毕竟进程最了解它自身的应用场景嘛。

但这对内核来说不是强制的,如果addr和addr+length之间的虚拟内存空间恰好是可用的,那么内核会满足进程的这一要求。如果flags中加上MAP_FIXED,那就是进程要求必须映射到这个addr起始的区域,当然,这会增加映射失败的概率。

实际映射到的VMA起始地址保存在mmap()的返回值中。

同read()和write()一样,要获得mmap()中的参数fd,自然是要先open()一下,而调用open()的时候是需要设置对文件的读写权限的,比如O_RDONLY(只读),O_RDWD(可读写),mmap()中prot和flags的设置需要和进程打开文件时的属性设置保持一致。

上文提到,mmap()的实现主要是在进程虚拟地址空间创建了一个VMA,而一个VMA是有VM_READ, VM_WRITE, VM_EXEC, VM_SHARED等诸多属性的。如果是通过mmap创建的VMA,则这个VMA的属性就是由mmap()中prot和flags参数传递进去的。

参考:

《Linux环境编程:从应用到内核》第11.4节

原创文章,转载请注明出处。

内存映射文件进行写文件和读文件有啥不同_Linux中的mmap映射 [二]相关推荐

  1. python 读文件写数据库_python读文件写数据库

    Python 读文件,写数据库 Python 读文件 打开文件 Python读写文件在计算机语言中被广泛的应用,如果你想了解其应用的程序,以下的文章会给你详细的介绍相关内容,会你在以后的学习的过程中有 ...

  2. C++之文件操作探究(四):读文件——二进制文件

    相关博文:C++之文件操作探究(一):写文件--文本文件 相关博文:C++之文件操作探究(二):读文件--文本文件 相关博文:C++之文件操作探究(三):写文件--二进制文件 相关博文:C++之文件操 ...

  3. Java读 写文本_java读文件写文件的方法

    java读文件写文件的方法 有的时候经常为真么读写文件最合理发愁,因为JAVA提过读写文件的方式太多了(C更甚至,fopen & open又有多少人傻傻分不清,更别说ReadFile了).今天 ...

  4. oracle存储过程文件在哪,Oracle存储过程读文件-Oracle

    Oracle存储过程读文件 create or replace PROCEDURE PR_FINANCE_PRODUCT_PARSE ( v_filename  IN  VARCHAR2,   –解析 ...

  5. python将txt文件转化为矩阵_python 读文件,然后转化为矩阵的实例

    代码流程: 1. 从文件中读入数据. 2. 将数据转化成矩阵的形式. 3. 对于矩阵进行处理. 具体的python代码如下: - 文件路径需要设置正确. - 字符串处理. - 字符串数组到 整型数组的 ...

  6. 读一个文件的java程序_java 读文件的几种方法(一)

    先前使用一直很混乱的使用过这些方法,但都没有好好做过总结. 这两天趁有空,小结一下. 1.输入输出流 说起 java的 读写文件不得不说java中的输入输出流对象, 被读取的对象为输入流(如某个文件, ...

  7. python播放wav文件_python3 写一个WAV音频文件播放器的代码

    环境:ubuntu 16.04 python3.5 pycharm 包 : wave pyaudio sys 上代码:AudioPlayer.py # coding:utf-8 # author:ki ...

  8. 单片机C语言头文件如何写,51单片机头文件怎么写

    本文收集整理关于51单片机头文件怎么写的相关议题,使用内容导航快速到达. 内容导航: Q1:51单片机的头文件究竟该如何写 举个例子先判断是否被定义过,没定义就先定义好,主要就是防止重复包含 #ifn ...

  9. 如何写sybase sql脚本文件_写一个删除lock文件的skill脚本

    Cadence软件使用过程中偶尔会遇到文件被lock的情况,这是Cadence软件对文件的一种保护措施,大部分情况都是对用户友好的,但是偶尔也会给用户带来困扰,给工作带来的一定的不便. 文件是如何被锁 ...

最新文章

  1. 3W字长文带你轻松入门视觉Transformer
  2. 边缘计算技术发展与对策研究
  3. mybatis 分页需要的jar包下载_牛逼哄哄的PageHelper分页插件到底牛在哪里?
  4. CDC之CreateCompatibleDC与BitBlt
  5. NIPS2018 Workshop一览
  6. NA-NP-IE系列实验44: 划分VLAN
  7. cordova 调用java_Cordova调用原生方法的插件的编写
  8. MATLAB批量读取图片,剪切,存储,放大
  9. box怎么用 latency_box-sizing使用场景
  10. 属性篇(2)—If you love css …
  11. arduino学习笔记-库函数解析_LiquidCrystal_i2c使用说明以及lcd1602的驱动
  12. vb.net中的换行
  13. Java前后端分离项目部署
  14. nginx 配置https 负载均衡
  15. 手写解析微信Matrix性能监控日志的工具
  16. 中国手机用户换机越来越慢
  17. matlab convexhull,使用convex_hull计算体积为0
  18. vue嵌入app中——首次加载慢的动画
  19. boost::serialization模块extended_type_info_typeid测试程序
  20. LaTeX排版系统及与word的区别

热门文章

  1. 升级 ServeRADI-8i控制器,使用IBM 3650 9797 老服务器支持2T 硬盘
  2. C#中return语句的使用方法
  3. 手把手教你怎么用动软.net代码生成器 搭建三层架构
  4. 点站点链接出现短时间白屏或闪屏现象
  5. 9月21日云栖精选夜读 | 如何优雅地从四个方面加深对深度学习的理解
  6. 最好用的markdown编辑器推荐typora
  7. Python json序列化时default/object_hook指定函数处理
  8. 在CentOS上安装Git
  9. mysql SQLyog导入导出csv文件
  10. [转]C语言茶余饭后之if...else PK switch...case