旧版Linux(内核<4.17)下安全映射共享内存至固定地址的方法
低内核版本下安全映射共享内存到固定地址的方案
针对Linux内核版本低于4.17、无法使用MAP_FIXED_NOREPLACE的场景,有几种相对安全的方式实现共享内存固定地址映射,避免直接用MAP_FIXED覆盖现有有效映射的风险:
先探测地址可用性再执行固定映射
先调用不带MAP_FIXED标志的mmap(),将目标地址作为addr参数传入(仅作为地址提示)。如果返回值等于目标地址,说明该地址当前处于空闲状态。此时再调用带MAP_FIXED的mmap(),将共享内存映射到该地址即可。
注意:这种方法存在竞态窗口——探测操作和正式映射之间,其他进程可能抢占该地址。如果由父进程先创建共享内存并完成映射,子进程再继承或跟进映射,竞态风险会大幅降低。
示例代码片段:#include <sys/mman.h> #include <unistd.h> void *target_addr = (void *)0x12340000; size_t shm_size = 4096 * 4; // 按实际需求调整 int shm_fd; // 已打开的共享内存文件描述符 // 探测目标地址是否可用 void *probe = mmap(target_addr, getpagesize(), PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (probe == target_addr) { // 地址可用,先释放探测用的匿名映射 munmap(probe, getpagesize()); // 正式映射共享内存到固定地址 void *shm_addr = mmap(target_addr, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, shm_fd, 0); if (shm_addr == MAP_FAILED) { // 处理映射错误 perror("mmap fixed failed"); } } else { // 目标地址已被占用,释放探测映射并处理错误 if (probe != MAP_FAILED) munmap(probe, getpagesize()); fprintf(stderr, "Target address is already in use\n"); }预留地址空间后再映射共享内存
先通过匿名映射预留一块目标地址范围,权限设为PROT_NONE(不可访问),这样操作系统会将这块地址标记为已占用,不会分配给其他内存操作。之后再用MAP_FIXED将共享内存映射到这块预留地址,因为地址已被预留,不会覆盖现有有效映射。
这种方法没有竞态问题,是多进程场景下最安全的方案。示例代码片段:#include <sys/mman.h> void *target_addr = (void *)0x12340000; size_t shm_size = 4096 * 4; int shm_fd; // 预留目标地址空间 void *reserved = mmap(target_addr, shm_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (reserved != target_addr) { perror("Reserve address failed"); return -1; } // 映射共享内存到预留地址 void *shm_addr = mmap(target_addr, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, shm_fd, 0); if (shm_addr == MAP_FAILED) { perror("Map shared memory failed"); munmap(reserved, shm_size); return -1; }调整进程地址空间布局
通过编译或链接选项,让进程的默认代码段、数据段避开目标共享内存地址。比如使用链接器选项-Wl,--section-start=.data=0x20000000调整数据段起始地址,或者用setrlimit()限制进程地址空间大小,让目标地址落在未被使用的区域。这种方法兼容性稍差,需要根据目标系统的编译器、内核版本进行测试验证。
内容的提问来源于stack exchange,提问作者NoamV
相关产品推荐
相关产品推荐

