Как инициализируется стек? - PullRequest
15 голосов
/ 11 марта 2011

Когда процесс запрашивает память, а операционная система отдает ему несколько новых страниц, ядро ​​должно инициализировать страницы (например, с нулями), чтобы избежать показа потенциально достоверных данных, которые использовал другой процесс. То же самое, когда процесс запускается и получает некоторую память, например сегмент стека.

Когда я выполняю следующий код в Linux, результат состоит в том, что большая часть выделенной памяти действительно равна 0, но что-то около 3-4 кБ в нижней части стека (последние элементы массива, самые высокие адреса) содержит случайные числа.

#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 КБ памяти?

Ответы [ 5 ]

11 голосов
/ 11 марта 2011

Операционная система не гарантирует обнуление памяти, только то, что вы им владеете. Это, вероятно, даст вам страницы памяти, которые использовались ранее (или никогда не использовались раньше, но не ноль). Если приложение хранит потенциально конфиденциальные данные, ожидается, что оно обнулит их перед free () 'ing.

Он не установлен на ноль, потому что это будет выполнять ненужную работу. Если вы выделяете 20 мегабайт для хранения текстуры или нескольких кадров видео, зачем ОС записывать нули во всю эту память, чтобы вы могли перезаписать их, как и в следующий раз, когда вы делаете.

Как правило, операционные системы не делают ничего, что им не нужно.

edit: чтобы немного расширить, когда вы «выделяете» блок памяти, все, что делает ОС, это переназначает страницы памяти (обычно блоки по 4096 байт) вашему процессу из пула выделенные страницы. Вы также можете иметь общую память, и в этом случае ОС «назначает» их нескольким процессам. Вот и все суммы распределения.

5 голосов
/ 11 марта 2011

Когда вы добавляете новую память в ваш процесс через brk(), sbrk() или mmap(), тогда она гарантированно обнуляется.

Но стек процесса уже выделен вашему процессу. Функция alloca() не получает нового пространства стека, она просто возвращает текущий указатель стека и перемещает указатель в конец нового блока.

Таким образом, блок памяти, возвращаемый alloca(), ранее использовался вашим процессом. Даже если у вас нет функций до alloca() в основном, библиотеки C и динамический загрузчик использовали стек.

4 голосов
/ 11 марта 2011

Я почти уверен, что когда ОС запускает ваш процесс, в стеке только нули. То, что вы наблюдаете, - другое явление, я думаю. Вы, кажется, скомпилировали свою программу как C ++. C ++ делает много кода (конструкторы и тому подобное) до запуска main. Так что вы видите оставленные значения вашего собственного исполнения.

Если вы скомпилируете свой код как C (смените его на «stdio.h» и т. Д.), Вы, вероятно, увидите намного более «загрязненное», если не вообще ничего. В частности, если вы хотите статически связать вашу программу с минималистской версией библиотеки C.

3 голосов
/ 11 марта 2011

В верхней части стека находятся определения переменных среды, а под ними - аргументы командной строки и массивы environment и 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 ().

3 голосов
/ 11 марта 2011

В документации alloca нет ничего, что говорило бы о том, что память инициализирована, так что вы просто получаете все, что там находилось.

Если вы хотите, чтобы память была инициализирована нулями, вы можете сделать очевидное: выделить и вручную инициализировать ее с помощью memset. Или вы можете использовать calloc, который гарантирует, что память инициализируется в ноль.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...