Portar Linux a los requisitos de otra plataforma [cerrado]

28

Sé que Linux está disponible y se ha portado para muchas plataformas diferentes, como X86, ARM, PowerPC, etc.

Sin embargo, en términos de portabilidad, ¿qué se requiere exactamente?

Tengo entendido que Linux es un software escrito en C. Por lo tanto, al portar Linux originalmente desde X86 a ARM u otros, por ejemplo, ¿no se trata solo de volver a compilar el código con el compilador para la arquitectura de destino específica?

Dejando a un lado los controladores de dispositivos para diferentes periféricos, qué más debería hacerse al portar Linux a una nueva arquitectura. ¿El compilador no se encarga de todo por nosotros?

Ingeniero999
fuente
11
¿Podemos suponer que ha examinado las fuentes de kernel específicas de la arquitectura? git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/…
Kusalananda

Respuestas:

57

Aunque la mayor parte del código en el kernel de Linux está escrito en C, todavía hay muchas partes de ese código que son muy específicas de la plataforma donde se está ejecutando y deben dar cuenta de eso.

Un ejemplo particular de esto es la memoria virtual, que funciona de manera similar en la mayoría de las arquitecturas (jerarquía de tablas de páginas) pero tiene detalles específicos para cada arquitectura (como el número de niveles en cada arquitectura, y esto ha aumentado incluso en x86 con introducción de nuevos chips más grandes.) El código del kernel de Linux introduce macros para manejar el recorrido de estas jerarquías que el compilador puede eludir en arquitecturas que tienen menos niveles de tablas de páginas (de modo que el código se escribe en C, pero toma detalles de la arquitectura en consideración.)

Muchas otras áreas son muy específicas para cada arquitectura y deben manejarse con un código específico de arco. Sin embargo, la mayoría de estos implican código en lenguaje ensamblador. Ejemplos son:

  • Cambio de contexto : el cambio de contexto implica guardar el valor de todos los registros para el proceso que se está desconectando y restaurar los registros del conjunto guardado del proceso programado en la CPU. Incluso el número y el conjunto de registros es muy específico para cada arquitectura. Este código generalmente se implementa en ensamblado, para permitir el acceso total a los registros y también para asegurarse de que se ejecute lo más rápido posible, ya que el rendimiento de la conmutación de contexto puede ser crítico para el sistema.

  • Llamadas del sistema : el mecanismo por el cual el código de espacio de usuario puede activar una llamada del sistema generalmente es específico de la arquitectura (y, a veces, incluso del modelo de CPU específico, por ejemplo, Intel y AMD introdujeron diferentes instrucciones para eso, las CPU más antiguas pueden carecer de esas instrucciones, por lo que los detalles para aquellos aún serán únicos.)

  • Controladores de interrupciones : los detalles sobre cómo manejar las interrupciones (interrupciones de hardware) generalmente son específicos de la plataforma y generalmente requieren un poco de pegamento de nivel de ensamblaje para manejar las convenciones de llamadas específicas que se usan para la plataforma. Además, las primitivas para habilitar / deshabilitar las interrupciones suelen ser específicas de la plataforma y también requieren un código de ensamblaje.

  • Inicialización : los detalles sobre cómo debe ocurrir la inicialización también suelen incluir detalles específicos de la plataforma y, a menudo, requieren un código de ensamblaje para manejar el punto de entrada al núcleo. En las plataformas que tienen múltiples CPU (SMP), los detalles sobre cómo poner en línea otras CPU también suelen ser específicos de la plataforma.

  • Primitivas de bloqueo : la implementación de primitivas de bloqueo (como los spinlocks) generalmente también implican detalles específicos de la plataforma, ya que algunas arquitecturas proporcionan (o prefieren) diferentes instrucciones de CPU para implementarlas de manera eficiente. Algunos implementarán operaciones atómicas, algunos proporcionarán un cmpxchg que puede probar / actualizar atómicamente (pero falla si otro escritor ingresa primero), otros incluirán un modificador de "bloqueo" para las instrucciones de la CPU. A menudo, esto también implicará escribir código de ensamblaje.

