Каково направление роста стека в большинстве современных систем? - PullRequest
96 голосов
/ 20 марта 2009

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

В каком направлении растет стек C в Linux, Windows, Mac OSX (PPC и x86), Solaris и в последних версиях Unix?

Ответы [ 9 ]

138 голосов
/ 20 марта 2009

Рост стека обычно не зависит от самой операционной системы, но от процессора, на котором он работает. Например, Solaris работает на x86 и SPARC. Mac OSX (как вы упомянули) работает на PPC и x86. Linux работает на всем, от моей большой системы System z на работе до маленьких маленьких наручных часов .

Если процессор предоставляет какой-либо выбор, соглашение об использовании интерфейса ABI / вызовов, используемое ОС, определяет, какой выбор нужно сделать, если вы хотите, чтобы ваш код вызывал код всех остальных.

Процессоры и их направление:

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

Показывая мой возраст в последние несколько лет, 1802 был чипом, который использовался для управления ранними шаттлами (я подозреваю, что, если двери открыты, исходя из вычислительной мощности, которую он имел :-) и моего второго компьютера, COMX-35 (после моего ZX80 ).

Детали PDP11 получены от здесь , 8051 детали от здесь .

В архитектуре SPARC используется модель регистра скользящего окна. Архитектурно видимые детали также включают в себя круговой буфер окон регистров, которые являются действительными и кэшируются внутри, с ловушками, когда это переполняет / переполняет. Смотрите здесь для деталей. Как руководство SPARCv8 объясняет , инструкции SAVE и RESTORE аналогичны инструкциям 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 для вызова / возврата.

23 голосов
/ 20 марта 2009

В 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);
    }
}
7 голосов
/ 19 апреля 2009

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

5 голосов
/ 20 марта 2009

Стек увеличивается на x86 (определяется архитектурой, pop увеличивает указатель стека, push уменьшает)

4 голосов
/ 29 июля 2013

В MIPS нет инструкции push / pop. Все push / pops явно выполняются путем загрузки / сохранения относительно указателя стека, а затем вручную настраивают указатель $sp. Однако, поскольку все регистры (кроме $0) являются универсальными, в теории любой регистр может быть указателем стека, и стек может расти в любом направлении, желаемом программистом. MIPS ABI обычно растут вниз.

В Intel 8051 стек растет, вероятно, потому, что объем памяти настолько мал (128 байт в оригинальной версии), что нет кучи, и вам не нужно помещать стек сверху, чтобы он был отделен от куча, растущая снизу.

2 голосов
/ 25 января 2014

В большинстве систем стек уменьшается, и моя статья в https://gist.github.com/cpq/8598782 объясняет, почему он растет. Причина в том, что оптимальное расположение двух растущих областей памяти (куча и стек).

2 голосов
/ 20 марта 2009

Она уменьшается, потому что память, выделенная для программы, имеет «постоянные данные», то есть код для самой программы внизу, а затем кучу в середине. Вам нужна другая фиксированная точка, из которой можно ссылаться на стек, чтобы оставить вас на вершине. Это означает, что стек уменьшается до тех пор, пока он потенциально не будет соседствовать с объектами в куче.

0 голосов
/ 20 апреля 2019

Просто небольшое дополнение к другим ответам, которое, насколько я вижу, не коснулось этого пункта:

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

Многие процессоры имеют инструкции, которые разрешают доступ с положительным смещением только относительно некоторого регистра. К ним относятся многие современные архитектуры, а также некоторые старые. Например, ARM Thumb ABI обеспечивает относительный доступ к указателю стека с положительным смещением, закодированным в одном 16-разрядном слове инструкции.

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

0 голосов
/ 01 февраля 2019

Этот макрос должен обнаруживать его во время выполнения без 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...