linux - mmap 成功但写入失败

标签 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似乎是成功的,但是写操作失败了。我无法解释这两个相互矛盾的观察结果。请帮我。谢谢。

最佳答案

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

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

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

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

关于linux - mmap 成功但写入失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34932551/

相关文章:

java - 子文件夹不是通过 java 通过 scp 命令复制的

我可以将 GCC 作为守护进程运行(或将其用作库)吗?

c++初学者使用字符串时出现段错误

c - 我的 C 程序出现段错误 11

未知原因的 C++ libpthread 程序段错误

c - 如何在不关心 MAC 地址的情况下使用 packet_mmap 发送 SOCK_DGRAM 数据包?

c++ - 在 Linux 上增加文件需要任何费用吗?

linux - bash:截断文件名,为重复项添加递增的数字

c++ - mmap map 大小限制

c++ - mmap分配的是页面还是页面的一部分?