mmap成功但写入失败

时间:2016-01-21 19:23:15

标签: linux segmentation-fault mmap low-memory

我有一个非常简单的代码来测试低内存地址上的mmap。

  unsigned long *p = mmap ((void*)(4096*16), 4096, PROT_READ|PROT_WRITE|PROT_EXEC,
              MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS|MAP_GROWSDOWN, -1, 0);
  fprintf (stderr, "p=0x%lx\n", (unsigned long)p);`
  *p = 2554;
  printf ("p=0x%lx; *p=%ld\n", (unsigned long)p, *p);

当我运行代码时,我得到以下输出:

 p=0x10000
 Segmentation fault (core dumped)

在dmesg日志中,我可以看到以下打印件:

 segfault at 10000 ip 00000000004006cc sp 00007fff5845f4c0 error 6

总的来说,似乎mmap是成功的,但写操作失败了。我无法解释这两个冲突观察。请帮我。感谢。

1 个答案:

答案 0 :(得分:0)

如果您从MAP_GROWSDOWN调用中的flags参数中省略mmap,您可能会发现不再出现细分错误。

如果您在/proc/$PID/maps电话后检查mmap文件,您可能会发现奇怪(MAP_GROWSDOWN中包含flags)。地址似乎比请求的地址高一页,并且该映射的大小似乎比您请求的小一页。简而言之,该映射的起始地址偏离4096字节。我在MAP_GROWSDOWN的文档中没有发现这种奇怪的现象,它看起来更像是一个错误,而不是一个功能。你是否看到这种特殊的怪异可能取决于你正在使用的内核版本(我从标签中假设你正在使用Linux内核)。在任何情况下,在进程处于活动状态时检查该文件可能具有教育意义,即使您的代码在没有MAP_GROWSDOWN的情况下按预期工作。

保持进程活动足够长以检查其maps文件的一种方法是在gdb中设置断点。调用mmap的函数中的任何位置都应该足够,如果你走得足够远(刚刚超过mmap调用)。上述路径名中的$PID旨在表示调用mmap的进程的进程ID。您可以从合适的ps输出或info inferiorgdb的输出中获取该流程ID。

为了解决您的具体问题,mmap调用的成功反映了maps文件中列出的映射(即使您的示例中该映射的大小为零),而失败则反映mmap(0x10000)的返回值与映射开始(0x11000)之间的差异。如果大小为4096(在您的示例中),则没有地址允许分配到*p,但是更大的大小将mmap的返回值添加4096将为您提供一个工作地址(假设您的内核)表现与我的行为相同)。如果映射的开头等于mmap返回值(因为没有MAP_GROWSDOWN),则不存在差异。