捕获SIGSEGV后调用mmap映射内存失败,报错Cannot allocate memory
问题分析与解决方案
核心问题原因
在macOS上,你的代码触发mmap返回Cannot allocate memory错误,主要源于以下几点:
1. 信号处理函数注册方式错误
你用signal()注册处理函数,但该函数不支持传递siginfo_t扩展信息,会导致信号处理函数内的si指针无效。更关键的是,这种老旧接口无法保证信号处理的可靠性,间接影响后续内存映射操作。
2. MAP_FIXED的地址冲突问题
MAP_FIXED要求指定地址必须是进程虚拟空间中完全未被占用的页对齐区域。你选择的page_size*100(通常是409600)属于低地址范围,macOS会将这类区域保留给系统或进程的初始内存段,强制映射必然失败。
3. 信号上下文的内存分配限制
尽管mmap属于POSIX定义的异步信号安全函数,但在信号处理的上下文里执行内存映射,可能受进程当前状态限制,增加分配失败概率。
修复后的代码
#include <signal.h> #include <stdio.h> #include <sys/mman.h> #include <unistd.h> #include <stdlib.h> void *addr; static void catch_function(int signo, siginfo_t *si, void *unused) { printf("Got SIGSEGV at address: 0x%lx\n", (unsigned long)si->si_addr); // 改用页对齐的映射长度,选择高地址区域尝试映射 void *ptr = mmap(addr, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON | MAP_FIXED, -1, 0); if (ptr == MAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); } else { printf("mmap succeeded at address: 0x%lx\n", (unsigned long)ptr); } } int main(void) { int page_size = getpagesize(); struct sigaction sa; // 用sigaction注册支持扩展信息的信号处理函数 sa.sa_sigaction = catch_function; sa.sa_flags = SA_SIGINFO; sigemptyset(&sa.sa_mask); if (sigaction(SIGSEGV, &sa, NULL) == -1) { fputs("Failed to set signal handler.\n", stderr); return EXIT_FAILURE; } // 选择64位macOS中更可能可用的高地址区域 addr = (void *)(0x100000000UL); // 触发段错误 *((int *)addr) = 1; printf("Successfully wrote to address: 0x%lx\n", (unsigned long)addr); return EXIT_SUCCESS; }
关键修复点
- 替换
signal()为sigaction():保证能正确获取段错误的地址信息,同时遵循现代信号处理的标准规范。 - 调整目标地址:选择
0x100000000这类64位系统的高地址区域,避开系统保留的低地址段,降低冲突概率。 - 规范映射长度:
mmap的长度参数必须是页大小的整数倍,显式使用getpagesize()避免系统自动取整带来的潜在问题。 - 可选:移除
MAP_FIXED:如果不需要强制映射到指定地址,去掉该标志可让系统自动分配可用地址,彻底避免地址冲突问题。若必须用MAP_FIXED,可通过vmmap <pid>命令查看进程当前地址布局,确认目标区域未被占用。
额外说明
即使mmap成功,触发段错误的指令会在信号处理完成后重新执行,此时地址已被映射,就能正常写入。但这种方式仅适用于简单的内存缺失场景,生产环境中不建议依赖信号处理动态修复内存访问问题,逻辑复杂时容易引发死循环或其他不可控问题。
内容的提问来源于stack exchange,提问作者agentcooper
相关产品推荐
相关产品推荐

