# Núcleo (sistema operacional)

Na informática, o kernel (em português: "núcleo") é o componente central do sistema operativo da maioria dos dispositivos computacionais (computador e smartphone por exemplo); ele serve de ponte entre programas-aplicativos e o processamento real de dados feito a nível de hardware, cuja responsabilidades incluem gerenciar os recursos do sistema (a comunicação entre componentes de hardware e software) usando o método da abstração (encapsulamento) facilitando o uso do dispositivo.
Geralmente como um componente básico do sistema operativo, um núcleo pode oferecer a camada de abstração de nível mais baixo para os recursos (especialmente processadores e dispositivos de entrada/saída) que softwares aplicativos devem controlar para realizar sua função. Ele tipicamente torna estas facilidades disponíveis para os processos de aplicativos através de mecanismos de comunicação entre processos e chamadas de sistema. Para evitar um sistema com núcleo, deve-se projetar o sistema que não trabalhe com abstração, porém isto aumenta a complexidade do projeto.
Tarefas de sistemas operativos são feitas de maneiras diferentes por núcleos diferentes, dependendo do seu desenho e abordagem. Enquanto núcleos monolíticos tentam alcançar seus objetivos executando todos os códigos de sistema no mesmo espaço de endereçamento para aumentar a performance do sistema, micronúcleos executam a maioria dos serviços do sistema no espaço de usuário como servidores, buscando melhorar a manutenção e a modularidade do sistema operativo.

## Visão geral

Na definição de núcleo, o professor alemão Jochen Liedtke disse que a palavra é "tradicionalmente usada para definir a parte do sistema operativo que é obrigatória e comum a todo software no sistema."
A maioria dos sistemas operativos depende do conceito de núcleo. A existência de um núcleo é uma consequência natural de projetar um sistema de computador como séries de camadas de abstração, cada uma das funções dependendo das funções das camadas abaixo de si. O núcleo deste ponto de vista, é simplesmente o nome dado ao nível mais inferior de abstração que é implementado em software. Caso queira fazer/implementar um sistema operacional sem núcleo, ter-se-ia que projetar todo o software no sistema de modo a não utilizar abstração alguma; isto iria aumentar a complexidade e o projeto a tal ponto que apenas os sistemas mais simples seriam capazes de ser implementados.
Enquanto isto hoje é chamado núcleo, originalmente a mesma parte do sistema também foi chamado o nucleus ou caroço (Nota, no entanto, este termo caroço também foi usado para se referir a memória primordial de um sistema de computador, por que alguns dos primeiros computadores usaram uma forma de memória chamada memória de caroços magnéticos), e foi concebido originalmente como contendo apenas os recursos de suporte essenciais do sistema operativo.
Na grande maioria dos casos, o processo de iniciação começa executando o núcleo no modo supervisor. O núcleo depois inicializa a si e depois o primeiro processo. Depois disto, tipicamente, o núcleo não executa diretamente, apenas em resposta para eventos externos (ex., através de chamadas de sistema usados pelos aplicativos para requisitar serviços do núcleo, ou via interrupções usadas pelo hardware para notificar o núcleo sobre eventos). Além disso, tipicamente o núcleo fornece um laço que é executado sempre que nenhum processo esta disponível para execução; geralmente chamado de processo desocupado.
O desenvolvimento do núcleo é considerado uma das mais complexas e difíceis tarefas em programação. Sua posição central em um sistema operativo implica a necessidade de bom desempenho, que define o núcleo como peça de software crítica e torna seu desenvolvimento correto e implementação correta difícil. Devido a diversas razões, o núcleo pode até não ser capaz de utilizar mecanismos de abstração, que ele fornece a outro software. Tais razões incluem preocupações com o gerenciamento de memória e a falta de reentrância, logo o seu desenvolvimento torna-se ainda mais difícil para engenheiros de software. A otimização do uso de memória faz-se necessária, pois o núcleo não pode utilizar recursos de paginação por demanda, já que ele próprio fornece esta facilidade, tendo, então, que permanecer na memória para tal.
Geralmente um núcleo vai fornecer recursos para escalonamento de processos de baixo nível, comunicação entre processos, sincronização de processos, troca de contexto, manipulação de blocos de controle de processo, gerenciamento de interrupções, criação e destruição de processos, e suspensão e continuação de processos (veja estados de processos).

## Finalidades básicas

