Який напрямок зростання стека в більшості сучасних систем?


102

Я готую кілька навчальних матеріалів на мові C і хочу, щоб мої приклади відповідали типовій моделі стеків.

У якому напрямку росте стек C у Linux, Windows, Mac OSX (PPC та x86), Solaris та останніх Unix?


Відповіді:


147

Зростання стека зазвичай не залежить від самої операційної системи, а від процесора, на якому вона працює. Наприклад, Solaris працює на x86 та SPARC. Mac OSX (як ви вже згадували) працює на PPC та x86. Linux працює на всьому, від моєї великої Honkin 'System z на роботі, до маленьких маленьких наручних годинників .

Якщо процесор надає будь-який вибір, конвенція ABI / виклику, що використовується ОС, визначає, який вибір потрібно зробити, якщо ви хочете, щоб ваш код називав код інших.

Процесори та їх напрямок:

  • x86: вниз.
  • SPARC: вибір. Стандартний ABI використовує вниз.
  • КПП: вниз, я думаю.
  • Система z: у зв'язаному списку я вас не відволікаю (але все ще вниз, принаймні для zLinux).
  • ARM: вибір, але Thumb2 має компактні кодування тільки для вниз (LDMIA = приріст після, STMDB = декремент раніше).
  • 6502: вниз (але лише 256 байт).
  • RCA 1802A: будь-яким способом ви хочете, за умови виконання SCRT.
  • PDP11: вниз.
  • 8051: вгору

Я показую мій вік на останніх кількох, 1802 - це мікросхема, яка використовується для управління ранніми човниками (я вважаю, що двері були відкриті, я підозрюю, виходячи з потужності процесора :-) і мого другого комп'ютера, COMX-35 ( слідкуючи за моїм ZX80 ).

Деталі PDP11 зібрані звідси , 8051 деталі звідси .

Архітектура SPARC використовує модель реєстрації розсувних вікон. Архітектурно видимі деталі також містять круговий буфер вікон регістрів, які є дійсними та кешовані внутрішньо, з пастками, коли це переповнює / перетікає. Детальніше дивіться тут . Як пояснюється посібник SPARCv8, інструкції Зберегти та ВІДКРИТИ подібні до інструкцій ADD плюс обертання вікна реєстрації. Використання позитивної константи замість звичайної негативної дало б зростаючу стадію вгору.

Згадана вище методика SCRT - інша - 1802 використовував кілька або це шістнадцять 16-бітних регістрів для SCRT (стандартна техніка виклику та повернення). Один був лічильником програм, ви можете використовувати будь-який реєстр як ПК з SEP Rnінструкцією. Один був вказівником стека, а два завжди встановлювались для вказівки на адресу коду SCRT, один для виклику, один для повернення. Жоден реєстр не розглядався особливим чином. Майте на увазі, що ці дані з пам'яті, вони можуть бути не зовсім коректними.

Наприклад, якби R3 був ПК, R4 була адресою виклику SCRT, R5 була адресою повернення SCRT, а R2 - "стеком" (котирування, як це реалізовано в програмному забезпеченні), SEP R4встановив би R4 як ПК та почав запускати SCRT код виклику.

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

Для повернення це дозволило SEP R5б витягнути стару адресу зі стека R2, додати до неї дві (щоб пропустити байти адреси виклику), завантажити її в R3 і SEP R3почати виконувати попередній код.

