了解一下mmap函数

请注意,此文主要是针对BSD 系统的调用。

这是一个c标准库(libc, -lc)里的函数。

它的主要功能,概括的说,就是分配内存,或将文件或设备映射到内存中去。

在使用此函数前,你需要引入头文件,#include <sys/mman.h>,函数声明是这样的,

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

下面我们来详细的了解一下此函数,

对 mmap() 系统调用,将产生,从地址 addr 开始,并持续最多 len 个字节大小的页面,对,以 fd 描述的对象从字节偏移 offset 开始,的映射。注意,offset 或 len最好指定为页面大小(pagesize)的倍数,如果 offset 或 len 不是页面的倍数,则映射区域可能会超出指定范围。超出映射对象末尾的任何扩展都将被零填充。offset参数一般设为0,表示从文件头开始映射。

        系统使用 addr 参数来确定映射的起始地址,其解释取决于 MAP_FIXED 标志的设置。怎么说呢,如果在 flags 中指定了 MAP_FIXED,系统将尝试将映射放置在指定地址,可能会删除该位置已存在的映射。如果未指定 MAP_FIXED,则系统将尝试使用从 addr 开始的地址范围,前提是它们不与任何现有映射重叠,包括由 malloc(3) 和其他此类分配器分配的内存。否则,系统将为映射(使用与实现相关的算法)选择不与任何现有映射重叠的备用地址。换句话说,如果没有指定 MAP_FIXED,如果指定的地址范围已经被其他对象映射,系统将尝试在地址空间中找到一个空位置。如果 addr 为零且未指定 MAP_FIXED,则系统将选择一个地址,以免与地址空间中的任何现有映射重叠。

在所有情况下,都会返回该区域的实际起始地址。如果指定了 MAP_FIXED,则成功的 mmap 会删除分配的地址范围内的任何先前映射。如果未指定 MAP_FIXED,则永远不会删除以前的映射。

映射区域的保护(即对应区域的可访问性)在 prot 参数中单一指定,或者通过或' | '运算符复合指定:

     PROT_NONE 页面可能无法访问。
     PROT_READ 可以读取页面。
     PROT_WRITE 可以写入页面。
     PROT_EXEC 页面可以被执行。

请注意,由于硬件限制,在某些平台上,单独指定PROT_WRITE 可能兼具 PROT_READ参数,而 单独指定PROT_READ 可能兼具 PROT_EXEC参数。 可移植程序不应该依赖这些单独强制执行的标志。

flags 参数指定映射对象的类型、映射选项以及对页面的映射副本所做的修改是进程私有的(copy-on-write)还是要与其他引用共享。共享、映射类型和选项在 flags 参数中通过以下值指定:

        MAP_ANONYMOUS     与 MAP_ANON 标志相同.
        MAP_ANON         映射与任何特定文件无关的匿名内存。 offset参数被忽略。 Mac OS X 规定: 用于创建 MAP_ANON 区域的文件描述符可用于传递一些 Mach VM 标志,如果没有与该区域关联的此类标志,则可以将其指定为 -1。 Mach VM 标志在 <mach/vm_statistics.h> 中定义,当前适用于 mmap 的标志是:

                VM_FLAGS_PURGABLE    用于创建Mach可清除(即,volatile可见性)内存。

                VM_MAKE_TAG(tag)         将 8 位标签与区域相关联。<mach/vm_statistics.h> 定义了一些预设tags(带有 VM_MEMORY_ 前缀)。 鼓励用户使用 240 到 255 之间的tags。这些tags被用于vmmap(1) 等工具来帮助识别特定的内存区域。

        MAP_FILE                      从常规文件映射。(这是默认映射类型,无需指定。)

        MAP_FIXED                    不允许系统选择与指定地址不同的地址。如果无法使用指定的地址,mmap() 将失败。如果指定了 MAP_FIXED,则 addr 必须是页面大小的倍数。如果 MAP_FIXED 请求成功,则 mmap() 建立的映射将替换从 addr 到 addr + len 范围内进程页面的任何先前映射。不鼓励使用此选项。

        MAP_HASSEMAPHORE     通知内核该区域可能包含信号量并且可能需要特殊处理。

        MAP_PRIVATE                     修改是私有的(copy-on-write)。

        MAP_SHARED                     修改是共享的。

        MAP_NOCACHE                   此映射中的页面不会保留在内核的内存缓存中。如果系统内存不足,MAP_NOCACHE 映射中的页面将首先被回收。此标志适用于具有很少局部性的映射,并向内核提示在不久的将来不太可能再次需要此映射中的页面。

注意,应用程序必须指定 MAP_PRIVATE 或 MAP_SHARED。

close(2) 系统调用不会取消映射页面,有关详细信息,请参阅 munmap(2)。

当前设计不允许进程指定交换空间的位置。将来我们可能会定义一个额外的映射类型,MAP_SWAP,其中文件描述符参数指定应该进行交换的文件或设备。

返回值

        成功完成后,mmap() 返回一个指向映射区域的指针。 否则,返回 MAP_FAILED 值并设置 errno 以指示错误。

