C 假设在使用mmap时未初始化的堆内存被初始化为零?
我们知道,虽然加载程序总是将C 假设在使用mmap时未初始化的堆内存被初始化为零?,c,heap-memory,virtual-memory,C,Heap Memory,Virtual Memory,我们知道,虽然加载程序总是将.bss内存位置(例如未初始化的全局C变量)初始化为零,但对于堆内存来说,情况并非如此。一个常见的错误是假设堆内存被初始化为零 但是通过mmap获得的分配内存呢?我们可以通过调用mmap来请求堆内存,用匿名文件(要求零页)映射堆中的区域,因此在这种情况下,我们可以安全地假设堆内存已初始化为零 而且,malloc实际上会在内部调用mmap来请求内存以进行大容量的分配,而对于小容量的分配,malloc仍然会在内部调用sbrk/brk。对于后者,我们当然不能假设堆内存被初始
.bss
内存位置(例如未初始化的全局C变量)初始化为零,但对于堆内存来说,情况并非如此。一个常见的错误是假设堆内存被初始化为零
但是通过mmap
获得的分配内存呢?我们可以通过调用mmap
来请求堆内存,用匿名文件(要求零页)映射堆中的区域,因此在这种情况下,我们可以安全地假设堆内存已初始化为零
而且,malloc
实际上会在内部调用mmap
来请求内存以进行大容量的分配,而对于小容量的分配,malloc
仍然会在内部调用sbrk
/brk
。对于后者,我们当然不能假设堆内存被初始化为零
因此,当
malloc
实际上在内部调用mmap
时,对于大容量的分配,我们仍然可以假设堆内存被初始化为零?我知道这仍然不是一个好的实践,我们不应该在所有情况下阅读假设0,但我只想确保我的理论是正确的,这样我就知道我对mmap
的理解是正确的。你的假设是错误的。在Linux上,mmap
有一个选项MAP\u UNINITIALIZED
,该选项可用于更快的分配
我们知道,虽然.bss内存位置(如未初始化的全局C变量)总是由加载程序初始化为零
我不知道-可能是加载程序,可能是链接到程序中的语言启动代码(例如“crt0.o”),可能是操作系统/内核提供了“新页面归零”的保证,而加载程序不必麻烦
但是mmap获得的分配内存又如何呢
谁的mmap()
如何使用
对于POSIX;该规范不包括直接用于分配内存的mmap()
。相反,您应该为打开的“内存对象”(例如,从“posix\u typed\u mem\u open()
”)获取一个文件描述符,然后使用其文件描述符映射内存对象;用于创建“内存对象”的函数的规范大多只是说(很有意思的解释)“诸如此类,实现定义的,诸如此类,很抱歉,我们的规范直到太晚才出现。”。正因为如此,对于面积是否归零没有什么可说的
对于Linux,mmap(…,MAP\u ANONYMOUS)
为您提供零页面,mmap(…,MAP\u ANONYMOUS | MAP\u UNINITIALIZED)
可能不会
对于Windows,您不使用mmap()
来分配内存-而是使用VirtualAlloc()
来确保新内存归零
其他任何操作系统都可以做它感觉到的任何事情
一般而言;多任务操作系统必须担心安全问题(例如,一个进程释放的内存中的数据在被另一个进程分配后仍然可以访问),并且出于安全原因(和方便性),会用零填充页面
所以,当malloc实际上在内部调用mmap时,对于大容量的分配,我们仍然可以假设堆内存被初始化为零
你可以随心所欲地假设,直到你发现这个假设是错误的。这包括假设malloc()
在内部调用mmap()
(在某些条件下或任何条件下)。例如,C库可以在程序的.bss
中保留大量空间,并将其用于malloc()
,这样malloc()
就永远不会使用mmap()
或sbrk()
(或VirtualAlloc()
或…)
如果您确实知道malloc()
从某个地方获得了底层内存,这确实保证了内存中充满了零(这几乎是不可能的,因为您可能会得到旧内存和新内存的混合);然后,您仍然不能假设malloc()
(或该语言运行时环境的任何其他部分-启动代码、C库等)没有临时使用内存来存储某些元数据,并且仍然不能假设从malloc()
分配的内存在获得它时仍然为零
请注意,堆可能会优化
calloc()
,这样,如果它知道内存已经被零填充(因为它保持跟踪,并且还知道底层内存最初来自提供保证的某个地方),它就不会无缘无故地用零填充内存。本质上,calloc()
是“malloc()
”(而不是“malloc()
后跟memset()
”)。如果你想保证内存设置为零,我建议你使用calloc
。我没有资格回答这个问题,但不要假设任何没有记录的东西。谁说“malloc仍然在内部调用sbrk/brk”?对于某些特定的实现可能是这样。如果未初始化的变量进入.bss
,则它们是零初始化的。如果。这是一个有效的编译器选择,因为您可以覆盖这些零,而C只保证未初始化的变量是可写的。您不能假定未初始化的变量是可读的,更不用说该值为零了。@MSalters 6.9.2/2:对于具有文件作用域但没有初始值设定项、没有存储类说明符或具有存储类说明符static
的对象,其标识符的声明构成了一个暂定定义。如果翻译单元包含一个或多个标识符的暂定定义,而翻译单元不包含该标识符的外部定义,则行为与翻译单元包含该标识符的文件范围声明完全相同,复合类型在t结尾