mmap详解

mmap()调用请求内核将fd表示的文件中从offset处开始的len个字节数据映射到内存中。如果包含了addr,表明优先使用addr为内存中的开始地址。访存权限由prot指定,flags指定了其他的操作行为。

函数原型

#include <sys/mman.h>

void * mmap (void *addr, 
             size_t len, 
             int prot,
             int flags, 
             int fd, 
             off_t offset);

len参数

需要映射文件的大小

fd参数

需要映射的文件

offset参数

需要映射的文件的偏移位

addr参数

告诉内核映射文件的最佳地址,被设为NULL,意味着匿名映射可以让内核安排的在任意地址上。当然给定一个non-NULL值也是可以的,只要它是页对齐的,但这样会限制了可移植性。实际上很少有程序真正在意映射到哪个地址上去!

prot 参数

描述了对内存区域所请求的访问权限。如果是PROTNONE,此时映射区域无法访问(没有意义),也可以是以下标志位的比特位的或运算值:

  • PROT-READ页面可读。

  • PROT_WRITE 页面可写。

  • PROT_EXEC页面可执行。

prot参数经常都同时设置了PROT_READ和PROT_WRITE位,使得映射是可读可写的。一块不能读写的空存储器映射是没有用的。另外一方面,很少将可执行代码映射到匿名映射,因为那样做能产生潜在的安全漏洞。

flag参数

描述了映射的类型和一些行为。其值为以下值按二进制或运算的

  • MAPFIXED告诉mmap()把addr看作强制性要求,而不是建议。如果内核无法映射文件到指定地址,调用失败。如果地址和长度指定的内存和已有映射有重叠区域,重叠区的原有内容被丢弃,然后被新内容填充。该选项需要深入了解进程的地址空间,不可移植,所以不鼓励使用。

  • MAP_PRIVATE映射区不共享。文件映射采用了写时拷贝,进程对内存的任何改变不影响真正的文件或者其它进程的映射。

  • MAP_SHARED和所有其它映射该文件的进程共享映射内存。对内存的写操作等效于写文件。读该映射区域会受到其他进程的写操作的影响。

  • MAP-ANONYMOUS使得映射是匿名的,假如MAP-ANONYMOUS被设置了,fd和offset参数将被忽略的。然而,在一些更早的系统里,需要让fd为-1,因此如果要考虑到程序的可移植性,那么这是一个好主意。

MAP_SHARED和MAP_PRIVATE必须指定其中之一,但不能同时指定。更多的标志以后再补充。

当你映射一个文件描述符的时候,描述符引用计数增加。因此,如果你映射文件后关闭文件,你的进程依然可以访问该文件。当你取消映射或者进程终止时,对应的文件引用计数会减1。

mmap示例

下面的代码中以只读共享的方式映射fd所指向的文件/root/test2.txt,从第一个字节开始,长度为8192个字节:

[root@ecs-3370 ~]# cat mymmap.c 
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <error.h>
#include <sys/mman.h>
 
main(){
void *p;
int fd;
fd = open ("/root/test2.txt", O_RDONLY);
if (fd == -1)
    perror("fd");
p = mmap (0,8192,PROT_READ,MAP_SHARED,fd,0);
}

页大小

页是内存中允许具有不同权限和行为的最小单元。因此,页是内存映射的基本块,同时也是进程地址空间的基本块。mmap()调用操作页。addr和offset参数都必须按页大小对齐。也就是说,它们必须是页大小的整数倍。

所以,映射区域是整数倍个的页。如果len参数不能按页对齐(可能因为需要映射的文件大小不是页大小的整数倍)映射区域延伸到下个空页。多出来的内存,即最后一个有效字节和映射区域边界的区域,用0填充,该区域的读操作都会返回0。

页大小:页大小由<asm/page_types.h>中的宏PAGE_SIZE定义。因此,第二种获得页大小的方法是:

int page_size= PAGE_SIZE ;

当前系统page大小也可以通过以下方式查询