Probablemente hay otras áreas donde se necesita código específico de la plataforma o la arquitectura en un kernel (o, específicamente, en el kernel de Linux). Mirando el árbol de fuentes del kernel, hay subárboles específicos de la arquitectura debajo arch/y debajo include/arch/donde puedes encontrar más ejemplos de esto.

Algunos son realmente sorprendentes, por ejemplo, verá que la cantidad de llamadas al sistema disponibles en cada arquitectura es distinta y algunas llamadas al sistema existirán en algunas arquitecturas y no en otras. (Incluso en x86, la lista de llamadas al sistema difiere entre un núcleo de 32 bits y un de 64 bits).

En resumen, hay muchos casos que un núcleo debe tener en cuenta que son específicos de una plataforma. El kernel de Linux intenta abstraer la mayoría de ellos, por lo que los algoritmos de nivel superior (como el funcionamiento de la gestión de la memoria y la programación) se pueden implementar en C y funcionan de la misma manera (o casi lo mismo) en todas las arquitecturas.

filbranden
fuente
77
Muy buen relato! La variación en el número de syscalls está relacionada principalmente con el historial: los nuevos puertos incluyen los syscalls válidos en el momento del puerto, no se molestan con el equipaje histórico presente en los puertos más antiguos, por lo que los syscalls obsoletos generalmente no están presentes en los puertos más nuevo que la desaprobación. (Eso no cubre todos los escenarios ...)
Stephen Kitt
10

Además de portar el kernel de Linux, deberá definir la interfaz binaria de aplicación (ABI) para los programas de "espacio de usuario" y portar las capas más bajas de la pila de software de espacio de usuario. Linux se usa típicamente con componentes de espacio de usuario de bajo nivel del proyecto GNU, de los cuales los más críticos son:

  • El compilador, ensamblador y enlazador de C: Binutils de GCC y GNU . Para una arquitectura de CPU completamente nueva, debe portar este software incluso antes de comenzar a portar el núcleo, ya que el núcleo es en sí mismo un programa en C y debe compilarse. Si ya hay soporte de "back-end" para la CPU de su plataforma, pero no con Linux como el núcleo del sistema operativo, tiene mucho menos trabajo por hacer y es posible que pueda aplazar la mayor parte del trabajo hasta que el núcleo esté activo y corriendo.
  • La biblioteca de tiempo de ejecución C: " GNU libc ". Esta biblioteca incluye el código que realiza llamadas al sistema e interactúa directamente con el núcleo.
  • La biblioteca de "interfaz de funciones foráneas", libffi , que es un componente esencial de muchos intérpretes de idiomas de alto nivel, y realiza una de las pocas tareas restantes que requieren una pequeña cantidad de lenguaje ensamblador escrito a mano.

Muchas otras piezas de software tienen componentes opcionales dependientes de la plataforma; por ejemplo, la navegación web será sustancialmente más rápida si escribe primitivas criptográficas optimizadas a mano para NSS y OpenSSL para su nueva arquitectura de CPU, y back- end de compilación justo a tiempo para IonMonkey y V8 . Pero estos no son esenciales para abrir una nueva plataforma.

zwol
fuente
1

Tienes que decirle al kernel sobre el hardware al que estás portando. El trabajo del kernel es interactuar directamente con el hardware, por lo que para que funcione correctamente, el kernel necesita saber sobre la CPU, los osciladores (relojes) y cualquier periférico, como los diversos tipos de puertos seriales (SPI, CAN, I2C, etc.).

En los viejos tiempos, hacía esto escribiendo código específico de la plataforma que los controladores usarían para funcionar. En estos días, esto se hace escribiendo una definición de Árbol de dispositivos .

RubberDuck
fuente