O principal propósito do núcleo é gerenciar os recursos do computador e permitir que outros programas rodem e usem destes recursos. Tipicamente estes recursos consistem de:
* Unidade de processamento central (CPU, o processador): principal parte de um sistema de computação, responsável por executar programas nele. O núcleo tem a responsabilidade de decidir, em qualquer momento, qual dos programas em execução deve ser alocado para o processador ou processadores (cada um dos quais geralmente pode executar um programa por vez)
* Memória: usada para armazenar instruções do programa e dados. Tipicamente, ambos precisam estar presentes na memória de modo a tornar a execução do programa possível. Frequentemente múltiplos programas buscarão acesso à memória ao mesmo tempo, na maioria das vezes exigindo mais memória do que o computador pode disponibilizar. O núcleo é responsável pela decisão de que memória cada processo pode utilizar, e determinar o que fazer quando menos do suficiente está disponível.
* Dispositivos de entrada/saída, tais como teclado, mouse, entradas de disquete, impressoras, telas etc. O núcleo aloca pedidos de aplicativos para realizar entrada/saída para um dispositivo apropriado (ou subseção de um dispositivo, no caso de arquivos em um disco ou janelas em uma tela) e fornece métodos convenientes para o uso do dispositivo (tipicamente abstraído ao ponto onde o aplicativo não precisa mais conhecer os detalhes da implementação do dispositivo).
Aspectos importantes no gerenciamento de recursos são a definição de um domínio de execução (espaço de endereçamento) e o mecanismo de proteção utilizado para mediar o acesso a recursos dentro de um domínio.
Núcleos geralmente não oferecem métodos para sincronização e comunicação entre processos (IPC (em inglês)).
Um núcleo pode implementar estes recursos ele mesmo, ou depender de alguns processos que ele executa para fornecer estas facilidades a outros processos, no entanto neste caso ele deve oferecer algum modo do IPC permitir que processos acessem as facilidades fornecidas um pelo outro.
Finalmente, um núcleo deve oferecer um método de acesso a estas facilidades para os programas em execução.

### Gerenciamento de processos

A principal tarefa de um núcleo é permitir a execução de aplicativos e ajudá-los com recursos como abstrações de hardware. Um processo define-se por um fluxo de atividades que se utiliza de recursos. Nesse caso, a execução de uma aplicação gera um processo. Processo este que delimita as porções da memória o aplicativo pode acessar. O gerenciamento de processos do núcleo deve levar em conta o equipamento de hardware embarcado para proteção de memória.
Para executar um aplicativo, um núcleo geralmente cria um espaço de endereçamento, carrega o arquivo contendo as instruções do programa na memória (talvez via paginação por demanda), cria uma pilha para o programa e ramos para uma dada localização dentro do programa, iniciando, portanto, a sua execução.
Núcleos multitarefa são capazes de dar ao usuário a ilusão de que um número de processos que está sendo executado simultaneamente no sistema é maior do que o número de processos que aquele sistema é fisicamente capaz de executar. Usualmente, o número de processos que um sistema pode executar simultaneamente é igual ao número de CPUs que ele possui instaladas (no entanto, isto pode não ser o caso de processadores que suportam múltiplas linhas de execução simultâneas).
Em um sistema multitarefas preemptivo, o núcleo dá a todos programas uma parcela do tempo e alterna entre os processos tão rapidamente que dá ao usuário a impressão de que eles estão sendo executados simultaneamente. O núcleo utiliza algoritmos de escalonamento para determinar qual processo será executado a seguir e quanto tempo lhe será dado. O algoritmo escolhido pode permitir que alguns processos tenham uma prioridade mais alta que muitos outros. O núcleo geralmente também provê a esses processos uma maneira de comunicarem-se; isto é chamado comunicação entre processos (IPC (em inglês)) e as principais implementações são memória compartilhada, troca de mensagens e chamadas de procedimento remoto (veja computação concorrente).
Outros sistemas (particularmente em computadores menos potentes) podem fornecer multitarefa de cooperação, em que para cada processo é permitida execução ininterrupta até que ele faça uma requisição especial ao núcleo para que ele alterne para outro processo. Tais requisições são conhecidos como "indulgências" (yield (em inglês)), e tipicamente ocorrem ou em resposta a um pedido para comunicação entre processos, ou para esperar até o acontecimento de um evento. Versões mais antigas do Microsoft Windows e do Mac OS utilizaram o conceito de multitarefa cooperativa, mas alternaram para esquemas preemptivos conforme a potência dos computadores alvo de seu mercado aumentava.
O sistema operativo pode também suportar o multiprocessamento (multiprocessamento simétrico (SMP (em inglês)), ou, acesso não-uniforme a memória); neste caso, diferentes programas e linhas de execução podem rodar em diferentes processadores. Um núcleo para tal sistema deve ser projetado para ser reentrante, o que significa que ele pode rodar seguramente duas partes de seu código simultaneamente. Isto tipicamente significa oferecer mecanismos de sincronização (como trava-giros) para assegurar que dois processadores não tentarão modificar os mesmos dados ao mesmo tempo.

### Gerenciamento de memória

