Почему стеки обычно растут вниз?

Я знаю, что в архитектуре я лично знаком с (x86, 6502 и т.д.), стек обычно растет вниз (т.е. каждый элемент, нажатый на стек, приводит к уменьшению SP, а не к увеличению).

Мне интересно, какое историческое обоснование для этого. Я знаю, что в унифицированном адресном пространстве удобно запускать стек на противоположном конце сегмента данных (скажем), так что существует только проблема, если обе стороны сталкиваются посередине. Но почему стек традиционно получает верхнюю часть? Особенно учитывая, как это противоположно "концептуальной" модели?

(И обратите внимание, что в архитектуре 6502 стек также растет вниз, хотя он ограничен одной 256-байтной страницей, и выбор этого направления кажется произвольным.)

Ответ 1

Что касается исторического обоснования, я не могу сказать наверняка (потому что я их не проектировал). Мои мысли по этому поводу состоят в том, что ранние процессоры получили исходный счетчик программ, равный 0, и было естественным желанием запустить стек на другом конце и расти вниз, так как их код естественно растет вверх.

Отметим, что этот параметр счетчика программ 0 на reset не соответствует всем ранним процессорам. Например, Motorola 6809 выберет счетчик программ из адресов 0xfffe/f, чтобы вы могли запускаться в произвольном месте, в зависимости от того, что было поставлено на этом адресе (обычно, но не ограничиваясь ПЗУ).

Одной из первых вещей, которые могли бы сделать некоторые исторические системы, было бы сканирование памяти сверху, пока она не найдет местоположение, которое будет считывать одно и то же значение, записанное, чтобы он знал, что фактическая RAM установлена ​​(например, z80 с 64К адресное пространство не обязательно имело 64 КБ или ОЗУ, на самом деле 64 КБ было бы массовым в мои ранние дни). Как только он найдет верхний фактический адрес, он соответствующим образом установит указатель стека и сможет начать вызов подпрограмм. Это сканирование обычно выполняется с помощью кода запуска процессора в ПЗУ в качестве части запуска.

Что касается роста стеков, не все из них растут вниз, см. этот ответ для деталей.

Ответ 2

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

Ответ 3

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

Ответ 4

IIRC стек растет вниз, потому что куча растет вверх. Возможно, это было наоборот.

Ответ 5

Стэнли Мазор (архитектор 4004 и 8080) объясняет, как направление роста стека было выбрано для 8080 (и, в конечном итоге, для 8086), в разделе "Микропроцессоры Intel: от 8008 до 8086":

Указатель стека был выбран для запуска "вниз" (с продвижением стека к меньшему объему памяти), чтобы упростить индексирование в стек из пользовательской программы (положительное индексирование) и упростить отображение содержимого стека с передней панели.

Ответ 6

Я считаю это чисто конструктивным решением. Не все из них растут вниз - см. этот поток SO для хорошего обсуждения направления роста стека на разных архитектурах.

Ответ 7

Я считаю, что соглашение началось с IBM 704 и его позорного "декрементного регистра". Современная речь назвала бы это поле смещения команды, но дело в том, что они опустились, не вверх.

Ответ 8

Просто 2с больше:

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

Я лично считаю это недостатком дизайна безопасности. Если бы, скажем, разработчики архитектуры x64 изменили бы направление роста стека, большинство переполнений буфера стека было бы устранено - что является большой проблемой. (так как струны растут вверх).

Ответ 9

Я не уверен, но я делал некоторые программы для VAX/VMS в те дни. Кажется, я помню, что одна часть памяти (куча?) Поднимается, и стек падает. Когда они встретились, вы потеряли память.

Ответ 10

Одним из преимуществ роста по убыванию стека в минимальной встроенной системе является то, что один кусок оперативной памяти может быть избыточно отображен как на страницу O, так и на страницу 1, что позволяет назначать нулевые переменные страницы, начиная с 0x000, и увеличивать стек с 0x1FF, максимизируя сумма, которую он должен был бы вырастить до перезаписи переменных.

Одна из первоначальных целей проектирования 6502 состояла в том, чтобы его можно было объединить, например, с 6530, что привело к созданию двухчиповой микроконтроллерной системы с 1 КБ программного ПЗУ, таймером, вводом-выводом и 64 байтами ОЗУ общего доступа. между стеком и переменными нуля страницы. Для сравнения, минимальная встроенная система того времени, основанная на 8080 или 6800, будет иметь четыре или пять чипов.