O espaço do Kernel é usado quando o Kernel está sendo executado em nome do programa do usuário, ou seja, Chamada do Sistema? Ou é o espaço de endereço para todos os threads do Kernel (por exemplo, agendador)?
Sim e sim.
Antes de prosseguirmos, devemos declarar isso sobre memória.
O get de memória é dividido em duas áreas distintas:
- O espaço do usuário , que é um conjunto de locais onde os processos normais do usuário são executados (ou seja, tudo que não seja o kernel). O papel do kernel é gerenciar aplicativos em execução neste espaço, mexendo um com o outro e com a máquina.
- O espaço do kernel , que é o local em que o código do kernel está armazenado e é executado em.
Os processos em execução no espaço do usuário têm acesso apenas a uma parte limitada da memória, enquanto o kernel tem acesso a toda a memória. Os processos em execução no espaço do usuário também não têm acesso ao espaço do kernel. Os processos de espaço do usuário podem acessar apenas uma pequena parte do kernel por meio de uma interface exposta pelo kernel - o sistema chama . Se um processo executa uma chamada do sistema, uma interrupção de software é enviada ao kernel, que despacha o manipulador de interrupção apropriado e continua seu trabalho após o término do manipulador.
O código de espaço do kernel possui a propriedade para executar no "modo kernel", que (em seu computador desktop -x86- típico) é o que você chama de código que é executado no anel 0 . Normalmente na arquitetura x86, existem 4 anéis de proteção . Anel 0 (modo kernel), Anel 1 (pode ser usado por hipervisores ou drivers de máquinas virtuais), Anel 2 (pode ser usado por drivers, embora não tenha tanta certeza disso). O anel 3 é o que os aplicativos comuns executam. É o anel menos privilegiado e os aplicativos em execução nele têm acesso a um subconjunto das instruções do processador. O anel 0 (espaço do kernel) é o anel mais privilegiado e tem acesso a todas as instruções da máquina. Por exemplo, um aplicativo "simples" (como um navegador) não pode usar instruções de montagem x86lgdt
para carregar a tabela de descritores globais ou hlt
para interromper um processador.
Se for o primeiro, significa que o programa normal do usuário não pode ter mais de 3 GB de memória (se a divisão for 3 GB + 1 GB)? Além disso, nesse caso, como o kernel pode usar Alta Memória, porque para qual endereço de memória virtual as páginas da memória alta serão mapeadas, pois 1 GB de espaço no kernel será mapeado logicamente?
Para obter uma resposta, consulte a excelente resposta por wag aqui
-1
era para hipervisores? pt.wikipedia.org/wiki/Protection_ringOs anéis da CPU são a distinção mais clara
No modo protegido x86, a CPU está sempre em um dos quatro toques. O kernel do Linux usa apenas 0 e 3:
Esta é a definição mais rígida e rápida de kernel vs userland.
Por que o Linux não usa os anéis 1 e 2: https://stackoverflow.com/questions/6710040/cpu-privilege-rings-why-rings-1-and-2-arent-used
Como é determinado o anel atual?
O anel atual é selecionado por uma combinação de:
tabela descritor global: uma tabela na memória de entradas GDT e cada entrada possui um campo
Privl
que codifica o anel.A instrução LGDT define o endereço para a tabela atual do descritor.
Veja também: http://wiki.osdev.org/Global_Descriptor_Table
o segmento registra CS, DS, etc., que apontam para o índice de uma entrada no GDT.
Por exemplo,
CS = 0
significa que a primeira entrada do GDT está ativa no momento para o código em execução.O que cada anel pode fazer?
O chip da CPU é fisicamente construído para que:
anel 0 pode fazer qualquer coisa
o anel 3 não pode executar várias instruções e gravar em vários registros, principalmente:
não pode mudar seu próprio anel! Caso contrário, poderia definir-se para tocar 0 e os anéis seriam inúteis.
Em outras palavras, não é possível modificar o descritor de segmento atual , que determina o anel atual.
não pode modificar as tabelas da página: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work
Em outras palavras, não é possível modificar o registro CR3 e a paginação em si impede a modificação das tabelas de páginas.
Isso impede que um processo veja a memória de outros processos por motivos de segurança / facilidade de programação.
não pode registrar manipuladores de interrupção. Esses são configurados gravando nos locais da memória, o que também é impedido pela paginação.
Os manipuladores executam no anel 0 e quebram o modelo de segurança.
Em outras palavras, não é possível usar as instruções LGDT e LIDT.
não pode executar instruções de E / S como
in
eout
, portanto, possui acessos de hardware arbitrários.Caso contrário, por exemplo, as permissões de arquivo seriam inúteis se algum programa pudesse ler diretamente do disco.
Mais precisamente graças a Michael Petch : é realmente possível que o sistema operacional permita instruções de E / S no anel 3; isso é realmente controlado pelo segmento de estado da tarefa .
O que não é possível é que o anel 3 se dê permissão para fazê-lo se não o tiver em primeiro lugar.
O Linux sempre o desaprova. Consulte também: https://stackoverflow.com/questions/2711044/why-doesnt-linux-use-the-hardware-context-switch-via-the-tss
Como os programas e sistemas operacionais fazem a transição entre os anéis?
quando a CPU está ligada, ele começa a executar o programa inicial no anel 0 (bem, mas é uma boa aproximação). Você pode pensar que esse programa inicial é o kernel (mas normalmente é um gerenciador de inicialização que chama o kernel ainda no anel 0).
quando um processo da terra do usuário deseja que o kernel faça algo como gravar em um arquivo, ele usa uma instrução que gera uma interrupção como
int 0x80
ousyscall
para sinalizar o kernel. x86-64 Linux syscall hello world example:compile e execute:
GitHub upstream .
Quando isso acontece, a CPU chama um manipulador de retorno de chamada de interrupção que o kernel registrou no momento da inicialização. Aqui está um exemplo baremetal concreto que registra um manipulador e o utiliza .
Esse manipulador é executado no anel 0, que decide se o kernel permitirá essa ação, faça a ação e reinicie o programa userland no anel 3. x86_64
quando a
exec
chamada do sistema é usada (ou quando o kernel inicia/init
), o kernel prepara os registros e a memória do novo processo da terra do usuário, depois pula para o ponto de entrada e muda a CPU para tocar 3Se o programa tentar fazer algo malicioso, como gravar em um registro ou endereço de memória proibido (por causa da paginação), a CPU também chamará algum manipulador de retorno de chamada do kernel no anel 0.
Mas como a região do usuário foi malcriada, o kernel pode interromper o processo dessa vez ou avisar com um sinal.
Quando o kernel é inicializado, ele configura um relógio de hardware com alguma frequência fixa, o que gera interrupções periodicamente.
Esse relógio de hardware gera interrupções que executam o anel 0 e permite agendar quais processos da terra do usuário serão ativados.
Dessa forma, o agendamento pode ocorrer mesmo se os processos não estiverem fazendo nenhuma chamada do sistema.
Qual o sentido de ter vários anéis?
Existem duas vantagens principais na separação do kernel e da terra do usuário:
Como brincar com isso?
Eu criei uma configuração bare metal que deve ser uma boa maneira de manipular anéis diretamente: https://github.com/cirosantilli/x86-bare-metal-examples
Infelizmente, não tive a paciência de fazer um exemplo da terra do usuário, mas fui até a configuração de paginação, portanto a terra do usuário deve ser viável. Eu adoraria ver uma solicitação de recebimento.
Como alternativa, os módulos do kernel Linux são executados no anel 0, para que você possa usá-los para experimentar operações privilegiadas, por exemplo, leia os registros de controle: https://stackoverflow.com/questions/7415515/how-to-access-the-control-registers -cr0-cr2-cr3-de-um-programa-obtendo-segmento / 7419306 # 7419306
Aqui está uma configuração conveniente do QEMU + Buildroot para testá-lo sem matar o seu host.
A desvantagem dos módulos do kernel é que outros kthreads estão em execução e podem interferir nos seus experimentos. Mas, em teoria, você pode assumir todos os manipuladores de interrupção com o seu módulo do kernel e possuir o sistema, que seria realmente um projeto interessante.
Anéis negativos
Embora os anéis negativos não sejam realmente mencionados no manual da Intel, na verdade existem modos de CPU que possuem recursos adicionais que o próprio anel 0 e, portanto, são adequados para o nome "anel negativo".
Um exemplo é o modo hypervisor usado na virtualização.
Para obter mais detalhes, consulte: https://security.stackexchange.com/questions/129098/what-is-protection-ring-1
BRAÇO
No ARM, os anéis são chamados de Níveis de exceção, mas as idéias principais permanecem as mesmas.
Existem 4 níveis de exceção no ARMv8, comumente usados como:
EL0: userland
EL1: kernel ("supervisor" na terminologia do ARM).
Introduzida com a
svc
instrução (SuperVisor Call), anteriormente conhecida comoswi
montagem unificada , que é a instrução usada para fazer chamadas do sistema Linux. Olá, mundo, exemplo do ARMv8:GitHub upstream .
Teste com o QEMU no Ubuntu 16.04:
Aqui está um exemplo baremetal concreto que registra um manipulador SVC e faz uma chamada SVC .
EL2: hipervisores , por exemplo, Xen .
Introduzido com a
hvc
instrução (Chamada HyperVisor).Um hypervisor é para um sistema operacional, o que é um sistema operacional para a terra do usuário.
Por exemplo, o Xen permite executar vários SOs como Linux ou Windows no mesmo sistema ao mesmo tempo, e isola os SOs uns dos outros para segurança e facilidade de depuração, assim como o Linux faz para programas da área de usuário.
Os hipervisores são uma parte essencial da infraestrutura de nuvem atual: eles permitem a execução de vários servidores em um único hardware, mantendo o uso de hardware sempre próximo de 100% e economizando muito dinheiro.
A AWS, por exemplo, usou o Xen até 2017, quando foi transferida para a KVM .
EL3: mais um nível. Exemplo TODO.
Introduzido com a
smc
instrução (Secure Mode Call)O Modelo de Referência de Arquitetura do ARMv8 DDI 0487C.a - Capítulo D1 - O Modelo do Programador de Nível de Sistema AArch64 - A Figura D1-1 ilustra isso de maneira bonita:
Observe como o ARM, talvez devido ao benefício da retrospectiva, tenha uma convenção de nomenclatura melhor para os níveis de privilégio do que x86, sem a necessidade de níveis negativos: 0 sendo o mais baixo e 3 o mais alto. Níveis mais altos tendem a ser criados com mais frequência do que os inferiores.
O EL atual pode ser consultado com a
MRS
instrução: https://stackoverflow.com/questions/31787617/what-is-the-current-execution-mode-exception-level-etcO ARM não exige que todos os níveis de exceção estejam presentes para permitir implementações que não precisam do recurso para economizar área de chip. O ARMv8 "Níveis de exceção" diz:
Por exemplo, o QEMU usa EL1, mas EL2 e EL3 podem ser ativados com opções de linha de comando: https://stackoverflow.com/questions/42824706/qemu-system-aarch64-entering-el1-when-emulating-a53-power-up
Trechos de código testados no Ubuntu 18.10.
fonte
Sim, este é o caso de um sistema linux normal. Havia um conjunto de patches "4G / 4G" flutuando em um ponto que tornava os espaços de endereço do usuário e do kernel completamente independentes (a um custo de desempenho porque dificultava o acesso do kernel à memória do usuário), mas acho que não eles já foram mesclados a montante e o interesse diminuiu com a ascensão de x86-64
A maneira como o linux costumava funcionar (e ainda funciona em sistemas em que a memória é pequena em comparação com o espaço de endereço) era que toda a memória física era permanentemente mapeada na parte do kernel do espaço de endereço. Isso permitiu ao kernel acessar toda a memória física sem remapear, mas claramente não é escalável para máquinas de 32 bits com muita memória física.
Assim nasceu o conceito de memória baixa e alta. a memória "baixa" é permanentemente mapeada no espaço de endereço dos kernels. memória "alta" não é.
Quando o processador está executando uma chamada de sistema, ele está sendo executado no modo kernel, mas ainda no contexto do processo atual. Portanto, ele pode acessar diretamente o espaço de endereço do kernel e o espaço de endereço do usuário do processo atual (supondo que você não esteja usando os patches 4G / 4G acima mencionados). Isso significa que não há problema em que a memória "alta" seja alocada para um processo da terra do usuário.
Usar memória "alta" para fins de kernel é mais um problema. Para acessar memória alta que não é mapeada para o processo atual, ela deve ser mapeada temporariamente no espaço de endereço do kernel. Isso significa código extra e uma penalidade de desempenho.
fonte