mmap调用使用MAP_FIXED时报Invalid argument错误的解决方法
问题背景
运行环境:Ubuntu 16.04,内核版本4.17.4
编写共享内存相关代码时,尝试将进程地址空间映射到创建的共享内存区域,在mmap()调用中使用MAP_FIXED标志时,程序返回如下错误:
mmap: Invalid argument
完整问题代码如下:
#include <stdio.h> #include <sys/mman.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #define STORAGE_ID "/SHM_TEST" #define STORAGE_SIZE 4096 int main(int argc, char *argv[]) { int res, temp; int fd; int len; pid_t pid; void *addr; char data[STORAGE_SIZE]; fd = shm_open(STORAGE_ID, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (fd == -1) { perror("open"); return 10; } res = ftruncate(fd, STORAGE_SIZE); if (res == -1) { perror("ftruncate"); return 20; } addr = mmap(NULL, STORAGE_SIZE, PROT_WRITE, MAP_FIXED, fd, 0); if (addr == MAP_FAILED) { perror("mmap"); return 30; } res = munmap(addr, STORAGE_SIZE); if (res == -1) { perror("munmap"); return 40; } // shm_open cleanup fd = shm_unlink(STORAGE_ID); if (fd == -1) { perror("unlink"); return 100; } return 0; }
使用MAP_FIXED标志的原因是后续需要手动指定映射地址,而非传入NULL参数(尝试传入手动指定的地址时仍然出现相同错误),需要如何操作才能正确在mmap()调用中使用MAP_FIXED标志?
错误原因
你遇到的报错来自两个核心问题:
MAP_FIXED标志使用规则违反:当传入MAP_FIXED时,mmap的第一个参数addr不能为NULL,同时传入的地址必须满足页对齐要求(地址值为系统内存页大小的整数倍),且对应的虚拟地址区间属于进程可用地址范围,否则内核会直接返回EINVAL(也就是Invalid argument错误)。你当前代码直接给MAP_FIXED传NULL作为地址,是触发报错的直接原因。- 缺少必要的映射标志:共享内存映射必须搭配
MAP_SHARED标志,否则映射的是内存私有副本,完全达不到共享内存的通信目的,属于隐性逻辑错误。
正确使用
MAP_FIXED的方法 不要硬编码固定地址(受ASLR地址空间随机化影响,硬编码地址在不同进程、不同运行环境下几乎必然非法),按照以下步骤操作:
- 第一步:先获取系统页大小,可通过
sysconf(_SC_PAGESIZE)获取,你的环境下默认值为4096字节。 - 第二步:先向内核申请一块空闲、页对齐的预留地址区间,避免手动指定的地址已经被代码段、堆、栈或其他映射区域占用。
- 第三步:拿到合法的预留地址后,再搭配
MAP_FIXED和MAP_SHARED标志映射共享内存到该地址。 - 可选安全优化:你的4.17版本内核支持
MAP_FIXED_NOREPLACE标志,相比强制覆盖原有映射的MAP_FIXED,该标志在目标地址已被占用时会直接返回错误,不会破坏进程原有内存映射,稳定性更高。
修正后的核心代码片段
#include <unistd.h> // 引入sysconf对应的头文件 // ... 前面的shm_open、ftruncate逻辑不变 ... long page_size = sysconf(_SC_PAGESIZE); // 先预留一块空闲的合法地址 void *target_addr = mmap(NULL, STORAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (target_addr == MAP_FAILED) { perror("reserve address"); return 25; } // 释放预留地址,该地址短时间内不会被内核分配给其他映射 munmap(target_addr, STORAGE_SIZE); // 带MAP_FIXED映射共享内存,注意必须加MAP_SHARED addr = mmap(target_addr, STORAGE_SIZE, PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0); if (addr == MAP_FAILED) { perror("mmap"); return 30; } // ... 后续逻辑不变 ...
内容的提问来源于stack exchange,提问作者Misbah
相关产品推荐
相关产品推荐