O núcleo possui acesso completo à memória do sistema e deve permitir que processos acessem a memória com segurança conforme suas necessidades. Frequentemente, o primeiro passo para isso é o endereçamento virtual, geralmente alcançado através da paginação e/ou segmentação. O endereçamento virtual permite ao núcleo fazer com que um endereço físico pareça ser outro, o endereço virtual. Espaços de endereço virtual podem ser diferentes para diferentes processos; a memória acessada por um processo através de um endereço virtual particular pode ser diferente da acessada por um outro processo através do mesmo endereço virtual. Isto possibilita que todos os programas funcionem como se estivessem sendo executados sozinhos, além do núcleo, e por isso evita que aplicativos travem uns aos outros.
Em vários sistemas, o endereço virtual de um programa pode se referir a dados que não estão na memória atualmente. A cama de indireção oferecida pelo endereçamento virtual permite que o sistema utilize meios de armazenagem de dados, como um disco rígido, para armazenar o que de outro modo teria que permanecer na memória RAM. Como resultado. sistemas operativos podem permitir que programas usem mais memória do que está fisicamente disponível. Quando um programa precisa de dados que não estão na RAM, a CPU avisa o núcleo que isto ocorre, e este responde escrevendo o conteúdo de um bloco de memória inativo para o disco (se necessário) e substituindo-o na memória com os dados requisitados pelo programa. O programa pode então continuar sua execução do ponto em que foi suspenso. Este esquema é geralmente conhecido como paginação por demanda.
Endereçamento virtual também permite a criação de partições virtuais de memória em duas áreas separadas, uma sendo reservada para o núcleo (espaço de núcleo) e o outra para os aplicativos (espaço de usuário). Os aplicativos não têm permissão do processador para acessar a memória do núcleo, prevenindo, portanto, que um aplicativo possa danificar o núcleo em execução. Esta partição fundamental de espaço de memória contribuiu muito para os projetos de núcleos realmente de propósito geral e é quase universal em tais sistemas, embora alguns núcleos de pesquisa (como o Singularity) usem outros métodos.

### Gerenciamento de dispositivos

