Linux mmap成功,但写入失败

Linux mmap成功,但写入失败,linux,segmentation-fault,mmap,low-memory,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; p

我有一个非常简单的代码来测试低内存地址上的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\u GROWSDOWN
,您可能会发现分段错误不再发生

如果在
mmap
调用之后检查
/proc/$PID/maps
文件,您可能会看到一个异常(在
标志中包含
MAP\u GROWSDOWN
)。地址似乎比请求的地址高一页,映射的大小似乎比您请求的小一页。简而言之,该映射的起始地址被关闭4096字节。我在
MAP\u GROWSDOWN
的文档中没有发现这个奇怪的地方,对我来说,它更像是一个bug,而不是一个特性。您是否看到这种特殊的奇怪之处可能取决于您使用的内核版本(我从标记中假设您使用的是Linux内核)。在任何情况下,在进程处于活动状态时检查该文件都可能是有益的,即使您的代码在没有
MAP\u GROWSDOWN
的情况下按预期工作

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


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

谢谢您的回答。正如您所提到的,它似乎是一个内核错误,而不是一个特性。