错误返回值

        在以下情况下 mmap() 系统调用将失败:

        [EACCES]        标志 PROT_READ 被指定为 prot 参数的一部分,并且 fd 未以可读的方式打开。 标志 MAP_SHARED 和 PROT_WRITE 被指定为标志和 prot 参数的一部分,并且 fd 没有以可写方式打开。

        [EBADF]        fd 参数不是一个有效的打开文件的描述符。

        [EINVAL]        指定了 MAP_FIXED 并且 addr 参数不是页面对齐的,或者所需地址空间的一部分位于用户进程的有效地址空间之外。

                                标志不包括 MAP_PRIVATE 或 MAP_SHARED。

                                len 参数为负数或零。 从历史上看,如果参数为零,系统调用不会返回错误。 请参阅下面的“兼容性”部分中的其他潜在附加限制。

                                offset 参数未根据 getpagesize(3) 返回的页面大小进行页面对齐。

        [ENODEV]        未指定 MAP_ANON 且 fd 引用的文件不支持映射。

        [ENOMEM]      指定了 MAP_FIXED 并且 addr 参数不可用。指定了 MAP_FIXED 并且指定的地址范围超出了进程的地址空间限制。指定了 MAP_ANON,但可用内存不足。

        [ENXIO]        指定范围内的地址对 fd 无效。

        [EOVERFLOW]        指定范围内的地址超出为 fd 设置的最大偏移量。  

        

                        

03-13
<think>好的,用户想了解mmap的使用方法及常见问题解决。首先,我需要回忆mmap的基本概念和主要用途。根据引用[3],mmap通过内存映射减少数据拷贝次数,提升文件操作效率。用户可能需要知道如何调用mmap函数,参数的意义以及实际应用场景。 接下来,我应该整理mmap函数原型和参数说明。引用[3]中提到了mmap函数定义,需要详细解释每个参数的作用,比如addr、length、prot、flags、fd和offset。同时,flags参数中的MAP_SHARED和MAP_PRIVATE需要区分清楚,因为它们决定了映射内存的可见性。 然后,常见问题部分需要结合引用[2]和[4]。比如权限错误可能和prot参数设置有关,文件大小不足需要调整length或提前扩展文件。此外,内存对齐的问题在引用[4]中提到的mmap.ninja项目可能涉及,可以建议使用适当的内存对齐方法或工具。 应用场景方面,引用[1]和[3]提到的高效文件IO、进程间通信和大文件处理是重点。需要举一些具体的例子,比如数据库管理系统使用mmap优化读写,或者图像处理库处理大文件。同时,Go语言中的mmap-go项目(引用[2])和Python的mmap模块也是用户可能关心的跨语言应用。 最后,生成相关问题时要覆盖原理、优化、跨语言使用和对比其他IO方法,帮助用户进一步学习。需要确保引用正确,比如引用[3]对应函数参数,引用[2]对应Go的解决方案,引用[4]提到numpy数组的内存映射。</think>### mmap使用方法及常见问题解决 #### 一、mmap核心功能 mmap(内存映射文件)通过建立虚拟内存与文件的直接映射关系,实现$高效文件IO操作$。相比传统read/write系统调用,mmap减少了用户态与内核态之间的数据拷贝次数,性能提升可达$30%-50%$[^3]。 #### 二、函数原型及参数详解 ```c void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); ``` | 参数 | 说明 | |---------|--------------------------------------------------------------------| | addr | 建议映射地址(通常置NULL由系统自动分配) | | length | 映射区域长度(必须>0) | | prot | 保护模式:PROT_READ/PROT_WRITE/PROT_EXEC组合 | | flags | MAP_SHARED(修改同步到文件)或MAP_PRIVATE(创建写时拷贝副本) | | fd | 已打开的文件描述符 | | offset | 文件偏移量(必须是系统页大小的整数倍) | #### 三、典型使用场景 1. **大文件处理**:将文件直接映射到内存,避免加载整个文件到内存(如GIS系统处理地图数据)[^1] 2. **进程间通信**:多个进程映射同一文件实现共享内存(比传统shm更灵活) 3. **随机访问优化**:数据库管理系统常用mmap实现索引快速访问[^4] #### 四、常见问题解决方案 1. **权限错误**: ```c // 错误示例:尝试写入只读映射 mmap(NULL, 4096, PROT_READ, MAP_SHARED, fd, 0); // 正确应设置 PROT_READ | PROT_WRITE ``` 2. **文件大小不足**: ```bash # 先扩展文件大小 truncate -s 1G data.bin ``` 3. **内存对齐问题**: ```python # 使用mmap.ninja处理非对齐访问(Python示例) import mmapninja arr = mmapninja.MemMapArray('data.bin', dtype=np.float32, shape=(1024, 1024))[^4] ``` #### 五、跨语言实现 1. **Go语言**: ```go import "github.com/edsrzf/mmap-go" // 创建可写映射 m, _ := mmap.MapRegion(file, 1024, mmap.RDWR, 0, 0) defer m.Unmap() ``` 2. **Python标准库**: ```python import mmap with open("data.bin", "r+b") as f: mm = mmap.mmap(f.fileno(), 0) mm[0:100] = b'*' * 100 # 直接修改文件内容 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值