No contexto de um processo Unix ou Linux, a frase "a pilha" pode significar duas coisas.
Primeiro, "a pilha" pode significar os registros de entrada e saída da sequência de chamada do fluxo de controle. Quando um processo é executado, main()
é chamado primeiro. main()
pode chamar printf()
. O código gerado pelo compilador grava o endereço da string de formato e quaisquer outros argumentos para printf()
em alguns locais da memória. Em seguida, o código grava o endereço para o qual o fluxo de controle deve retornar após a conclusão de printf()
. Em seguida, o código chama um salto ou uma ramificação para o início de printf()
. Cada thread possui uma dessas pilhas de registro de ativação de função. Observe que muitas CPUs têm instruções de hardware para configurar e manter a pilha, mas que outras CPUs (IBM 360, ou qualquer que seja o nome dela) realmente usaram listas vinculadas que poderiam estar espalhadas pelo espaço de endereço.
Segundo, "a pilha" pode significar os locais de memória para os quais a CPU grava argumentos para funções, e o endereço para o qual uma função chamada deve retornar. "A pilha" refere-se a uma parte contígua do espaço de endereço do processo.
A memória em um processo Unix ou Linux ou * BSD é uma linha muito longa, começando em cerca de 0x400000 e terminando em aproximadamente 0x7fffffffffff (em CPUs x86_64). O espaço de endereço da pilha começa no maior endereço numérico. Toda vez que uma função é chamada, a pilha de registros de ativação de função "cresce": o código de processo coloca argumentos de função e um endereço de retorno na pilha de registros de ativação e decrementa o ponteiro de pilha, um registro de CPU especial usado para rastrear onde no espaço de endereço da pilha, os valores das variáveis atuais do processo residem.
Cada thread recebe um pedaço da "pilha" (espaço de endereço de pilha) para uso próprio como uma pilha de registros de ativação de função. Em algum lugar entre 0x7fffffffffff e um endereço inferior, cada thread tem uma área de memória reservada para uso em chamadas de função. Geralmente, isso é imposto apenas por convenção, não por hardware, portanto, se o thread chamar função após função aninhada, a parte inferior da pilha desse segmento poderá sobrescrever o topo da pilha de outro segmento.
Assim, cada segmento tem um pedaço da área de memória "pilha" e é daí que vem a terminologia "pilha compartilhada". É uma conseqüência de um espaço de endereço de processo ser um único bloco linear de memória e os dois usos do termo "pilha". Tenho certeza que algumas JVMs mais antigas (na verdade antigas) só tinham um único thread. Qualquer encadeamento dentro do código Java foi realmente feito por um único encadeamento real. JVMs mais novas, JVMs que invocam encadeamentos reais para executar encadeamentos Java, terão a mesma "pilha compartilhada" descrita acima. O Linux e o Plan 9 possuem uma chamada de sistema de início de processo (clone () para Linux, rfork () no Plan 9) que pode configurar processos que compartilham partes do espaço de endereço e talvez diferentes espaços de endereço de pilha, mas esse estilo de encadeamento nunca realmente pegou.