【问题标题】:mmap success but write failedmmap 成功但写入失败
【发布时间】:2016-01-21 19:23:15
【问题描述】:

我有一个非常简单的代码来测试低内存地址上的 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是成功的,但是写操作失败了。我无法解释这两个冲突的观察结果。请帮我。谢谢。

【问题讨论】:

    标签: linux segmentation-fault mmap low-memory


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 感谢您的回答。正如您所提到的,这似乎是一个内核错误而不是一个功能。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多