堆栈是如何初始化的?

时间:2011-03-10 21:41:38

标签: c++ c linux

当进程请求内存并且操作系统向进程提供一些新页面时,内核应该初始化页面(例如,使用零),以避免显示另一个进程使用的可靠数据。当进程启动并接收一些内存时也是如此,例如堆栈段。

当我在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;
}
  1. 为什么它也没有设置为零?
  2. 可能是因为它被这个过程重用了吗?
  3. 如果是,可能是之前使用过那些3-4 kB内存的初始化代码吗?

5 个答案:

答案 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库的极简版本。