Дуже важко обернути голову спочатку після всього коду, заснованого на стеці 6502/6809 / z80, але все-таки елегантно вдарив головою об стіну. Також однією з найбільших особливостей продажу чіпа був повний набір 16 16-бітових регістрів, незважаючи на те, що ви відразу втратили 7 з них (5 для SCRT, два для DMA та переривання з пам'яті). А-а-а, торжество маркетингу над реальністю :-)

Система z насправді досить схожа, використовуючи свої регістри R14 і R15 для виклику / повернення.


3
Щоб додати до списку, ARM може зростати в будь-якому напрямку, але може бути встановлена ​​в ту чи іншу за допомогою конкретної реалізації кремнію (або може бути вибрана програмним забезпеченням). Нечисленні, з якими я мав справу, завжди були в режимі зростання.
Майкл Берр

1
У невеликій частині світу ARM, яку я бачив досі (ARM7TDMI), стек повністю обробляється програмним забезпеченням. Зворотні адреси зберігаються в реєстрі, який за потреби зберігається програмним забезпеченням, а інструкції до / після збільшення / зменшення дозволяють розміщувати його та інші речі на стеку в будь-якому напрямку.
starblue

1
Один HPPA, стек виріс! Досить рідкісний серед досить сучасних архітектур.
tml

2
Для цікавих, ось хороший ресурс про те, як працює стек на z / OS: www-03.ibm.com/systems/resources/Stack+and+Heap.pdf
Dillon Cower

1
Дякуємо @paxdiablo за ваше розуміння. Іноді люди сприймають це як особисту прихильність, коли ви робите такий коментар, особливо коли це старший. Я лише знаю, що є різниця, тому що я і раніше робив ту саму помилку. Піклуватися.
CasaDeRobison

23

В C ++ (адаптується до C) stack.cc :

static int
find_stack_direction ()
{
    static char *addr = 0;
    auto char dummy;
    if (addr == 0)
    {
        addr = &dummy;
        return find_stack_direction ();
    }
    else
    {
        return ((&dummy > addr) ? 1 : -1);
    }
}

14
Нічого собі, минуло давно, як я бачив ключове слово "авто".
paxdiablo

9
(& dummy> addr) не визначено. Результат подачі двох покажчиків реляційному оператору визначається лише у тому випадку, якщо два вказівники вказують у межах одного масиву чи структури.
sigjuice

2
Спроба дослідити макет власного стека - те, що C / C ++ взагалі не вказує - для початку "нерепортаж", тому я б не переймався цим. Схоже, ця функція працюватиме правильно лише один раз.
ефеміент

9
Не потрібно використовувати staticдля цього. Натомість ви можете передати адресу як аргумент рекурсивному виклику.
R .. GitHub СТОП ДОПОМОГАЙТЕ

5
Плюс до цього static, якщо ви зателефонуєте до цього декілька разів, наступні дзвінки можуть закінчитися невдало ...
Кріс Додд

7

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


3
Не справжня перевага, а тавтологія.
Маркіз Лорнський

1
Не тавтологія. Справа в тому, щоб дві області пам’яті, що зростали, не заважали (хіба що пам'ять все-таки повна), як зазначав @valenok.
YvesgereY

6

Стек зростає на x86 (визначається архітектурою, приріст покажчика стека, декременти push).


5

У MIPS та багатьох сучасних архітектурах RISC (таких як PowerPC, RISC-V, SPARC ...) немає pushі popінструкцій. Ці операції виконуються явним чином шляхом ручного налаштування покажчика стека та завантаження / збереження значення відносно відрегульованого покажчика. Усі регістри (крім нульового регістру) мають загальне призначення, тому теоретично будь-який регістр може бути покажчиком стека, і стек може рости в будь-якому напрямку хоче програміст

Однак, стек, як правило, зростає в більшості архітектур, ймовірно, щоб уникнути випадку, коли дані стека та програми або дані купи купують один одного. Існує також велика причина вирішення згаданої відповіді sh-sh . Деякі приклади: MIPS ABI зростає вниз і використовується$29 (AKA $sp) в якості вказівника стека, RISC-V ABI також зростає вниз і використовує x2 як покажчик стека

В Intel 8051 стек зростає, ймовірно, тому, що простір пам'яті настільки крихітний (128 байт в оригінальній версії), що немає купи, і вам не потрібно ставити стек зверху, щоб він був відокремлений від наростаючої купи знизу

Ви можете знайти більше інформації про використання стека в різних архітектурах на https://en.wikipedia.org/wiki/Calling_convention

Дивитися також


2

Лише невелике доповнення до інших відповідей, яке, наскільки я бачу, не торкнулося цього моменту:

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

У багатьох процесорах є вказівки, які дозволяють отримати доступ із зміщенням лише позитивним відносно деякого регістра. Сюди входить багато сучасних архітектур, а також деякі старі. Наприклад, ARM Thumb ABI передбачає відношення до стекпоінтернентів з позитивним зміщенням, закодованим у межах одного 16-бітного слова інструкції.

Якщо стек зростає вгору, усі корисні зрушення відносно стекпоінта будуть негативними, що менш інтуїтивно та менш зручно. Це також суперечить іншим програмам відношення до регістру, наприклад, для доступу до полів структури.


2

У більшості систем стек зменшується, і моя стаття на веб- сайті https://gist.github.com/cpq/8598782 пояснює ЧОМУ він зростає. Це просто: як розташувати два зростаючих блоки пам'яті (купу та стек) у фіксованому шматі пам'яті? Найкраще рішення - покласти їх на протилежні кінці і нехай рости назустріч один одному.


що суть , здається, мертвий :(
Дост

@Ven - Я можу дістатись до цього
Бретт Холман

1

Він зростає, тому що в пам'яті, виділеній програмі, є "постійні дані", тобто код для самої програми внизу, потім купа в середині. Вам потрібна інша фіксована точка, з якої слід посилатися на стек, щоб ви залишали верх. Це означає, що стек росте вниз, поки він потенційно не прилягає до об'єктів на купі.


0

Цей макрос повинен виявити його під час виконання без UB:

#define stk_grows_up_eh() stk_grows_up__(&(char){0})
_Bool stk_grows_up__(char *ParentsLocal);

__attribute((__noinline__))
_Bool stk_grows_up__(char *ParentsLocal) { 
    return (uintptr_t)ParentsLocal < (uintptr_t)&ParentsLocal;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.