匿名mmap零填充? [英] Anonymous mmap zero-filled?

查看:105
本文介绍了匿名mmap零填充?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Linux中,mmap(2)手册页说明了匿名映射

. . .没有任何文件支持;其内容初始化为零.

FreeBSD mmap(2)手册页尽管确实保证非匿名映射中文件末尾的字节为零填充,但它并没有对零填充做出类似的保证.

哪种类型的Unix承诺从匿名mmap返回零初始化的内存?在实践中,哪些返回零初始化内存,但在其手册页中没有做出这样的承诺?

我的印象是,零填充部分是出于安全原因.我想知道是否有任何mmap实现会跳过被映射,屏蔽,然后由单个进程再次映射的页面的零填充,或者是否有任何实现会用伪随机位或某个非零常量填充新映射的页面. >

P.S. 显然,甚至brk和sbrk都可以保证零填充页面. /a>我在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]);
}

很难说是哪一个在没有简单地详尽列举所有手册页或其他发行文档的情况下会做出什么承诺,但是处理MAP_ANON的底层代码是(通常是?总是?)还用于在bss空间中映射可执行文件,并且bss空间需要填充为零.因此很有可能.

如果取消映射并重新映射,则退还您的旧值"(或一些非零值,但很可能是您的旧值),如果某些系统是懒惰的",这似乎是有可能的关于释放.我只使用了少数几个首先支持mmap的系统(BSD和Linux派生),但没有一个是那种懒惰的,至少在内核代码处理mmap中没有.

sbrk可能会或可能不会零填充重新生成"页面的原因可能与历史有关,或与历史无关.当前的FreeBSD代码与我在mmap之前的旧时回忆的内容相匹配:有两个半秘密变量minbrkcurbrk,并且brksbrk都只会调用(实际系统调用),如果它们将curbrk移至至少minbrk的值. (实际上,这看起来有些破损:brk至少具有行为,但sbrk只是将其参数添加到curbrk并调用SYS_break.由于内核在/sys/vm/vm_unix.c中的sys_obreak()中进行检查,因此似乎无害. ,因此太负的sbrk()将失败,并显示EINVAL.)

我不得不查看Linux C库(然后也许还要查看内核代码),但是它可能只是忽略了降低中断"的尝试,而只是在libc中记录了逻辑中断"值.如果您具有mmap()并且没有向后兼容性要求,则可以使用匿名映射在libc中完全实现brk()sbrk(),并且将它们都实现为仅增长"将是微不足道的

In Linux, the mmap(2) man page explains that an anonymous mapping

. . . is not backed by any file; its contents are initialized to zero.

The FreeBSD mmap(2) man page does not make a similar guarantee about zero-filling, though it does promise that bytes after the end of a file in a non-anonymous mapping are zero-filled.

Which flavors of Unix promise to return zero-initialized memory from anonymous mmaps? Which ones return zero-initialized memory in practice, but make no such promise on their man pages?

It is my impression that zero-filling is partially for security reasons. I wonder if any mmap implementations skip the zero-filling for a page that was mmapped, munmapped, then mmapped again by a single process, or if any implementations fill a newly mapped page with pseudorandom bits, or some non-zero constant.

P.S. Apparently, even brk and sbrk used to guarantee zero-filled pages. My experiments on Linux seem to indicate that, even if full pages are zero-filled upon page fault after a sbrk call allocates them, partial pages are not:

#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]);
}

解决方案

It's hard to say which ones promise what without simply exhaustively enumerating all man pages or other release documentation, but the underlying code that handles MAP_ANON is (usually? always?) also used to map in bss space for executables, and bss space needs to be zero-filled. So it's pretty darn likely.

As for "giving you back your old values" (or some non-zero values but most likely, your old ones) if you unmap and re-map, it certainly seems possible, if some system were to be "lazy" about deallocation. I have only used a few systems that support mmap in the first place (BSD and Linux derivatives) and neither one is lazy that way, at least, not in the kernel code handling mmap.

The reason sbrk might or might not zero-fill a "regrown" page is probably tied to history, or lack thereof. The current FreeBSD code matches with what I recall from the old, pre-mmap days: there are two semi-secret variables, minbrk and curbrk, and both brk and sbrk will only invoke SYS_break (the real system call) if they are moving curbrk to a value that is at least minbrk. (Actually, this looks slightly broken: brk has the at-least behavior but sbrk just adds its argument to curbrk and invokes SYS_break. Seems harmless since the kernel checks, in sys_obreak() in /sys/vm/vm_unix.c, so a too-negative sbrk() will fail with EINVAL.)

I'd have to look at the Linux C library (and then perhaps kernel code too) but it may simply ignore attempts to "lower the break", and merely record a "logical break" value in libc. If you have mmap() and no backwards compatibility requirements, you can implement brk() and sbrk() entirely in libc, using anonymous mappings, and it would be trivial to implement both of them as "grow-only", as it were.

这篇关于匿名mmap零填充?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