匿名映射零填充?

17

在Linux中,mmap(2)手册解释了匿名映射

. . .不由任何文件支持;其内容初始化为零。

FreeBSD mmap(2)手册没有对零填充做出类似的保证,尽管它承诺非匿名映射中文件结束后的字节将被填充为零。

哪些Unix版本承诺从匿名mmap返回零初始化内存?哪些实际上返回零初始化内存,但在手册页上没有这样的承诺?

我的印象是,零填充部分是出于安全原因。我想知道是否有任何mmap实现跳过了对由单个进程进行mmapped、munmapped,然后再次由该进程进行mmapped的页面的零填充,或者是否有任何实现使用伪随机位或某些非零常数填充新映射的页面。

P.S. 显然,即使brk和sbrk过去也保证零填充页面。 我在Linux上的实验似乎表明,即使在sbrk调用分配它们后,完整的页面在页面故障时被零填充,但部分页面并没有:

#include <unistd.h>
#include <stdio.h>

int main() {
  const intptr_t many = 100;
  char * start = sbrk(0);
  sbrk(many);
  for (intptr_t i = 0; i < many; ++i) {
    start[i] = 0xff;
  }
  printf("%d\n",(int)start[many/2]);
  sbrk(many/-2);
  sbrk(many/2);
  printf("%d\n",(int)start[many/2]);
  sbrk(-1 * many);
  sbrk(many/2);
  printf("%d\n",(int)start[0]);
}
2个回答

13

哪些Unix版本承诺从匿名mmaps返回零初始化的内存?

GNU/Linux

正如您在问题中所说,Linux版本的mmap承诺对匿名映射进行零填充:

MAP_ANONYMOUS

该映射不由任何文件支持;其内容被初始化为零。

NetBSD

NetBSD版本的mmap承诺对匿名映射进行零填充:

MAP_ANON

映射未与任何特定文件相关联的匿名内存。文件描述符不用于创建MAP_ANON区域,并且必须指定为-1。映射的内存将填充为零。

OpenBSD

OpenBSD的mmap手册没有承诺对匿名映射进行零填充。然而,Theo de Raadt(著名的OpenBSD开发人员)在2019年11月的OpenBSD邮件列表中宣布:

当然是零填充。还能有什么其他选择?

我认为说出这么显而易见的事情会分散注意力。

其他OpenBSD开发人员没有反驳他。

IBM AIX

AIX版本的mmap承诺对匿名映射进行零填充:

MAP_ANONYMOUS

指定创建一个新的匿名内存区域,该区域初始化为全零。

HP-UX

根据nixdoc.net的说法,HP-UX版本的mmap承诺对匿名映射进行零填充:

如果在flags中设置了MAP_ANONYMOUS,则将创建一个新的内存区域并初始化为全部为零。

Solaris

Solaris版本的mmap承诺对匿名映射进行零填充:

当在flags中设置MAP_ANON,并且fildes设置为-1时,mmap()提供了一个直接的路径来将匿名页面返回给调用方。这个操作相当于在flags参数中省略了MAP_ANON的情况下,在/dev/zero上使用打开的文件描述符调用mmap()

这个Solaris man页面提供了一种获取零填充内存页面的方法,不依赖于使用MAP_ANONYMOUS标志的mmap的行为:不要使用MAP_ANONYMOUS标志,创建一个由/dev/zero文件支持的映射。了解提供/dev/zero文件的类Unix操作系统列表将非常有用,以查看此方法是否比使用MAP_ANONYMOUS标志更具可移植性(/dev/zeroMAP_ANONYMOUS都不是POSIX)。
有趣的是,/dev/zero的维基百科文章声称MAP_ANONYMOUS是为了消除在创建匿名映射时打开/dev/zero的需要而引入的。

8
很难不详尽地列举所有man页面或其他发行文档中的承诺,但处理MAP_ANON的底层代码(通常?总是?)也用于映射可执行文件的bss空间,并且bss空间需要填充为零。所以这很有可能。
至于如果您取消映射并重新映射,是否会“还原您的旧值”(或某些非零值,但最有可能是您的旧值),如果某些系统对释放内存时“懒惰”,那肯定是可能的。我只使用过一些支持mmap的系统(BSD和Linux衍生版),而且没有一个在处理mmap的内核代码中是这样懒惰的。
sbrk是否填充“重新增长”的页面可能与历史或缺乏历史有关。当前的FreeBSD代码与我从旧的、前mmap时代回忆起来的代码匹配:有两个半秘密变量minbrk和curbrk,brk和sbrk都只会在将curbrk移动到至少minbrk的值时调用SYS_break(真正的系统调用)。 (实际上,这看起来略有问题:brk具有至少的行为,但sbrk仅将其参数添加到curbrk并调用SYS_break。似乎无害,因为内核在/sys/vm/vm_unix.c的sys_obreak()中进行检查,因此太负面的sbrk()将失败并显示EINVAL。)
我必须查看Linux C库(然后可能还要查看内核代码),但它可能只是忽略“降低断点”的尝试,并仅在libc中记录一个“逻辑断点”值。如果您有mmap()而没有向后兼容性要求,则可以使用匿名映射完全在libc中实现brk()和sbrk(),并且将两者都实现为“仅增长”将非常简单。

2
我在查阅资料之前并不知道MAP_ANON不在POSIX中。 - Olsonist
1
@Olsonist:这有点令人惊讶,但它增加了“关于MAP_ANON没有硬性承诺”的程度,甚至没有承诺MAP_ANON将存在。 :-) - torek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接