当进程请求内存并且操作系统向进程提供一些新页面时,内核应该初始化页面(例如,使用零),以避免显示另一个进程使用的可靠数据。当进程启动并接收一些内存时也是如此,例如堆栈段。
当我在Linux中执行以下代码时,结果是大部分已分配的内存确实为0,但堆栈底部约为3-4 kB(数组的最后一个元素,最高地址)包含随机数。
#include <cstdlib>
#include <iostream>
using namespace std;
int main()
{
int * a = (int*)alloca(sizeof(int)*2000000);
for(int i = 0; i< 2000000; ++i)
cout << a[i] << endl;
return 0;
}
答案 0 :(得分:11)
操作系统不保证内存零,只是你拥有它。它可能会为您提供之前使用过的内存页面(或以前从未使用过但非零值)。如果应用程序存储了潜在敏感数据,则应该在free()之前将其归零。
它不会设置为零,因为这会执行不必要的工作。如果您分配20兆字节来存储纹理或几帧视频,为什么操作系统会将零写入所有内存,这样您就可以覆盖它们了。
作为一般规则,操作系统不会执行任何他们不需要的操作。
编辑:扩展一点,当你“分配”一块内存时,所有操作系统正在做的是从一个池中重新分配内存页面(通常是4096字节的块)到你的进程分配的页面。您还可以拥有共享内存,在这种情况下,操作系统会将它们分配给多个进程。这就是所有分配金额。
答案 1 :(得分:5)
当您通过brk()
,sbrk()
或mmap()
为您的流程注入新内存时,可以保证将其归零。
但是进程堆栈已经分配给您的进程。 alloca()
函数没有获得新的堆栈空间,它只返回当前堆栈指针并将指针移动到新块的末尾。
因此,alloca()
返回的内存块以前已由您的进程使用。即使你在main alloca()
之前没有函数,C库和动态加载器也一直在使用堆栈。
答案 2 :(得分:3)
alloca
documentation中没有任何内容表明内存已初始化,所以你只是得到了那里的垃圾。
如果您希望将内存初始化为零,则可以执行以下操作:使用memset
分配并手动初始化内存。或者你使用can calloc
来保证内存初始化为零。
答案 3 :(得分:3)
堆栈顶部包含环境变量定义,下面是命令行参数以及environ和argv数组。
在x86_64上,Linux下的简单启动代码可能如下所示:
asm(
" .text\n"
" .align 16\n"
" .globl _start\n"
" .type _start,@function\n"
"_start:\n"
" xor %rbp, %rbp\n" // Clear the link register.
" mov (%rsp), %rdi\n" // Get argc...
" lea 8(%rsp), %rsi\n" // ... and argv ...
" mov %rax, %rbx\n" // ... copy argc ...
" inc %rbx\n" // ... argc + 1 ...
" lea (%rsi, %rbx, 8), %rdx\n"// ... and compute environ.
" andq $~15, %rsp\n" // Align the stack on a 16 byte boundry.
" call _estart\n" // Let's go!
" jmp .\n" // Never gets here.
" .size _start, .-_start\n"
);
编辑:
我完全误解了这个问题。代码中堆栈顶部的东西可能是在输入main()之前调用的启动代码的结果。
答案 4 :(得分:3)
我很确定当操作系统启动你的进程时,堆栈只是零。你认为,你观察到的是另一种现象。您似乎已将程序编译为C ++。在main
开始之前,C ++会做很多代码(构造函数和类似的东西)。所以你看到的是你自己执行的遗留值。
如果您将代码编译为C(更改为“stdio.h”等),您可能会看到一个大大减少的“污染”,如果不是甚至根本没有。特别是如果您将程序静态链接到C库的极简版本。