Eu sei que o Linux está disponível e foi portado para muitas plataformas diferentes, como para X86, ARM, PowerPC etc.
No entanto, em termos de portabilidade, o que é necessário exatamente?
Meu entendimento é que o Linux é um software escrito em C. Portanto, ao portar o Linux originalmente do X86 para o ARM ou outros, por exemplo, não é apenas uma questão de recompilar o código com o compilador para a arquitetura de destino específica?
Pondo de lado os drivers de dispositivo para diferentes periféricos, o que mais seria necessário ao transportar o Linux para uma nova arquitetura. O compilador não cuida de tudo para nós?
Respostas:
Embora a maior parte do código no kernel do Linux seja escrita em C, ainda existem muitas partes desse código que são muito específicas da plataforma em que está sendo executada e precisam dar conta disso.
Um exemplo específico disso é a memória virtual, que funciona de maneira semelhante na maioria das arquiteturas (hierarquia de tabelas de páginas), mas possui detalhes específicos para cada arquitetura (como o número de níveis em cada arquitetura, e isso tem aumentado mesmo no x86 com introdução de novos chips maiores.) O código do kernel do Linux introduz macros para lidar com a travessia dessas hierarquias que podem ser elididas pelo compilador em arquiteturas que possuem menos níveis de tabelas de páginas (para que o código seja escrito em C, mas inclua detalhes da arquitetura em consideração.)
Muitas outras áreas são muito específicas para cada arquitetura e precisam ser tratadas com código específico do arco. A maioria deles envolve código em linguagem assembly. Exemplos são:
Troca de Contexto : A troca de contexto envolve salvar o valor de todos os registros para o processo que está sendo desativado e restaurar os registros do conjunto salvo do processo agendado na CPU. Até o número e o conjunto de registros são muito específicos para cada arquitetura. Esse código geralmente é implementado em assembly, para permitir acesso total aos registros e também para garantir que ele seja executado o mais rápido possível, pois o desempenho da alternância de contexto pode ser crítico para o sistema.
Chamadas do sistema : o mecanismo pelo qual o código do espaço do usuário pode acionar uma chamada do sistema geralmente é específico da arquitetura (e algumas vezes até do modelo de CPU específico, por exemplo, Intel e AMD introduziram instruções diferentes para isso, as CPUs mais antigas podem não ter essas instruções, portanto, detalhes para aqueles que ainda serão únicos.)
Manipuladores de interrupção : os detalhes de como lidar com interrupções (interrupções de hardware) geralmente são específicos da plataforma e geralmente requerem uma cola no nível do assembly para lidar com as convenções de chamada específicas em uso na plataforma. Além disso, as primitivas para ativar / desativar as interrupções geralmente são específicas da plataforma e também exigem código de montagem.
Inicialização : Os detalhes de como a inicialização deve ocorrer também geralmente incluem detalhes específicos da plataforma e geralmente exigem algum código de montagem para lidar com o ponto de entrada no kernel. Nas plataformas que possuem várias CPUs (SMP), os detalhes sobre como colocar outras CPUs online também costumam ser específicos da plataforma.
Primitivas de bloqueio : a implementação de primitivas de bloqueio (como spinlocks) geralmente também envolve detalhes específicos da plataforma, pois algumas arquiteturas fornecem (ou preferem) instruções diferentes da CPU para implementá-las com eficiência. Alguns implementarão operações atômicas, outros fornecerão um cmpxchg que pode testar / atualizar atomicamente (mas falhará se outro escritor entrar primeiro), outros incluirão um modificador de "bloqueio" nas instruções da CPU. Isso geralmente envolve escrever código de montagem também.
Provavelmente, existem outras áreas em que o código específico da plataforma ou arquitetura é necessário em um kernel (ou, especificamente, no kernel Linux). Observando a árvore de fontes do kernel, existem subárvores específicas da arquitetura, abaixo
arch/
e abaixo, nasinclude/arch/
quais você pode encontrar mais exemplos disso.Algumas são realmente surpreendentes, por exemplo, você verá que o número de chamadas de sistema disponíveis em cada arquitetura é distinto e que algumas chamadas de sistema existirão em algumas arquiteturas e não em outras. (Mesmo no x86, a lista de syscalls difere entre um kernel de 32 e 64 bits.)
Em resumo, há muitos casos em que um kernel precisa estar ciente de que é específico para uma plataforma. O kernel do Linux tenta abstrair a maioria deles, para que algoritmos de nível superior (como o gerenciamento e agendamento de memória funcionem) possam ser implementados em C e funcionem da mesma maneira (ou principalmente da mesma forma) em todas as arquiteturas.
fonte
Além de portar o kernel do Linux, você precisará definir a ABI ( interface binária do aplicativo ) para programas de "espaço do usuário" e portar as camadas mais baixas da pilha de software do espaço do usuário. O Linux é normalmente usado com componentes de espaço de usuário de baixo nível do projeto GNU, dos quais os mais críticos são:
Muitos outros softwares possuem componentes dependentes da plataforma opcionais; por exemplo, a navegação na Web será substancialmente mais rápida se você escrever primitivas criptográficas otimizadas para mão para NSS e OpenSSL para sua nova arquitetura de CPU e back-ends de compilação just-in-time para IonMonkey e V8 . Mas isso não é essencial para criar uma nova plataforma.
fonte
Você precisa informar o kernel sobre o hardware para o qual está portando. O trabalho do kernel é interagir diretamente com o hardware, para que ele funcione corretamente, o kernel precisa saber sobre a CPU, osciladores (relógios) e quaisquer periféricos, como os vários tipos de portas seriais (SPI, CAN, I2C, etc.).
Antigamente, você fazia isso escrevendo código específico da plataforma que os drivers usariam para funcionar. Hoje em dia, isso é feito escrevendo uma definição da Árvore de Dispositivos .
fonte