首页 > 解决方案 > 假设使用 mmap 时未初始化的堆内存被初始化为零?

问题描述

我们知道,虽然.bss内存位置(例如未初始化的全局 C 变量)总是由加载器初始化为零,但堆内存并非如此。一个常见的错误是假设堆内存被初始化为零。

但是获得的分配内存mmap呢?我们可以通过调用mmap将堆中的一个区域映射到一个匿名文件(需求零页)来请求堆内存,所以在这种情况下我们可以安全地假设堆内存被初始化为零?

而且,malloc实际上mmap在内部调用以请求大尺寸分配的内存,而对于小尺寸分配,malloc仍然在内部调用sbrk/ brk。对于后者,我们当然不能假设堆内存被初始化为零。

所以对于内部malloc实际调用的大容量分配mmap,我们仍然可以假设堆内存初始化为零?我知道这仍然不是一个好的做法,我们不应该在所有情况下都阅读假设 0,但我只想确保我的理论是正确的,以便我知道我的理解mmap是正确的。

标签: cheap-memoryvirtual-memory

解决方案


我们知道,虽然 .bss 内存位置(例如未初始化的全局 C 变量)总是被加载器初始化为零......

我不知道 - 可能是加载程序,可能是链接到程序的语言启动代码(例如“crt0.o”),可能是操作系统/内核提供“新页面归零”保证加载程序没有'不必打扰。

但是mmap获得的分配内存呢?

mmap()的怎么用?

对于 POSIX;该规范不包括mmap() 直接用于分配内存。相反,您应该为打开的“内存对象”(例如从“ posix_typed_mem_open()”)获取文件描述符,然后使用其文件描述符映射内存对象;并且用于创建“内存对象”的函数的规范大多只是说(非常解释)“废话,实现定义,废话,抱歉我们的规范直到为时已晚才存在”。因此,对于该区域是否归零没有什么可说的。

对于 Linux,mmap(..., MAP_ANONYMOUS)为您提供归零页面,mmap(..., MAP_ANONYMOUS | MAP_UNINITIALIZED)但可能不会。

对于 Windows,您不使用它mmap()来分配内存——而是使用VirtualAlloc()它来保证新内存为零。

每个其他操作系统都可以做任何其他感觉。

一般来说; 多任务操作系统必须担心安全问题(例如,仍在由一个进程释放的内存中的数据在由另一个进程分配后可以访问)并且出于安全原因(和方便)将填充页面(用零)。

因此,当 malloc 实际在内部调用 mmap 时,对于大尺寸的分配,我们仍然可以假设堆内存被初始化为零?

你可以假设任何你喜欢的东西,直到你发现这个假设是错误的。malloc()这包括内部调用的假设mmap()(在某些条件下或在任何条件下)。例如,C 库可以只在程序中保留大量空间.bss并将其用于malloc(),因此malloc()根本不会使用mmap()or sbrk()(或VirtualAlloc()or ...)。

如果您确实知道malloc()从某个地方获得了底层内存,该内存可以保证内存填充为零(这几乎是不可能的,因为您可以获得旧内存和新内存的混合);那么您仍然不能假设malloc()(或该语言的运行时环境的任何其他部分 - 启动代码、C 库等)没有暂时将内存用于某些元数据,并且仍然不能假设内存分配malloc()自在您获得它时仍然为零。

请注意,堆可以优化calloc(),以便如果它知道内存已经用零填充(因为它保持跟踪,并且还知道底层内存最初来自提供保证的某个地方)它不会用零填充内存没理由。本质上,calloc()是“malloc()有保证”(而不是“malloc()后跟memset()”)。


推荐阅读