Pergunta

Eu estou preparando alguns materiais de treinamento em C e eu quero que meus exemplos para se ajustar ao modelo de pilha típico.

O sentido é que uma pilha C crescer em Linux, Windows, Mac OSX (PPC e x86), Solaris e mais recente Unixes?

Foi útil?

Solução

crescimento Stack não costuma depender do próprio sistema operacional, mas no processador que está sendo executado. Solaris, por exemplo, é executado em x86 e SPARC. Mac OSX (como você mencionou) é executado em PPC e x86. Linux funciona em tudo, desde a minha grande Honkin' System z no trabalho a um insignificante pouco relógio de pulso .

Se a CPU fornece qualquer tipo de escolha, o ABI / convenção de chamada usada pelos especifica OS qual a escolha que você precisa fazer se você quiser que o seu código ao código de chamada de todos os outros.

Os processadores e sua direção são:

  • x86:. Baixo
  • SPARC: selecionável. A ABI padrão usa para baixo.
  • PPC: para baixo, eu acho
  • .
  • System z:. Em uma lista ligada, eu não te miúdo (mas ainda baixo, pelo menos por zLinux)
  • ARM:. Selecionável, mas Thumb2 tem codificações compactos somente para baixo (LDMIA = incremento depois, STMDb = decréscimo antes)
  • 6502:. Baixo (mas apenas 256 bytes)
  • RCA 1802A:. Da maneira que quiser, sujeitas a implementação SCRT
  • PDP11:. Baixo
  • 8051:. Up

Mostrando minha idade sobre os últimos, 1802 foi o chip usado para controlar o início dos serviços de transporte (sensing se as portas estavam abertas, eu suspeito, com base no poder de processamento que tinha :-) e meu segundo computador, o < a href = "http://en.wikipedia.org/wiki/Comx-35" rel = "noreferrer"> COMX-35 (seguindo o meu ZX80 ).

PDP11 detalhes adquirida a partir aqui , 8051 detalhes de aqui .

O SPARC arquitetura utiliza um modelo de janela de registo de correr. Os detalhes arquitetonicamente visíveis também incluem um buffer circular de registo no Windows que são válidos e em cache internamente, com armadilhas quando essa sobre / underflows. Consulte aqui para mais detalhes. Como o manual sparcv8 explica , salvar e restaurar instruções são como instruções ADD mais registar-window rotação. Usando uma constante positiva em vez do negativo habitual daria uma pilha ascendente de crescimento.

A técnica SCRT acima mencionado é outra - 1802 usado alguns ou é dezesseis registradores de 16 bits para SCRT (chamadas padrão e técnica de retorno). Um deles era o contador de programa, você pode usar qualquer registo como o PC com a instrução SEP Rn. Um deles foi o ponteiro de pilha e dois foram definidos sempre a apontar para o endereço código SCRT, um para chamadas, uma para o retorno. Não registo foi tratado de uma forma especial. Tenha em mente esses detalhes são de memória, eles podem não ser totalmente correto.

Por exemplo, se R3 foi o PC, R4 foi o endereço de chamada SCRT, R5 era o endereço SCRT retorno e R2 foi a "pilha" (aspas como ele é implementado em software), SEP R4 iria definir R4 ser o PC e começar a executar o código de chamada SCRT.

Seria, então, armazenar R3 na "pilha" R2 (acho que R6 foi usado para armazenamento temporário), ajustando-o para cima ou para baixo, pegar os dois bytes seguintes R3, carregá-los em R3, em seguida, fazer SEP R3 e estar em execução no novo endereço.

Para voltar, seria SEP R5 que iria puxar o endereço antigo da pilha R2, adicione dois para isso (para ignorar o endereço de bytes da chamada), carregá-lo em R3 e SEP R3 para começar a executar o código anterior.

Muito difícil de quebrar a cabeça em torno inicialmente depois de todo o código baseado em pilha 6502/6809 / z80, mas ainda elegante em um bang-seu-cabeça-contra-o-parede tipo de forma. Também uma das grandes características de venda do chip foium conjunto completo de 16 registos de 16 bits, apesar do fato de que você imediatamente perdeu 7 dos (5 para SCRT, dois para DMA e interrupções de memória). Ahh, o triunfo do marketing sobre a realidade: -)

System z é realmente muito semelhante, usando seus registros R14 e R15 para a chamada / retorno.

Outras dicas

C ++ (adaptável a 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);
    }
}

A vantagem de crescimento para baixo é em sistemas mais antigos da pilha era tipicamente no topo da memória. Programas normalmente memória a partir do fundo cheios, assim, este tipo de gerenciamento de memória minimizado a necessidade de medir e colocar o fundo da pilha em algum lugar sensível.

pilha cresce para baixo em x86 (definido pela arquitetura, pop incrementos pilha ponteiro, decréscimos de pressão.)

Em MIPS não há nenhuma instrução push / pop. Todos os empurra / POP são explicitamente feito por carga / armazenamento relativamente ao ponteiro de pilha e, em seguida, ajustar manualmente o ponteiro $sp. No entanto, como todos os registos (exceto $0) são de uso geral, na teoria qualquer registo pode ser um ponteiro de pilha, ea pilha pode crescer em qualquer direção que o programador quer. MIPS ABIs normalmente crescem para baixo.

Em Intel 8051 a pilha cresce, provavelmente porque o espaço de memória é tão pequeno (128 bytes na versão original) que não há nenhuma pilha e você não precisa colocar a pilha em cima para que ele vai ser separada da a pilha crescente de baixo.

Ela cresce para baixo, porque a memória alocada para o programa tem a "permanente de dados" código ou seja, para o próprio programa na parte inferior, em seguida, a pilha no meio. Você precisa de um outro ponto fixo a partir do qual fazer referência a pilha, de modo que deixa você no topo. Isto significa que a pilha cresce para baixo, até que ele é potencialmente adjacente a objetos no heap.

Na maioria dos sistemas, pilha cresce para baixo, e meu artigo em https://gist.github.com/ CPQ / 8598782 explica por que cresce para baixo. A razão é que é o layout ideal de duas regiões de memória crescente (heap e pilha).

Apenas uma pequena além das outras respostas, que, tanto quanto eu posso ver se não tocaram neste ponto:

Tendo a pilha crescer para baixo faz com que todos os endereços dentro da pilha têm um deslocamento positivo em relação ao ponteiro de pilha. Não há nenhuma necessidade de deslocamentos negativos, como eles só apontaria para espaço de pilha não utilizados. Isto simplifica acessando localizações de pilha quando os suportes processador stackpointer endereçamento relativo.

Muitos processadores têm instruções que permitem acessos com um positivo somente compensada em relação a algum registo. Aqueles incluem muitas arquiteturas modernas, bem como algumas das antigas. Por exemplo, o ARM Thumb ABI prevê acessos stackpointer-relação com um deslocamento positivo codificado dentro de uma única palavra de instrução de 16 bits.

Se a pilha cresceu para cima, todos os deslocamentos úteis relativas ao stackpointer seria negativo, que é menos intuitiva e menos conveniente. Ele também está em desacordo com outras aplicações de registo endereçamento relativo, por exemplo, para acessar campos de um struct.

Esta macro deve detectá-lo em tempo de execução sem 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;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top