You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何访问mmap映射范围外内存未触发预期的SIGSEGV?

内存映射超出长度未触发SIGSEGV的原因

我尝试创建长度大于底层文件的内存映射(mmap)。当访问映射中无对应文件内容的页面时,会触发SIGBUS信号;但访问超出内存映射长度的内存区域时,预期应触发SIGSEGV信号却未出现,请问这是为什么?

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char* argv[]) {

  char* inp_file_name = argv[1];
  int src_fd = open(inp_file_name, O_RDWR);

  printf("system page size is: %ld\n", sysconf(_SC_PAGE_SIZE));
  struct stat buf;
  fstat(src_fd, &buf) == -1);

  printf("file size is: %ld\n", buf.st_size);

  // 创建更大的内存映射
  // 文件大小为9360字节,
  // 通过以下命令生成:dd if=/dev/random of=rand_inp count=120
  char* addr = mmap(NULL, 2 * buf.st_size, PROT_READ, MAP_PRIVATE, src_fd, 0);

  // 会触发SIGBUS
  //  printf("value at point in unmapped file region: %c", addr[13000]);

  // 预期触发SIGSEGV
  // 未触发,可能是因为访问的是进程虚拟内存中未标记为不可访问的区域
  printf("value at point in unmapped file region: %c", addr[40000]);
}

原因解析

1. 虚拟内存的页粒度对齐

操作系统以页为最小单位管理虚拟内存,mmap的映射长度会被自动向上对齐到系统页大小的整数倍。假设系统页大小为4KB(4096字节),你的文件大小9360字节,2*buf.st_size为18720字节,对齐后实际映射的是20480字节(5个完整页)。你访问的addr[40000]对应第10个页,但这个地址未必处于未映射区域——如果它恰好落在进程已有的其他虚拟内存映射(如堆、共享库、栈)范围内,或者操作系统为进程预分配的虚拟地址空间中,访问就不会触发SIGSEGV。

2. SIGSEGV与SIGBUS的触发边界

  • SIGBUS:触发于访问mmap映射范围内,但对应文件内容不存在的地址(比如文件大小不足以覆盖该页的磁盘数据),内核无法从文件加载页内容,因此抛出该信号。
  • SIGSEGV:仅当访问的地址完全不在进程的任何有效虚拟内存映射中(即页表中无对应可访问的项)时才会触发。如果addr[40000]所在的页被进程其他映射占用,或属于进程虚拟地址空间中已预留但未提交的区域,访问时就不会触发信号。

3. 代码中的语法错误

原代码中fstat(src_fd, &buf) == -1);是无效语法,缺少if关键字和错误处理逻辑,可能导致buf.st_size未初始化,进而让mmap的长度计算出错,干扰测试结果。

验证方案

要确保访问的地址确实在所有有效映射之外,可以:

  • 运行程序时,通过/proc/[进程ID]/maps查看虚拟内存映射详情,确认addr+40000是否落在任何有效映射区间外。
  • 大幅增大访问偏移,比如访问addr[100*1024*1024](100MB偏移),此时该地址大概率处于未映射的虚拟内存区域,会触发SIGSEGV。

内容的提问来源于stack exchange,提问作者sudeepdino008

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 20:54:22