pilha do kernel e pilha do espaço do usuário

110

Qual é a diferença entre a pilha do kernel e a pilha do usuário? Por que a pilha do kernel é usada? Se uma variável local for declarada em um ISR, onde ela será armazenada? Cada processo tem sua própria pilha de kernel? Então, como o processo é coordenado entre essas duas pilhas?

jkv
fonte

Respostas:

187
  1. Qual é a diferença entre a pilha do kernel e a pilha do usuário?

Em suma, nada - além de usar um local diferente na memória (e, portanto, um valor diferente para o registro do ponteiro de pilha) e, geralmente, diferentes proteções de acesso à memória. Ou seja, ao executar no modo de usuário, a memória do kernel (parte da qual é a pilha do kernel) não estará acessível mesmo se mapeada. Vice-versa, sem ser explicitamente solicitado pelo código do kernel (no Linux, por meio de funções como copy_from_user()), a memória do usuário (incluindo a pilha do usuário) geralmente não é diretamente acessível.

  1. Por que [uma] pilha de kernel separada é usada?

Separação de privilégios e segurança. Por um lado, os programas do espaço do usuário podem fazer sua pilha (ponteiro) o que quiserem, e geralmente não há nenhum requisito arquitetônico para ter uma pilha válida. O kernel, portanto, não pode confiar que o ponteiro de pilha do espaço do usuário seja válido nem utilizável e, portanto, exigirá um conjunto sob seu próprio controle. Diferentes arquiteturas de CPU implementam isso de maneiras diferentes; As CPUs x86 trocam automaticamente os stackpointers quando ocorrem mudanças de modo de privilégio, e os valores a serem usados ​​para diferentes níveis de privilégio são configuráveis ​​- por código privilegiado (ou seja, apenas o kernel).

  1. Se uma variável local for declarada em um ISR, onde ela será armazenada?

Na pilha do kernel. O kernel (kernel do Linux, isto é) não conecta ISRs diretamente às portas de interrupção da arquitetura x86, mas, em vez disso, delega o despacho de interrupção para um mecanismo de entrada / saída de interrupção de kernel comum que salva o estado de registro de pré-interrupção antes de chamar o (s) manipulador (es) registrado (s) . A própria CPU ao despachar uma interrupção pode executar um privilégio e / ou troca de pilha, e isso é usado / configurado pelo kernel de forma que o código de entrada de interrupção comum já possa contar com a presença de uma pilha de kernel.
Dito isso, as interrupções que ocorrem durante a execução do código do kernel simplesmente (continuam a) usar a pilha do kernel instalada naquele ponto. Isso pode, se os manipuladores de interrupção tiverem caminhos de chamada profundamente aninhados, levar a estouros de pilha (se um caminho de chamada profundo do kernel for interrompido e o manipulador causar outro caminho profundo; no Linux, o código RAID do sistema de arquivos / software sendo interrompido pelo código de rede com iptables ativo é conhecido por acionar tais kernels antigos não ajustados ... a solução é aumentar o tamanho da pilha do kernel para tais cargas de trabalho).

  1. Cada processo tem sua própria pilha de kernel?

Não apenas cada processo - cada thread tem sua própria pilha de kernel (e, de fato, sua própria pilha de usuário também). Lembre-se de que a única diferença entre processos e threads (para Linux) é o fato de que vários threads podem compartilhar um espaço de endereço (formando um processo).

  1. Como o processo é coordenado entre essas duas pilhas?

Nem um pouco - não precisa. A programação (como / quando diferentes threads estão sendo executados, como seu estado é salvo e restaurado) é a tarefa do sistema operacional e os processos não precisam se preocupar com isso. Conforme os threads são criados (e cada processo deve ter pelo menos um thread), o kernel cria pilhas de kernel para eles, enquanto as pilhas de espaço de usuário são explicitamente criadas / fornecidas por qualquer mecanismo usado para criar um thread (funções como makecontext()ou pthread_create()permitir que o chamador especifique uma região de memória a ser usada para a pilha do thread "filho") ou herdada (por clonagem de memória ao acessar, geralmente chamada de "cópia na gravação" / COW, ao criar um novo processo).
Dito isto,(estado, entre eles está o stackpointer do thread). Existem várias maneiras para isso: sinais UNIX, setcontext(), pthread_yield()/ pthread_cancel(), ... - mas isso é disgressing um pouco da pergunta original.

