1

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

4

1 回答 1

1

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

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

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

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

于 2016-01-23T03:53:05.407 回答