Para realizar funções úteis, processos precisam acessar periféricos conectados ao computador, que são controlados pelo núcleo através do driver do dispositivo. Por exemplo, para mostrar ao usuário algo utilizando a tela, um aplicativo teria que fazer uma requisição ao núcleo que encaminharia a requisição para o seu driver de tela, que é o responsável por gerar a imagem através dos pixels.
Um núcleo deve manter uma lista de dispositivos disponíveis. Esta lista pode ser conhecida de antemão (como em um sistema embarcado em que o núcleo será reescrito se o hardware disponível mudar), configurada pelo usuário (típico em computadores pessoais antigos e em sistemas projetados para uso pessoal) ou detectada pelo sistema durante a execução (normalmente chamado Plug and Play).
Num sistema "Plug and Play", um dispositivo realiza primeiro uma sondagem nos diferentes barramentos de hardware, como Interconector de Componentes Periféricos (PCI (em inglês)) ou Barramento Serial Universal (USB (em inglês)), para detectar os dispositivos instalados e depois procura os drivers apropriados.
Como a gestão de dispositivos é uma tarefa muito especifica do SO, os drivers são manipulados de forma diferente pelo tipo de arquitetura do núcleo. Em todos os casos, porém, o núcleo deve fornecer a entrada/saída para permitir que os drivers acessem fisicamente seus dispositivos através de alguma porta ou localização da memória. Decisões muito importantes precisam ser feitas ao projetar o sistema de gestão de dispositivos, já que alguns projetos de acesso podem envolver trocas de contexto, tornando a operação custosa para o processador e causando um gasto excessivo de recursos.$^[$

### Chamadas do sistema

Para realmente realizar algo útil, um processo deve acessar os serviços oferecidos pelo núcleo. Isto é implementado por cada núcleo, mas a maioria oferece uma Biblioteca padrão do C ou uma Interface de programação de aplicativos, que envolve as funções relativas ao núcleo.
O método de invocar as funções do núcleo varia entre diferentes núcleos. Se o isolamento de memória está sendo usado, é impossível para um processo de usuário chamar o núcleo diretamente, visto que isso seria uma violação das regras de controle de acesso do processador. Algumas possibilidades são:
* Usar uma interrupção de software simulada. Este método está disponível na maioria dos hardwares, e é, portanto, muito comum.
* Usando um portão de chamada. Um portão de chamada é um endereço especial armazenado pelo núcleo em uma lista na memória do núcleo em uma localização conhecida pelo processador. Quando o processador detecta uma chamada para este endereço, ele ao invés disso redireciona para a localização alvo sem causar nenhuma violação de acesso. Exige suporte no hardware, mas este tipo de hardware é muito comum.
* Usando uma instrução de chamada de sistema especial. Esta técnica exige suporte especial no hardware, que em algumas arquiteturas habituais não possuem (notavelmente, x86). Instruções de chamadas de sistema foram adicionadas a modelos recentes de processadores x86, embora, poucos (mas não todos) sistemas operativos fazem uso destes quando disponíveis.
* Usando uma fila baseada na memória. Um aplicativo que faz um grande número de requisições, mas não precisa esperar o resultado de cada uma pode adicionar detalhes das requisições em uma área da memória que o núcleo sonda periodicamente para encontrar requisições.

## Decisões de desenho
### Problemas com o suporte do núcleo para proteção

Uma consideração importante no desenho do núcleo é o suporte que ele oferece para proteção contra faltas (tolerância a falhas) e comportamentos mal-intencionados (segurança). Estes dois aspectos geralmente não são claramente distinguidos, e a separação no desenho do núcleo leva a rejeição de uma estrutura hierárquica de proteção.
Os mecanismos ou políticas oferecidas pelo núcleo podem ser classificados de acordo com vários critérios, como: estático (forçado durante o tempo de compilação) ou dinâmico (forçado durante o tempo de execução); preemptivo ou pós-detecção; de acordo com os princípios de proteção a que eles correspondem (ex. Denning); quer eles sejam suportados pelo hardware ou baseados em linguagem; quer eles sejam mais um mecanismo aberto ou má política compulsiva; e muito mais.

#### Tolerância a falhas

Uma medida útil para o nível de tolerância a falhas de um sistema é quão estrito ele é com relação ao princípio do menor privilégio. Em casos onde múltiplos programas estão rodando em um único computador, é importante prevenir falhas em um dos programas de afetar negativamente outro. Estendendo-se ao desenho com más intenções mais do que a falha em si, isto também implica a segurança, quando é necessário impedir processos de acessar informações sem que lhes seja dada a devida permissão.
As duas principais implementações via hardware para proteção (de informações sensíveis) são domínios hierárquicos de proteção (também chamadas arquiteturas anel, arquiteturas de segmento ou modo supervisor), e endereçamento baseado em capacidades.
Domínios hierárquicos de proteção são muito menos flexíveis, como no caso de qualquer núcleo com uma estrutura hierárquica presumida como um critério de desenvolvimento global. No caso de proteção não é possível designar diferentes privilégios a processos que não estão no mesmo nível de privilégio, e por isso não é possível corresponder aos quatro princípios de Denning para a tolerância a falhas, particularmente o princípio do menor privilégio. Domínios hierárquicos de proteção também carregam uma enorme desvantagem na performance, já que a interação entre diferentes níveis de proteção, quando um processo tem que manipular uma estrutura de dados em ambos 'modo usuário' e 'modo supervisor', sempre exige cópia de mensagens (transmissão por valor). Um núcleo baseado em capacidades, no entanto, é mais flexível em designar privilégios, pode corresponder aos princípios de Denning para a tolerância a falhas, e geralmente não sofrem de problemas de performance da cópia por valor.
Ambas implementações tipicamente exigem algum suporte de hardware ou firmware para serem operáveis e eficientes. O suporte de hardwarepara domínios hierárquicos de proteção geralmente é de "modos de CPU." Um modo simples e eficiente de fornecer suporte a hardware é delegar à unidade de gerenciamento de memória a responsabilidade por checar as permissões de acesso para todos acessos a memória, um mecanismo chamado endereçamento baseado em capacidades. Falta na maioria das arquiteturas comerciais, o suporte a MMU para capacidades.
Uma abordagem alternativa é simular capacidades usando domínios hierárquicos comumente suportados; nesta abordagem, cada objeto protegido deve residir num espaço de endereçamento ao qual o aplicativo não possui acesso; o núcleo também mantém uma lista de capacidades em tal memória. Quando um aplicativo precisa acessar um objeto protegido por uma capacidade, ele realiza uma chamada de sistema e o núcleo realiza o acesso a ele. O custo de performance de trocar de espaço de endereçamento limita a praticabilidade desta abordagem em sistemas com interações complexas entre objetos, mas é utilizado nos sistemas operativos atuais para objetos que não são acessados frequentemente ou que não devem ser feitos rapidamente. Implementações onde os mecanismos de proteção não suportados pelo firmware, mas são, ao invés disso, simulados em níveos mais altos (ex. simulando capacidades ao manipular tabelas de páginas em hardware que não possui suporte direto), são possíveis, mas há implicações de performance. No entanto, falta de suporte no hardware pode não ser problema, para sistemas que escolhem usar uma proteção baseada em linguagem.
Uma decisão importante no projeto do núcleo é a escolha dos níveis de abstração em que os mecanismos e políticas de segurança devem ser implementados. Os mecanismos de segurança do núcleo têm um papel crítico no suporte a segurança nos níveis superiores.
Uma abordagem é utilizar suporte no núcleo e firmware para tolerância a falhas (ver acima), e montar as políticas de segurança para comportamento malicioso em cima disso (adicionando recursos como mecanismos de criptografia quando necessário), delegar mais responsabilidade para o compilador. Implementações que delegam a aplicação de políticas de segurança para o compilador e/ou nível do aplicativo são geralmente chamados segurança baseada em linguagem.
A falta de muitos mecanismos críticos de segurança nos principais sistemas operativos impede a implementação adequada de políticas de segurança no nível de abstração do aplicativo. Na verdade, um engano muito comum na segurança de computadores é que qualquer política de segurança pode ser implementada no aplicativo, independentemente do suporte no núcleo.

#### Proteção baseada em hardware ou linguagem

Hoje, típicos sistemas de computação usam regras aplicadas pelo hardware sobre quais programas têm permissão para acessar quais dados. O processador monitora a execução e desliga um programa que viole uma regra (ex., um processo de usuário que tenta ler ou escrever na memória do núcleo, e assim por diante). Em sistemas que não possuem suporte para capacidades, processos são isolados um do outro, utilizando-se espaços de endereçamento separados. Chamadas de um processo de usuário no núcleo são regidas pela exigência de que eles usem um dos métodos de chamada do sistema descritos acima.
Uma abordagem alternativa é usar proteção baseada em linguagem. Em um sistema de proteção baseado em linguagem, o núcleo vai permitir a execução apenas de código produzido por um compilador em que ele confie. A linguagem pode então, ser projetada de modo tal que será impossível para o programador instruir algo que violaria os requisitos de segurança.
Desvantagens incluem:
* Demora maior para a inicialização efetiva do aplicativo. Aplicações devem ser verificadas sempre que são iniciadas para garantir que foram compiladas utilizando um compilador "correto" ou podem necessitar de recompilação de código fonte ou bytecode.
* Sistemas de tipo inflexível. Em sistemas tradicionais, aplicativos realizam frequentemente operações que não são de tipagem forte. Tais operações não podem ser permitidas em um sistema de proteção baseado em linguagem, o que significa que aplicativos podem precisar ser reescritos e podem, em alguns casos, perder performance.
Vantagens desta abordagem incluem:
* Separação de espaços de endereçamento desnecessária. A troca de espaços de endereçamento é uma operação lenta que causa grande degradação na performance, e muito trabalho de otimização é feito atualmente para prevenir trocar desnecessárias nos sistemas operativos. Trocar é complemente desnecessário em um sistema de proteção baseada em linguagem, já que todo código opera no mesmo espaço de endereçamento.
* Flexibilidade. Qualquer esquema de proteção que possa ser desenvolvida para ser expresso através de linguagem de programação pode ser implementada através deste método. Mudanças no esquema de proteção (ex. de um sistema hierárquico para um baseado em capacidades) não exigem novo hardware.
Exemplos de sistemas com proteção baseada em linguagem incluem o JX e Singularity.

### Cooperação de processos

Edsger Dijkstra provou que partindo de um ponto de vista lógico, operações atômicas de travamento e destravamento operando em semáforos binários são suficientemente primitivos para expressar a qualquer funcionalidade de cooperação entre processos. No entanto esta abordagem é geralmente tomada como deficiente em termos de segurança e eficiência, enquanto que uma abordagem via troca de mensagens é mais flexível.

### Gerenciamento de dispositivos de entrada/saída

A ideia de um núcleo onde dispositivos de entrada/saída são gerenciados uniformemente com outros processos, como processos paralelos em cooperação, foi proposta e implementada primeiramente por Brinch Hansen (embora ideias similares tenham sido sugeridas em 1967). Na descrição de Hansen disto, os processos "comuns" são chamados processos internos, enquanto que os dispositivos de entrada/saída são chamados processos externos.

## Formas de desenvolvimento

Naturalmente, as tarefas e recursos listados acima podem ser fornecidas de vários modos que diferem entre si em projeto e implementação.
O princípio da separação entre o mecanismo e a política é a diferença substancial entre a filosofia de micronúcleo e núcleo monolítico. Aqui um mecanismo é o apoio que permite a implementação de várias políticas diferentes, enquanto uma política é um "modo de operação" particular. Por exemplo, um mecanismo pode oferecer às tentativas de entrada de um usuário um método de chamar um servidor de autorização para determinar se um acesso deve ser dado; uma política pode ser para o servidor de autorização exigir uma senha e checá-la contra uma senha embaralhada armazenada numa base de dados. Devido ao fato de o mecanismo ser genérico, a política pode ser alterada com mais facilidade (ex. ao exigir o uso de um passe) do que se um mecanismo e política fossem integrados no mesmo módulo.
Em um micronúcleo mínimo algumas políticas básicas são incluídas, e seus mecanismos permite que o que está rodando sobre o núcleo (a parte remanescente do sistema operativo e outras aplicações) decida quais políticas adotar (como gerenciamento de memória, escalonamento de processo de alto nível, gerenciamento de sistema de arquivos, etc.). Um núcleo monolítico ao invés disso, tende a incluir várias políticas, então restringindo o resto do sistema dependente delas.
Per Brinch Hansen apresentou um argumento convincente a favor da separação do mecanismo e da política. A falha em preencher completamente esta separação, é uma das maiores causas para a falta de inovação nos sistemas operativos existentes atualmente, um problema comum nas arquiteturas de computador. O projeto monolítico é induzido pela abordagem de arquitetura "modo núcleo"/"modo usuário" para proteção (tecnicamente chamada de domínios hierárquicos de proteção), que é comum em sistemas comercias convencionais; na verdade, todo módulo que necessite de proteção é, portanto, preferivelmente incluído no núcleo. Esta ligação entre projeto e "modo privilegiado" pode ser reconduzida até o problema chave da separação do mecanismo e da política; de fato, a abordagem de arquitetura de "modo privilegiado" se funde ao mecanismo de proteção com as políticas de segurança, enquanto a principal abordagem de arquitetura alternativa, endereçamento baseado em capacidades, claramente distingue ambos, levando naturalmente ao desenvolvimento de um micronúcleo design (veja Separação entre proteção e segurança).
Enquanto núcleos monolíticos executam todo seu código no mesmo espaço de endereçamento (espaço de núcleo) micronúcleos tentam executar a maior parte dos seus serviços no espaço de usuário, buscando aprimorar a manutenção e modulabilidade do código base. A maioria dos núcleos não se encaixa exatamente em uma destas categorias, sendo mais encontrados entre estes dois projetos. Os chamados núcleos híbridos. Projetos mais exóticos como nanonúcleos e exonúcleos estão disponíveis, mas são usados raramente em sistemas produtivos. O virtualizador Xen, por exemplo, é um exonúcleo.

### Núcleos monolíticos

Em um núcleo monolítico, todos os serviços do sistema operativo rodam junto com a linha de execução principal do núcleo, portanto, também se encontram na mesma área de memória. Esta abordagem permite o acesso vasto e poderoso de hardwares. Alguns desenvolvedores, como desenvolvedor do UNIX Ken Thompson, defendem que é "mais fácil de implementar um núcleo monolítico" que micronúcleos. As principais desvantagens de núcleos monolíticos são as dependências entre os componentes do sistema - um defeito em um driver de dispositivo pode paralisar todo o sistema - e o fato de núcleos grandes podem se tornar muito difíceis de manter.
* BSD
* Linux
* MS-DOS e derivados, incluindo Windows 95, Windows 98 e Windows ME
* Solaris
* Palm OS

### Micronúcleos

A abordagem de micronúcleo consiste em definir abstrações simples sobre o hardware, com um conjunto de primitivos ou chamadas de sistema para implementar serviços mínimos do sistema operativo como gerenciamento de memória, multitarefas, e comunicação entre processos. Outros serviços, incluindo aqueles normalmente fornecidos por um núcleo monolítico como rede, são implementados em programas de espaço de usuário, conhecidos como servidores. Micronúcleos são mais fáceis de manter do que núcleos monolíticos, mas um grande número de chamadas de sistemas de trocas de contexto podem desacelerar o sistema por que eles geralmente geram mais degradação na performance do que simples chamadas de função.
Um micronúcleo permite a implementação das partes restantes do sistema operativo como aplicativos normais escritos em linguagem de alto nível, e o uso de diferentes sistemas operativos sobre o mesmo núcleo não modificado. Ele também torna possível alternar dinamicamente entre sistemas operativos e manter mais de um deles ativos simultaneamente.
* Hurd
* MINIX
* Mach

### Núcleos monolíticos x micronúcleos

Conforme o núcleo do computador crescem, um número de problemas se torna evidente. Um dos mais óbvios é que o espaço de memória aumenta. Isto é mitigado de certo modo ao aperfeiçoar o sistema de memória virtual, mas nem todas arquitetura de computador suportam memórias virtuais. Para reduzir o espaço utilizado pelo núcleo, modificações extensivas precisam ser realizadas para remover cuidadosamente código inútil, que pode ser muito difícil devido a dependências pouco aparentes entre partes de um núcleo com milhões de linhas de código.
Pelo começo dos anos 1990, devido a vários problemas de núcleos monolíticos em comparação a micronúcleos, núcleos monolíticos foram considerados obsoletos por virtualmente todos pesquisadores de sistemas operativos. Como resultado, o projeto do Linux, um núcleo monolítico mais do que um micronúcleo foi o tópico da famosa discussão inflamada entre Linus Torvalds e Andrew Tanenbaum. Há méritos em ambos argumentos presentes no debate Tanenbaum–Torvalds.

#### Performance

Núcleos monolíticos são projetados para que todo o seu código fique no mesmo espaço de endereçamento (espaço de núcleo), que alguns desenvolvedores argumentam ser necessário para aumentar a performance do sistema. Alguns desenvolvedores também sustentam a hipótese de que núcleos monolíticos são extremamente eficientes se forem bem escritos.
A performance de micronúcleos construídos nos anos 1980 e começos dos 1990 era terrível. Estudos empíricos que mediram a performance destes micronúcleos não analisaram os motivos para tal ineficiência. As explicações para estes dados foram deixadas para o "folclore", com a suposição de que eles eram devido ao aumento da frequência da troca de modo núcleo para modo usuário, devido a maior frequência de comunicação entre processos e a maioria frequência de trocas de contexto.
De fato, como foi conjeturado em 1995, os motivos para a terrível performance dos micronúcleos podem também ter sido: (1) uma real ineficiência na implementação de toda a abordagem de micronúcleo, (2) conceitos particulares implementados nesses micronúcleos, e (3) a implementação individual destes conceitos. Portanto ainda falta estudar se a solução para construir um micronúcleo eficiente foi, ao contrário de tentativas anteriores, a de aplicar as técnicas corretas de construção.
No outro extremo, a arquitetura de domínios hierárquicos de proteção que leva a um projeto de núcleo monolítico gera impactos significativos na performance cada vez que há uma interação entre diferentes níveis de proteção (ex. quando um processo tem que manipular uma estrutura de dados em ambos 'modo usuário' e 'modo supervisor'), desde que isto exija cópia de mensagem por valor.
Em meados de 1990, a maioria dos pesquisadores abandonou a crença de que ajustes cuidadosos poderiam reduzir estes impactos dramaticamente,$^[$ mas recentemente, novos micronúcleos, otimizados para performance, tais como os L4 e K42 vêm trabalhando nestes problemas.

### Núcleos híbridos

Núcleos híbridos são um acordo entre o desenvolvimento de micronúcleos e núcleos monolíticos. Isto implica executar alguns serviços (como a pilha de rede ou o sistema de arquivos) no espaço do núcleo para reduzir o impacto na performance$^[$ de um micronúcleo tradicional, mas ainda executar o código no núcleo (como drivers de dispositivos) como servidores no espaço de usuário.
* BeOS / Haiku
* Microsoft Windows NT
* XNU (núcleo do Darwin utilizado no Mac OS X)

### Nanonúcleos

Um nanonúcleo delega virtualmente todos os serviços — incluindo até os mais básicos como controlador de interrupções ou o temporizador — para drivers de dispositivo para tornar o requerimento de memória do núcleo ainda menor do que o dos tradicionais micronúcleos.
* Adeos
* Dycos
* EROS
* EKA2
* Jari Operating System
* JNode
* KeyKOS
* LSE/OS
* Mac OS nanonúcleo
* OZONE
* Trion Operating System
* XtratuM

### Exonúcleos

Um exonúcleo é um tipo de núcleo que não abstrai hardware em modelos teóricos. Ao invés disso, ele aloca recursos físicos de hardware (como o tempo de um processador), páginas de memória e blocos de disco, para diferentes programas. Um programa rodando em um exonúcleo pode ligar para uma biblioteca do sistema operacional que usa o exonúcleo para simular as abstrações de um sistema operativo conhecido, ou ele pode desenvolver abstrações específicas para aquele aplicativo para uma performance superior.

## História do desenvolvimento do núcleo
### Os primeiros sistemas operativos

Falando estritamente, um sistema operativo (e, isto inclui um núcleo) não é obrigado a rodar um computador. Programas podem ser carregados diretamente e executados na máquina de "bare metal", desde que os autores destes programas estiverem dispostos a trabalhar sem nenhuma abstração de hardware ou suporte a sistema operativo. Os primeiros computadores operaram desta maneira durante os anos de 1950, começo dos 1960, eram reiniciados e recarregados entre cada execução de diferentes programas. Eventualmente, pequenos programas auxiliares como carregadores de programas e depuradores foram mantidos na memória entre as execuções, ou carregados de memória somente de leitura. Conforme estas eram desenvolvidas, elas formaram a base do que depois se tornaria o núcleo dos sistemas operativos. A abordagem de "bare metal" ainda é usada hoje em alguns consoles de videogame e sistemas embarcados, mas no geral, computadores novos usam sistemas operativos e núcleos modernos.
Em 1969 o RC 4000 Multiprogramming System (microcomputador RC-4000) introduziu a filosofia de desenvolvimento de sistemas de pequeno núcleo "na qual sistemas operativos para diferentes propósitos poderiam ser criados de maneira metódica", algo que poderia ser chamado de abordagem de micronúcleo.

### Sistemas operativos de tempo compartilhado

Na década precedendo o fenômeno Unix, computadores aumentaram muito em poder de processamento — ao ponto de os operadores de computador estarem buscando novos modos de conseguir com que as pessoas usassem o tempo livre em suas máquinas. Uma das maiores evoluções durante esta era foi o tempo compartilhado, em que um número de usuários conseguiria pequenas parcelas do tempo do computador, em uma taxa que fazia parecer que cada um estava conectado à sua própria máquina, embora mais lenta.
O desenvolvimento dos sistemas de tempo compartilhado levou a inúmeros problemas. Um deles foi que usuários, particularmente em universidades, em que os sistemas estavam sendo desenvolvidos, pareciam tentar hackear o sistema para conseguir mais tempo de processamento. Por esta razão, segurança e controles de acesso se tornaram um foco principal do projeto Multics em 1965. Outro problema corrente era gerenciar apropriadamente os recursos do sistema: usuários gastavam a maior parte do tempo iniciando na tela e pensando ao invés de realmente utilizar os recursos do computador, e o sistema de tempo compartilhado deveria dar tempo de processamento para um usuário ativo durante estes períodos. Por fim, tipicamente os sistemas ofereciam uma hierarquia de memória de várias camadas de profundidade, e particionar este recurso caro levou a um grande desenvolvimento nos sistemas de memória virtual.

### Unix

Durante a fase de projeto do Unix, os programadores decidiram modelar todo dispositivo de alto nível como um arquivo, por que eles acreditavam que o propósito da computação era a transformação de dados. Por exemplo, impressoras eram representadas como um "ficheiro" em uma localização conhecida — quando dados eram copiados para o arquivo, ela realizava a impressão destes. Outros sistemas, para fornecer uma funcionalidade similar, possuem a tendência de virtualizar dispositivos em um nível mais baixo — ou seja, ambos dispositivos e ficheiros seriam instâncias de algum conceito de nível inferior. Virtualizar o sistema a nível de ficheiros permitiu aos usuários manipular todo o sistema usando seus conceitos e ferramentas de gerenciamento de ficheiros, simplificando a operação dramaticamente. Como uma extensão do mesmo paradigma, o Unix permite que programadores manipulem arquivos usando uma série de pequenos programas, usando o conceito de encadeamento, que permite aos usuários completar operações em etapas, alimentando um ficheiro através de uma cadeia de ferramentas de propósito único. Embora o resultado final fosse o mesmo, usar programas menores aumentou drasticamente a flexibilidade, assim como o uso e desenvolvimento, permitindo que o usuário modificasse seu fluxo de trabalho ao adicionar ou remover um programa da cadeia.
No modelo Unix, o sistema operativo consiste de duas partes: a primeira é uma enorme coleção de programas de utilidades que guiam a maioria das operações; a segunda, o núcleo que executa os programas. No Unix, do ponto de vista da programação, a distinção entre os dois é extremamente tênue: o núcleo é um programa rodando no modo supervisor, que age como um carregador de programas e supervisor para os pequenos programas de utilidade que integram o resto do sistema e fornecem travas e serviços de entrada/saída para estes programas; além disso, o núcleo não intervém de modo algum no espaço de usuário.
Ao longo dos anos o modelo de computação mudou, e o tratamento do Unix de tudo como um ficheiro ou fluxo de bytes não era mais universalmente aplicável como antes. Embora um terminal pudesse ser tratado como um ficheiro ou fluxo de bytes, de que se exibia ou lia, o mesmo não parecia ser verdade para a interface gráfica. As redes se tornaram outro problema. Mesmo se a comunicação de rede pudesse ser comparada ao acesso de ficheiros, a arquitetura de baixo nível orientada a pacotes lidava com pedaços discretos de dados e não com ficheiros completos. Conforme a capacidade dos computadores crescia, o Unix se tornava cada vez mais desorganizado com relação a código. Enquanto núcleos podiam ter 100.000 linhas de código nos anos 1970 e 1980, núcleos sucessores modernos do núcleo do Unix como o Linux possuem mais de 4,5 milhões de linhas.
Derivados modernos do Unix são geralmente baseados em núcleos monolíticos que carregam módulos. Exemplos disto são o Linux, um núcleo monolítico com suporte a núcleos, e diversas distribuições que o incluem, assim como os núcleos das variantes do BSD como FreeBSD, DragonflyBSD, OpenBSD, NetBSD etc. Além destas alternativas, desenvolvedores amadores mantém uma comunidade ativa de desenvolvimento de sistemas operativos, cheia de núcleos que são criados como passatempo que acabam compartilhando vários dos recursos com o Linux e/ou os núcleos do FreeBSD, DragonflyBSD, OpenBSD e NetBSD e/ou sendo compatíveis com eles.

### macOS

A Apple lançou Mac OS pela primeira vez em 1984, empacotado com o seu computador pessoal Apple Macintosh. Nos primeiros lançamentos, o Mac OS (ou Sistema de Software, como ele foi chamado) careceu de muitos recursos básicos, como multitarefas e um sistema hierárquico. Com o passar tempo, o sistema operacional evoluiu e se tornou o Mac OS 9 com alguns recursos adicionados, mas o núcleo se manteve basicamente o mesmo.$^[$ Em oposição a isto, o macOS é baseado no Darwin, que utiliza um conceito de núcleo híbrido chamado XNU, criado combinando o núcleo do 4.3BSD e o Mach.

### Amiga

O Amiga da Commodore foi lançado em 1985, e estava dentre os primeiros (e certamente mais bem sucedidos) computadores domésticos a apresentar um sistema operativo com um micronúcleo. O núcleo do Amiga, a exec.library, era pequena mas capaz, oferecendo multitarefas rápidas e preemptivas em hardware similar ao do Apple Macintosh, e um sistema avançado de ligações dinâmicas que permitia uma expansão fácil.

### Microsoft Windows

O Microsoft Windows foi lançado em 1985 como uma extensão para o MS-DOS. Devido à sua dependência de outro sistema operativo, todas as versões até a 95 são consideradas um ambiente operacional (e não um sistema operativo propriamente dito). Tal linha de produtos continuou por 1980 e 1990, resultando nos lançamentos das séries Windows 9x, atualizando as capacidades do sistema para endereçamento de 32 bits e multitarefas preemptivo, ao longo dos anos 1990, terminando com o lançamento do Windows Me em 2000.
O lançamento do Windows XP em outubro de 2001 uniu as duas linhas de produto, com a intenção de combinar a estabilidade do núcleo NT com os recursos ao consumidor das séries 9x. A arquitetura do núcleo do Windows NT é considerada híbrida pois o próprio núcleo contém tarefas como o gerenciador de janelas e o gerenciador de comunicação entre processos, mas vários subsistemas são executados no modo de usuário. O ponto de quebra exato entre espaço de usuário e espaço de núcleo têm deslocado conforme a versão, mas a introdução do UMDF (User-Mode Driver Framework) no Windows Vista e do escalonamento de linha de execução no espaço de usuário no Windows 7, deslocou mais recursos do núcleo para processos no espaço de usuário.

### Desenvolvimento de micronúcleos

Embora o Mach, desenvolvido na Universidade Carnegie Mellon de 1985 a 1994, é o micronúcleo de propósito geral mais conhecido, outros micronúcleos foram desenvolvidos com objetivos mais específicos. Uma família de micronúcleos L4 (principalmente o micronúcleo L3 e o L4) foi criada para demonstrar que micronúcleos não são necessariamente lentos. Implementações mais novas como Fiasco e Pistachio são capazes de executar o Linux junto com outros processos L4 em espaços de endereçamento separados.
QNX é um Sistema operativo de tempo-real com um projeto de micronúcleo minimalista que vem sendo desenvolvido desde 1982, sendo mais bem-sucedido do que o Mach em alcançar os objetivos do paradigma do micronúcleo. Ele é usado principalmente em sistemas embarcados e em situações em que o software não pode falhar, como nos braços robóticos do ônibus espacial e máquinas que controlam a moeção de vidro a tolerâncias extremamente finas, onde um minúsculo erro poderia custar centenas de milhares de reais.