Чому стеки зазвичай ростуть вниз?


95

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

Мені цікаво про історичне обгрунтування цього. Я знаю, що в уніфікованому адресному просторі зручно запускати стек на протилежному кінці сегмента даних (скажімо), тому існує проблема лише в тому випадку, якщо обидві сторони стикаються посередині. Але чому стек традиційно отримує верхню частину? Особливо з огляду на те, як це протилежність «концептуальній» моделі?

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

Відповіді:


52

Що стосується історичного обґрунтування, то я не можу сказати напевно (бо я їх не розробляв). Мої думки з цього приводу полягають у тому, що для початкових процесорів встановлено початковий лічильник програм 0, і це було природним бажанням запустити стек з іншого кінця і рости вниз, оскільки їх код, природно, росте вгору.

Окрім того, зауважимо, що це налаштування лічильника програми на 0 при перезавантаженні стосується не всіх ранніх процесорів. Наприклад, Motorola 6809 отримає лічильник програми з адрес, 0xfffe/fщоб ви могли почати працювати в довільному місці, залежно від того, що було надано за цією адресою (зазвичай, але жодним чином не обмежуючись ПЗУ).

Одне з перших дій, які деякі історичні системи повинні зробити, - це сканувати пам’ять зверху, поки вона не знайде місце, яке зчитує те саме записане значення, щоб вона знала фактично встановлену оперативну пам’ять (наприклад, z80 з адресовим простором 64K не обов'язково мали 64 КБ або оперативну пам'ять, насправді 64 Кб були б масовими в мої перші дні). Як тільки він знайшов верхню фактичну адресу, він встановив би вказівник стека відповідним чином, а потім міг би розпочати виклик підпрограм. Це сканування, як правило, здійснюється за допомогою запущеного коду процесора в ПЗУ в рамках запуску.

Що стосується зростання стеків, не всі вони ростуть вниз, див. Цю відповідь для деталей.


1
Мені подобається історія стратегії виявлення оперативної пам'яті Z80. Має певний сенс, що текстові сегменти викладені зростаючими - попередні програмісти мали дещо більш безпосередній контакт із наслідками цього, ніж стек. Дякуємо paxdiablo. Вказівник на набір альтернативних форм реалізації стека також надзвичайно цікавий.
— Ben Zotto

Хіба пам’ять раннього дня не має способу повідомити про її розмір, і нам доводиться обчислювати її вручну?
— phuclv

1
@ LưuVĩnhPhúc, я повинен припустити, що ти за мною покоління (або два). Я все ще пам’ятаю метод TRS-80 модель 3 для отримання дати та часу, коли запитував у користувача під час завантаження. Наявність сканера пам’яті для встановлення верхньої межі пам’яті вважалося сучасним сучасним :-) Чи можете ви уявити, що могло б статися, якби Windows запитував час або скільки пам’яті ви мали при кожному завантаженні?
— paxdiablo

1
Справді, в документації Zilog Z80 сказано, що частина запускається шляхом встановлення реєстру ПК на 0000h та виконання. Він встановлює режим переривання на 0, відключає переривання та встановлює регістри I та R також на 0. Після цього воно починає виконуватися. О 0000h він починає запускати код. ТОЙ код повинен ініціалізувати покажчик стека, перш ніж він може викликати підпрограму або активувати переривання. Який продавець продає Z80, який поводиться так, як ви описуєте?
— MikeB

1
Майк, вибач, я повинен був бути яснішим. Коли я сказав, що процесор сканує пам'ять, я не мав на увазі, що це особливість самого процесора. Насправді цим керували з програми в ПЗУ. Я уточню.
— paxdiablo

21

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


7

Стенлі Мазор (архітектор 4004 та 8080) пояснює, як було обрано напрямок зростання стека для 8080 (і врешті-решт для 8086) в "Мікропроцесори Intel: 8008 до 8086" :

Вказівник стека був обраний для запуску "вниз" (при цьому стек просувався до нижчої пам'яті), щоб спростити індексацію в стек з програми користувача (позитивне індексування) та спростити відображення вмісту стека з передньої панелі.


6

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


1
Комп’ютери не віднімають; вони додають у комплімент 2. Все, що робиться шляхом віднімання, справді робиться шляхом додавання. Зверніть увагу, комп’ютери мають суматори, а не віднімачі.
— jww

1
@jww - це різниця без різниці. Я міг би твердити, що комп’ютери не додають, що вони лише віднімають! Для цілей цієї відповіді це насправді не має значення - але більшість ALU будуть використовувати схему, яка підтримує як додавання, так і віднімання з однаковою продуктивністю. Тобто, хоча A - Bконцептуально може бути реалізовано як A + (-B)(тобто окремий етап заперечення B), на практиці це не є.
— BeeOnRope

@jww Ваш nitpick неправильний для ранніх комп’ютерів - потрібен був деякий час, щоб доповнити двоє, і поки це не сталося, існували комп’ютери, які використовували замість цього доповнення та знак і величину, а може й інші речі. З цими реалізаціями цілком могла бути перевага додавання чи віднімання. Отже, за відсутності додаткової інформації, неправильно виключати це як можливий фактор, що впливає на вибір схеми вирішення, як напрям стека.
— mtraceur

4

IIRC стек росте вниз, тому що купа росте вгору. Могло бути і навпаки.


5
Зростаюча купа дозволяє в деяких випадках ефективно перерозподілити, але зростаюча купа майже ніколи цього не робить.
— Пітер Кордес,

Чому @PeterCordes?
— Яшас,

3
@Yashas: тому що після об’єкта realloc(3)потрібно більше місця , щоб просто розширити відображення без копіювання. Повторний перерозподіл того самого об’єкта можливий, коли за ним йде довільна кількість невикористаного простору.
— Пітер Кордес,

2

Я вважаю, що це суто дизайнерське рішення. Не всі з них зростають вниз - див. Цей потік SO для гарного обговорення напряму зростання стека на різних архітектурах.


1

Я вважаю, що Конвенція розпочалася з IBM 704 та її сумнозвісного "реєстру декрементів". Сучасна мова могла б назвати це зміщеним полем інструкції, але справа в тому, що вони пішли вниз , а не вгору .


1

Ще лише на 2 с:

Окрім усіх згаданих історичних обґрунтувань, я абсолютно впевнений, що немає жодної причини, яка б відповідала сучасним процесорам. Всі процесори можуть приймати підписані зсуви, і максимізація відстані між купою та стеком є ​​досить спірною з тих пір, як ми почали працювати з декількома потоками.

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


0

Я не впевнений, але ще колись я програмував VAX / VMS. Здається, я пам’ятаю, як одна частина пам’яті (купа ??) зростала, а стек опускався. Коли вони познайомились, тоді у вас не вистачало пам’яті.


1
Це правда, але чому тоді купа росте вгору, а не навпаки?
— Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

0

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.