¿Cómo encuentra el procesador el código del núcleo después de una interrupción?


13

Cuando ocurre una interrupción, el procesador se adelanta al proceso actual y llama al código del núcleo para manejar la interrupción. ¿Cómo sabe el procesador dónde ingresar el núcleo?

Entiendo que hay controladores de interrupciones que se pueden instalar para cada línea de interrupción. Pero dado que el procesador solo ejecuta 'lógica cableada', tiene que existir un lugar predefinido que apunte a un controlador de interrupción en sí mismo o algún código que se ejecute antes del controlador (ya que puede haber múltiples controladores para una línea de interrupción, supongo que el último).

Respuestas:


13

Al inicio, el núcleo inicializará una tabla de vectores de interrupción (llamada tabla de descriptores de interrupciones o IDT en x86) que apunta a un controlador de interrupciones para cada línea.

Antes del 80286, el IDT siempre se almacenaba en una dirección fija; comenzando con el 80286, el IDT se carga utilizando las LIDTinstrucciones.

Las tablas de vectores de interrupción apuntan a un único controlador por línea de interrupción; Dicho esto, un núcleo podría elegir, por ejemplo, proporcionar un controlador de interrupciones que ejecute varias otras rutinas de interrupciones, o proporcionar un único controlador que cubra algunas o todas las interrupciones. Linux hace estas cosas al proporcionar un controlador de interrupción genérico que determina qué línea de interrupción se llamó y encuentra el controlador apropiado apropiado para llamar.


1
entonces el procesador usa la línea de interrupción como índice para el IDT, coloca la entrada en la PC y comienza a ejecutar? pero ¿no hay una función genérica que se ejecute antes de todos los manejadores de interrupciones? para Linux sería do_IRQ (). ¿Es esta la función a la que apunta cada entrada IDT, sin importar la línea de interrupción?
Philipp Murry

@PhilippMurry sí. El núcleo luego usa su propio conjunto de manejadores de interrupciones (de los cuales puede haber más de uno por línea) para manejar realmente la interrupción, antes de regresar al código que se estaba ejecutando previamente.
Adam Maras

De acuerdo, hay dos tipos de controladores de interrupciones: los que llama el procesador (siempre do_IRQ ()) y los que llama el núcleo (el que he registrado a través de request_irq ()). ¿podrías agregar esto a tu respuesta? creo que lo aceptaré :) muchas gracias
Philipp Murry

1
@PhilippMurry No soy un experto en cómo Linux maneja las interrupciones (y cómo los desarrolladores de kernel aprovechan ese sistema) pero agregué más información en un sentido más amplio sobre cómo los kernels pueden tener su propia administración de ISR.
Adam Maras

12

Sí, hay un lugar predefinido que contiene la dirección de código para saltar: un vector de interrupción . Dependiendo del procesador, esta puede ser una ubicación específica en la memoria física (8088), una ubicación específica en la memoria virtual, un registro del procesador, una ubicación en la memoria indicada por un registro (ARM, 386), ...

Los detalles varían en los diferentes procesadores, pero los principales elementos comunes para manejar una interrupción en el procesador son:

  • Máscaras de interrupciones (para que cualquier interrupción posterior tenga que esperar).
  • Establezca el modo de procesador en kernel o en modo de interrupción (si el procesador tiene tales modos).
  • Guarde el valor del contador del programa en un lugar conocido (registro o memoria).
  • Posiblemente guarde el valor de otros registros, o cambie entre bancos de registros).
  • Ejecute la siguiente instrucción (en el nuevo valor de PC).

1

Las otras dos respuestas (al momento de escribir) hablan sobre las interrupciones y el IDT. Esto es correcto, sin embargo, en una CPU Intel-esque moderna, no hay menos de tres formas de llamar a un núcleo.

Método # 1: interrupciones.

Esto se explica arriba. Configura una entrada en la tabla de descriptores de interrupciones / vector de interrupciones, y luego ejecuta una interrupción de software para ingresar al núcleo.

La principal ventaja de este método es que un núcleo típico debe ser capaz de manejar las interrupciones de todos modos, y funciona en hardware arcaico.

Método # 2: Llamar puertas.

Una puerta de llamada es un tipo especial de selector de segmento. El objetivo de la llamada debe cargarse en la tabla de descriptores de segmento global o local (GDT y LDT respectivamente). Si luego realiza una instrucción de llamada lejana utilizando la puerta de llamada como segmento (se ignora el desplazamiento de la llamada), esto le permite llamar a un código más privilegiado. Las puertas de llamada son extremadamente flexibles; La arquitectura IA-32 tiene cuatro niveles de privilegio, y las puertas de llamada le permiten llamar a cualquier nivel.

No creo que Linux alguna vez haya usado puertas de llamada, pero Windows 95 sí. Los servicios del kernel Win95 ( krnl386.exey kernel.dll) realmente se ejecutaron en modo de usuario (anillo 3). El nivel de privilegio más alto (anillo 0) solo se usó para controladores y un microkernel que realizó solo el cambio de proceso. La llamada a los conductores se realizó mediante puertas de llamada. Esto permitió que el código heredado de 16 bits (¡de los cuales había mucho!) Usara los controladores Win95 simplemente usando una llamada remota estándar, como siempre lo hacían.

La protección inadecuada de la tabla de descriptores globales fue la causa de varias vulnerabilidades de Windows 95, que lograron instalar sus propias puertas de llamadas escribiendo sobre la memoria.

Método # 3: SYSCALL / SYSRET y SYSENTER / SYSEXIT

Estos son dos conjuntos de instrucciones, inventados independientemente por AMD e Intel, pero esencialmente hacen lo mismo. SYSCALL / SYSRET fue lo primero y fue solo para AMD, SYSENTER / SYSEXIT fue Intel, pero AMD lo implementa ahora. Así que voy a describir SYSENTER / SYSEXIT.

A diferencia de las puertas de llamada, SYSENTER solo se puede usar para transferir al anillo 0, y solo se puede transferir a una ubicación. Sin embargo, tiene la ventaja de tener una latencia extremadamente baja porque, a diferencia de una llamada o interrupción, no toca la pila.

La ubicación de transferencia se configura utilizando tres registros específicos del modelo: uno para la información del segmento y uno para el puntero de instrucción y el puntero de pila del código del núcleo. Como no se "empuja" nada en la pila, el código de modo de usuario es responsable de decirle al núcleo a dónde regresar pasando el puntero de instrucción de retorno y el puntero de pila en los registros. El núcleo es responsable de restaurar el puntero de la pila, y la instrucción SYSEXIT restaura el puntero de la instrucción.

Más información sobre las instrucciones SYSENTER y SYSEXIT.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.