¿Cómo determina el núcleo el ISA del sistema en el que se está ejecutando?

Un kernel del sistema operativo no “determina” la arquitectura del conjunto de instrucciones (ISA) de la computadora en la que se ejecuta; el kernel debe ser dirigido (compilado / ensamblado) para el ISA en primer lugar, antes de cargarlo en la RAM de la computadora . De lo contrario, no se ejecutará (ejecutará) con éxito. No se puede cargar un núcleo Unix compilado para PowerPC o SPARC ISA en una computadora x86, y saltar a él: los códigos de instrucciones de la máquina son totalmente incompatibles y el resultado sería un bloqueo.

En el caso de distribuciones de Unix de arquitectura múltiple como NetBSD, todo el sistema base se compila individualmente para cada familia ISA / CPU que admitimos en nuestras distribuciones de software binario (consulte Plataformas compatibles con NetBSD); el código de arranque tiende a ser muy específico del sistema, ya que debemos trabajar con cualquier firmware que el fabricante de la computadora haya puesto en su sistema para obtener nuestro NetBSD (generalmente “no nativo”, es decir, no lo que el fabricante del sistema pretendía como sistema operativo para sus sistemas) Kernel del sistema operativo cargado y en ejecución.

Para los sistemas operativos que admiten directamente múltiples arquitecturas sin distribuciones binarias específicas de la máquina, la determinación de qué núcleo arrancar sería el trabajo de un programa de arranque secundario: elegir entre los núcleos del sistema operativo implementados en múltiples arquitecturas de conjuntos de instrucciones (ISA) mutuamente incompatibles después de examinar la ejecución medio ambiente. Las primeras versiones de macOS (sistema operativo) se ejecutaban tanto en PowerPC como en Intel x86 y el sistema instalado incluía código para ambos.

Sin embargo, existen variaciones ISA incluso dentro de las familias de CPU, por ejemplo, versiones posteriores de CPU que implementan instrucciones nuevas o adicionales, que un núcleo podría necesitar atrapar y emular para CPU más antiguas. Este tipo de determinaciones generalmente se tienen en cuenta durante la inicialización del núcleo del sistema cuando el núcleo sondea o examina el hardware para ver qué hay allí y carga rutinas específicas de la CPU según sea necesario.

Consulte también mi respuesta a ¿Son los gestores de arranque como GRUB y LILO específicos del hardware? En otras palabras, ¿necesitan saber el ISA del sistema en el que se encuentra?

Hay una excepción en la era moderna: las matrices de puertas programables de campo (FPGA) que son efectivamente hardware reconfigurable sobre la marcha. Necesariamente, deben programarse antes de su uso, lo que se puede hacer de forma “permanente” (para un FPGA no volátil) o cada vez que se necesita cambiar el contenido del FPGA, casi como RAM. Dependiendo de cuántas puertas programables tenga el FPGA, qué núcleos preconfigurados se incluyen en un chip FPGA, cuántos pines de E / S tiene el chip (o chips) y a qué están conectados, un FPGA en principio se puede programar ser cualquier tipo de computadora con cualquier ISA arbitraria que mejor se adapte a la aplicación, por ejemplo, ejecutar un cálculo en el número mínimo de instrucciones que cada uno hace exactamente lo que uno necesita sin desperdicio.

Necesariamente, si se va a programar un FPGA (o una serie de ellos), se prefiere tener un microprocesador con software para controlar todo el proceso y administrar los FPGA bajo control de software, lo que significa, desde el inicio, volver a la situación como se describe en la parte superior de esta respuesta.

Cuando compila el código fuente del núcleo, debe especificar al compilador la arquitectura de destino como x86 y eventualmente la subfamilia (como 386, 486, …), por lo tanto, sabrá qué conjunto de instrucciones se debe usar para generar La imagen del núcleo.

Además, varias arquitecturas proporcionan una manera conveniente de detectar las características de la CPU y los conjuntos de subinstrucciones disponibles, mientras se ejecuta un programa.
Por ejemplo, en la arquitectura x86 puede usar la instrucción CPUID (disponible en el 486 y sus sucesores) para detectar si las tecnologías (y las instrucciones relacionadas con ellas) como MMX, SSE, SSE2, VT-x, … están disponibles.
El núcleo puede decidir, mientras se ejecuta, si se puede cargar y ejecutar un código específico de arquitectura (como un controlador).

Cuando se compila un núcleo a partir del código fuente, se proporcionan la arquitectura de la CPU y otros atributos del procesador. Por lo tanto, la construcción final de la imagen del núcleo es específica del HW.

Como Ajay ha dicho, la determinación se realiza en tiempo de compilación. Esto es cierto al menos en la línea principal del código de inicialización. Puede ser posible que la inicialización del núcleo esté en algún subconjunto de CPU que admita un conjunto de instrucciones y que se vincule dinámicamente a extensiones específicas del conjunto de instrucciones para algunos módulos o rutas de código. (Por ejemplo, los núcleos x86_64 todavía se inician en modo x86 (32 bits) (también conocido como IA32), hasta donde sé, el proceso de inicio muy temprano todavía está en modo 8086 (16 bits) dentro de esa arquitectura de 32 bits.

Obviamente, Linux soporta varias arquitecturas de CPU diferentes. No sé mucho sobre los detalles de bajo nivel para la mayoría de ellos.

El núcleo se compila específicamente para un ISA particular, como x86, x64, ARMv7, MIPS, etc.