FrankH.
fonte
Respostas excelentes FrankH. Obrigado.
Kumar
1
@FrankH Excelente resposta .. mas eu tenho pequenas perguntas relacionadas a isso, mas na arquitetura ARM .. Como essa pilha de kernel está relacionada aos diferentes modos de processador?
Rahul
2
@Rahul: "a margem de um comentário do SO é muito pequena para conter essa resposta". Como os registros de stacks / stackpointer funcionam nos diferentes modos de CPU ARM (quais implementam um SP armazenado) é uma boa pergunta, mas requer mais espaço para responder do que um comentário pode fornecer. O mesmo se aplica a coisas como portas de tarefas x86 ou ISTs - não existe um ponteiro de pilha de kernel "único" (assim como não há um ponteiro de pilha de usuário "único"), e qual suporte / mandato de hardware existe para ponteiros de pilha separados em modos operacionais diferentes são ... muito dependentes do hardware.
FrankH.
@FrankH. Criei uma nova pergunta para o mesmo ... stackoverflow.com/q/22601165/769260 Espero que agora você possa me ajudar sem se importar com o espaço :)
Rahul
1
@FrankH. Você pode fornecer um diagrama mostrando onde a pilha do kernel pertence no layout de memória de um processo?
Jithin Pavithran
19

Minha resposta é coletada de outras perguntas do SO com minhas coisas.

What's the difference between kernel stack and user stack?

Como um programador do kernel, você sabe que o kernel deve ser restrito a programas de usuário errôneos. Suponha que você mantenha a mesma pilha para kernel e espaço do usuário, então o segfault simples no aplicativo do usuário trava o kernel e precisa ser reiniciado.

Existe uma "pilha de kernel" por CPU como ISR Stack e uma "pilha de kernel" por processo. Há uma "pilha de usuário" para cada processo, embora cada thread tenha sua própria pilha, incluindo threads de usuário e kernel.

http://linux.derkeiler.com/Mailing-Lists/Kernel/2004-10/3194.html

Why kernel stack is used?

Portanto, quando estamos no modo kernel, o tipo de mecanismo de pilha é necessário para lidar com chamadas de função, variáveis ​​locais semelhantes ao espaço do usuário.

http://www.kernel.org/doc/Documentation/x86/kernel-stacks

If a local variable is declared in an ISR, where it will be stored?

Ele será armazenado na pilha ISR (IRQSTACKSIZE). O ISR é executado em uma pilha de interrupção separada apenas se o hardware oferecer suporte. Caso contrário, os quadros de pilha ISR são colocados na pilha do thread interrompido.

O espaço do usuário não sabe e francamente não se importa se a interrupção é servida na pilha do kernel do processo atual ou em uma pilha ISR separada. Como as interrupções vêm por cpu, portanto, a pilha ISR deve ser por cpu.

 Does each process has its own kernel stack ?

Sim. Cada processo tem sua própria pilha de kernel.

 Then how the process coordinates between both these stacks?

A resposta do @FrankH parece ótima para mim.

Jeyaram
fonte
4
  1. Qual é a diferença entre pilha do kernel e pilha do usuário

Tomando como referência o Linux Kernel Development de Robert Love, a principal diferença é o tamanho:

O espaço do usuário pode se safar alocando estaticamente muitas variáveis ​​na pilha, incluindo estruturas enormes e arrays de mil elementos.
Esse comportamento é legal porque o espaço do usuário tem uma grande pilha que pode crescer dinamicamente.
A pilha do kernel não é grande nem dinâmica; é pequeno e de tamanho fixo.
O tamanho exato da pilha do kernel varia de acordo com a arquitetura.
No x86, o tamanho da pilha é configurável em tempo de compilação e pode ser de 4 KB ou 8 KB.
Historicamente, a pilha do kernel é de duas páginas, o que geralmente implica que ela tem 8 KB em arquiteturas de 32 bits e 16 KB em arquiteturas de 64 bits - esse tamanho é fixo e absoluto.
Cada processo recebe sua própria pilha.

Além disso, a pilha do kernel contém um ponteiro para a estrutura thread_info que contém informações sobre a thread.

arenard
fonte