Почему адрес стека растет в сторону уменьшения адресов памяти?

Я прочитал в текстовых книгах, что стек растет, уменьшая адрес памяти; то есть от более высокого адреса до нижнего адреса. Это может быть плохой вопрос, но я не понял эту концепцию. Можете ли вы объяснить?

Ответ 1

Во-первых, это зависит от платформы. В некоторых архитектурах стек выделяется из нижней части адресного пространства и растет вверх.

Предполагая, что архитектура, подобная x86, которая стекала вверх из верхней части адресного пространства, идея довольно проста:

===============     Highest Address (e.g. 0xFFFF)
|             |
|    STACK    |
|             |
|-------------|  <- Stack Pointer   (e.g. 0xEEEE)
|             |
.     ...     .
|             |
|-------------|  <- Heap Pointer    (e.g. 0x2222)
|             |
|    HEAP     |
|             |
===============     Lowest Address  (e.g. 0x0000)

Чтобы увеличить стек, вы уменьшите указатель стека:

===============     Highest Address (e.g. 0xFFFF)
|             |
|    STACK    |
|             |
|.............|  <- Old Stack Pointer (e.g. 0xEEEE)
|             |
| Newly       |
| allocated   |
|-------------|  <- New Stack Pointer (e.g. 0xAAAA)
.     ...     .
|             |
|-------------|  <- Heap Pointer      (e.g. 0x2222)
|             |
|    HEAP     |
|             |
===============     Lowest Address    (e.g. 0x0000)

Как вы можете видеть, чтобы увеличить стек, мы уменьшили указатель стека от 0xEEEE до 0xAAAA, тогда как для увеличения кучи вам нужно увеличить указатель кучи.

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

Вы можете спросить, почему стек должен расти вниз. Ну, как я уже говорил, некоторые архитектуры делают наоборот, заставляя кучу расти вниз, и стек растет вверх. Имеет смысл ставить стек и кучу на противоположных сторонах, поскольку он предотвращает перекрытие и позволяет обеим областям расти свободно, если у вас достаточно доступного адресного пространства.

Еще один правильный вопрос: Может ли программа не уменьшать/увеличивать указатель стека? Как архитектура может накладывать одну на другую программисту? Почему это не так зависит от программы, поскольку она зависит от архитектуры? Хотя вы можете в значительной степени бороться с архитектурой и каким-то образом уйти со своего стека в противоположном направлении, некоторые инструкции, особенно call и ret, которые изменяют указатель стека непосредственно, будут предполагать другое направление, создавая беспорядок.

Ответ 2

В наше время это в значительной степени потому, что это было сделано так долго, и многие программы предполагают, что это делается именно так, и нет реальной причины для его изменения.

Назад, когда динозавры бродили по земле, а компьютеры имели 8 КБ памяти, если вам повезло, это была важная оптимизация пространства. Вы кладете нижнюю часть стека в самую верхнюю часть памяти, опустившись вниз, и вы помещаете программу и ее данные в самое нижнее положение, а область malloc растет. Таким образом, единственным ограничением размера стека был размер программы + куча и наоборот. Если стек вместо этого начинался с 4 КБ (например) и вырос, куча никогда не могла быть больше 4 КБ (минус размер программы), даже если программе понадобилось всего несколько сотен байт стека.

Ответ 3

int main() {
    int a = 0x12345678;
    int b = 0x34234232;
    printf("%p\n", &a);
    printf("%p\n", &b);
    return 0;
}

Эта программа производит этот вывод на x86. Разве адрес не должен уменьшаться?

[email protected]:~/eclipse_workspace/Sample/Sample$ ./a.out 
0xbf8a5f98
0xbf8a5f9c
[email protected]:~/eclipse_workspace/Sample/Sample$ 

Ответ 4

Man CLONE: аргумент child_stack указывает местоположение стека, используемого дочерним процессом. Поскольку дочерний и вызывающий процессы могут совместно использовать память, дочерний процесс не может выполняться в том же стеке, что и вызывающий процесс. Поэтому вызывающий процесс должен установить пространство памяти для дочернего стека и передать указатель на это пространство в clone(). Стеки растут вниз на всех процессорах, работающих под Linux (кроме процессоров HP PA), поэтому child_stack обычно указывает на самый верхний адрес пространства памяти, настроенного для дочернего стека.

Ответ 5

На x86 основной причиной увеличения стека в сторону уменьшения адресов памяти является то, что инструкция PUSH уменьшает указатель стека:

Уменьшает указатель стека, а затем сохраняет исходный операнд на вершине стека.

Смотрите стр. 4-511 в Руководстве для разработчиков программного обеспечения Intel® 64 и IA-32.