[root@ecs-3370 ~]# getconf -a | grep -i page
PAGESIZE                           4096
PAGE_SIZE                          4096
_AVPHYS_PAGES                      6939561
_PHYS_PAGES                        8194828

返回值和错误码

调用成功,mmap0返回映射区的地址。失败时,返回MAPFAILED,并设置相应的errno。mmap()从不返回0。

可能的errno值:

  • EACESS 给定的文件描述符不是普通文件,或者打开模式和prot或者flags冲突。

  • EAGAIN 文件已被文件锁锁定。

  • EBADF 给定文件描述符无效。

  • EINVALaddr,len,off中的一个或多个无效。

  • ENFILE 打开文件数达到系统上限。

  • ENODEV 文件所在的文件系统不支持存储映射。

  • ENOMEM 没有足够的内存。

  • EOVERFLOW addr+len的结果超过了地址空间大小。

  • EPERM 设定了PROTEXEC,但是文件系统以不可执行方式挂载。

mmap()的优点

相对于read(),write(),使用mmap)处理文件有很多优点。其中包括:

  • 使用read()或write()系统调用需要从用户缓冲区进行数据读写,而使用映射文件进行操作,可以避免多余的数据拷贝。

  • 除了潜在的页错误,读写映射文件不会带来系统调用和上下文切换的开销。就像直接操作内存一样简单。

  • 当多个进程映射同一个对象到内存中,数据在进程间共享。只读和写共享的映射在全体中都是共享的;私有可写的尚未进行写时拷贝的页是共享的。

  • 在映射对象中搜索只需要一般的指针操作。而不必使用lseek)。

mmap()的缺陷

  • 映射区域的大小通常是页大小的整数倍。因此,映射文件大小与页大小的整数倍之间有空间浪费。对于小文件,较大比重的空间被浪费。例如对于4kb的页,一个7字节的映射浪费了4089字节。

  • 存储映射区域必须在进程地址空间内。对于32位的地址空间,大量的大小各异的映射会导致大量的碎片出现,使得很难找到连续的大片空内存。这个问题在64位地址空间明显减少。

  • 创建和维护映射以及相关的内核数据结构有一定的开销。通过上节提到的消除读写时的不必要拷贝的,这些开销可以忽略,对于大文件和频繁访问的文件更是如此。

基于以上理由,处理大文件(浪费的空间只占很小的比重),或者在文件大小恰好被page大小整除时(没有空间浪费)优势很明显。

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
***种原因。根据引用,在Linux环境下,mmap是一种文件访问的优化手段。当系统限制进程可使用的mmap数量达到上限时,会导致mmap失败。为了解决这个问题,可以通过调整系统参数来增加可使用的mmap数量,具体的调整方式是编辑/etc/sysctl.conf文件并添加以下内容:vm.max_map_count = 1048575。这样可以扩大可使用的mmap数量,从而解决mmap失败的问题。 另外,根据引用,当mmap函数发生错误时,它会返回MAP_FAILED或-1。所以在代码中,应该检查mmap函数的返回值是否为0来判断是否发生错误。 此外,引用还提到了可能由于文件只读却在mmap中使用了PROT_READ | PROT_WRITE(读写)而导致mmap失败的情况。因此,在使用mmap时应确保文件的读写权限与mmap的参数相匹配,以避免mmap失败。 总结起来,mmap失败可能是因为系统限制了可使用的mmap数量,解决方法是调整系统参数。另外,在代码中应该检查mmap函数的返回值是否为0,以判断是否发生错误。此外,还应确保文件的读写权限与mmap的参数相匹配,以避免mmap失败。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [记Cassandra的OOM问题分析和解决(mmap failed)](https://blog.csdn.net/u013887254/article/details/107809312)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [简单的shell脚本来检测mmap()返回值的错误检查-C/C++开发](https://download.csdn.net/download/weixin_42140710/19108465)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [mmap失败时原因](https://blog.csdn.net/qq_16097611/article/details/51916275)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值