Каково направление роста стека в большинстве современных систем?

StackOverflow https://stackoverflow.com/questions/664744

Вопрос

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

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

Это было полезно?

Решение

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

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

Процессорами и их направлением являются:

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

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

Детали PDP11, почерпнутые из здесь, 8051 подробная информация из здесь.

Архитектура SPARC использует модель регистра скользящего окна.Архитектурно видимые детали также включают циклический буфер окон регистров, которые являются допустимыми и кэшируются внутри, с ловушками при переполнении / переполнении.Видишь здесь для получения подробной информации.Как руководство SPARCv8 объясняет, Инструкции ПО СОХРАНЕНИЮ и ВОССТАНОВЛЕНИЮ похожи на инструкции по добавлению плюс поворот окна регистрации.Использование положительной константы вместо обычной отрицательной дало бы растущий вверх стек.

Вышеупомянутый метод 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 для вызова / возврата.

Другие советы

На 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);
    }
}

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

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

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

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

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

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

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

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

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

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

Этот макрос должен обнаружить его во время выполнения без 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;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top