Manejador de interrupciones
En la programación de sistemas informáticos, un controlador de interrupciones, también conocido como rutina de servicio de interrupción o ISR, es un bloque especial de código asociado con una condición de interrupción específica. Los controladores de interrupciones se inician mediante interrupciones de hardware, instrucciones de interrupción de software o excepciones de software, y se utilizan para implementar controladores de dispositivos o transiciones entre modos de operación protegidos, como llamadas al sistema.
La forma tradicional de controlador de interrupciones es el controlador de interrupciones de hardware. Las interrupciones de hardware surgen de condiciones eléctricas o protocolos de bajo nivel implementados en lógica digital, generalmente se envían a través de una tabla codificada de vectores de interrupción, de forma asincrónica con el flujo de ejecución normal (según lo permitan los niveles de enmascaramiento de interrupciones), a menudo usando una pila separada, y entrando automáticamente en un contexto de ejecución diferente (nivel de privilegio) durante la ejecución del controlador de interrupciones. En general, las interrupciones de hardware y sus controladores se utilizan para manejar condiciones de alta prioridad que requieren la interrupción del código actual que está ejecutando el procesador.
Más tarde se consideró conveniente que el software pudiera activar el mismo mecanismo mediante una interrupción de software (una forma de interrupción sincrónica). En lugar de utilizar una tabla de distribución de interrupciones codificada a nivel de hardware, las interrupciones de software a menudo se implementan a nivel del sistema operativo como una forma de función de devolución de llamada.
Los manejadores de interrupciones tienen una multitud de funciones, que varían según lo que desencadenó la interrupción y la velocidad a la que el manejador de interrupciones completa su tarea. Por ejemplo, presionar una tecla en el teclado de una computadora o mover el mouse desencadena interrupciones que llaman a controladores de interrupciones que leen la tecla o la posición del mouse y copian la información asociada en la memoria de la computadora.
Un controlador de interrupciones es una contraparte de bajo nivel de los controladores de eventos. Sin embargo, los manejadores de interrupciones tienen un contexto de ejecución inusual, muchas restricciones severas en tiempo y espacio, y su naturaleza intrínsecamente asincrónica los hace notoriamente difíciles de depurar mediante la práctica estándar (los casos de prueba reproducibles generalmente no existen), por lo que exigen un conjunto de habilidades especializadas. (un subconjunto importante de la programación de sistemas) de ingenieros de software que trabajan en la capa de interrupción del hardware.
Indicadores de interrupción
A diferencia de otros controladores de eventos, se espera que los controladores de interrupciones establezcan indicadores de interrupción en los valores apropiados como parte de su funcionalidad principal.
Incluso en una CPU que admite interrupciones anidadas, a menudo se llega a un controlador con todas las interrupciones enmascaradas globalmente por una operación de hardware de la CPU. En esta arquitectura, un controlador de interrupciones normalmente guardaría la menor cantidad de contexto necesaria y luego restablecería el indicador de desactivación de interrupciones global en la primera oportunidad, para permitir interrupciones de mayor prioridad para interrumpir el controlador actual. También es importante que el controlador de interrupciones sofoque la fuente de interrupción actual mediante algún método (a menudo alternando un bit de bandera de algún tipo en un registro periférico) para que la interrupción actual no se repita inmediatamente al salir del controlador, lo que resulta en una Bucle infinito.
Salir de un controlador de interrupciones con el sistema de interrupciones exactamente en el estado correcto ante cada eventualidad puede ser a veces una tarea ardua y exigente, y su mal manejo es la fuente de muchos errores graves, del tipo que detiene el sistema por completo. Estos errores a veces son intermitentes, y el caso extremo mal manejado no ocurre durante semanas o meses de operación continua. La validación formal de los manejadores de interrupciones es tremendamente difícil, mientras que las pruebas generalmente identifican solo los modos de falla más frecuentes, por lo que los errores sutiles e intermitentes en los manejadores de interrupciones a menudo llegan a los clientes finales.
Contexto de ejecución
En un sistema operativo moderno, al ingresar, el contexto de ejecución de un controlador de interrupciones de hardware es sutil.
Por motivos de rendimiento, el controlador normalmente se iniciará en la memoria y el contexto de ejecución del proceso en ejecución, con el que no tiene una conexión especial (la interrupción esencialmente usurpa el contexto en ejecución; la contabilidad del tiempo del proceso a menudo acumulará el tiempo invertido). manejo de interrupciones del proceso interrumpido). Sin embargo, a diferencia del proceso interrumpido, la interrupción generalmente se eleva mediante un mecanismo de CPU codificado a un nivel de privilegio lo suficientemente alto como para acceder directamente a los recursos de hardware.
Consideraciones sobre el espacio de la pila
En un microcontrolador de bajo nivel, el chip puede carecer de modos de protección y no tener una unidad de administración de memoria (MMU). En estos chips, el contexto de ejecución de un controlador de interrupciones será esencialmente el mismo que el del programa interrumpido, que normalmente se ejecuta en una pequeña pila de tamaño fijo (los recursos de memoria tradicionalmente han sido extremadamente escasos en el extremo inferior). A menudo se proporcionan interrupciones anidadas, lo que exacerba el uso de la pila. Una restricción principal sobre el manejador de interrupciones en este esfuerzo de programación es no exceder la pila disponible en el peor de los casos, lo que requiere que el programador razone globalmente sobre el requisito de espacio de pila de cada manejador de interrupciones y tarea de aplicación implementados.
Cuando se excede el espacio de pila asignado (una condición conocida como desbordamiento de pila), los chips de esta clase normalmente no lo detectan en el hardware. Si la pila se excede en otra área de memoria de escritura, el controlador normalmente funcionará como se esperaba, pero la aplicación fallará más tarde (a veces mucho más tarde) debido al efecto secundario de corrupción de la memoria del controlador. Si la pila se excede en un área de memoria no grabable (o protegida), la falla generalmente ocurrirá dentro del propio controlador (generalmente el caso más fácil de depurar más adelante).
En el caso de escritura, se puede implementar una protección de pila centinela: un valor fijo justo más allá del final de la pila legal cuyo valor puede ser sobrescrito, pero nunca lo será si el sistema funciona correctamente. Es común observar periódicamente daños en la protección de la pila con algún tipo de mecanismo de vigilancia. Esto detectará la mayoría de las condiciones de desbordamiento de pila en un momento cercano a la operación infractora.
En un sistema multitarea, cada hilo de ejecución normalmente tendrá su propia pila. Si no se proporciona una pila de sistema especial para las interrupciones, las interrupciones consumirán espacio de pila de cualquier hilo de ejecución que se interrumpa. Estos diseños generalmente contienen una MMU, y las pilas de usuario generalmente están configuradas de manera que la MMU atrapa el desbordamiento de la pila, ya sea como un error del sistema (para depuración) o para reasignar la memoria para ampliar el espacio disponible. Los recursos de memoria en este nivel de microcontrolador suelen estar mucho menos restringidos, por lo que las pilas se pueden asignar con un generoso margen de seguridad.
En sistemas que soportan un alto número de subprocesos, es mejor si el mecanismo de interrupción de hardware cambia la pila a una pila especial del sistema, de modo que ninguna de las pilas de subprocesos tenga que tener en cuenta el uso de interrupciones anidadas en el peor de los casos. Pequeñas CPU que se remontan al Motorola 6809 de 8 bits de 1978 han proporcionado punteros de pila de usuario y de sistema separados.
Restricciones de tiempo y concurrencia
Por muchas razones, es muy deseable que el controlador de interrupciones se ejecute lo más brevemente posible, y se desaconseja (o prohíbe) que una interrupción de hardware invoque llamadas al sistema potencialmente bloqueantes. En un sistema con múltiples núcleos de ejecución, las consideraciones de reentrada también son primordiales. Si el sistema proporciona DMA de hardware, pueden surgir problemas de simultaneidad incluso con un solo núcleo de CPU. (No es raro que un microcontrolador de nivel medio carezca de niveles de protección y una MMU, pero aun así proporcione un motor DMA con muchos canales; en este escenario, muchas interrupciones normalmente son activadas por el propio motor DMA., y se espera que el controlador de interrupciones asociado actúe con cuidado).
Ha evolucionado una práctica moderna para dividir los manejadores de interrupciones de hardware en elementos de la mitad frontal y la mitad posterior. La mitad frontal (o primer nivel) recibe la interrupción inicial en el contexto del proceso en ejecución, hace el trabajo mínimo para restaurar el hardware a una condición menos urgente (como vaciar un búfer de recepción lleno) y luego marca la mitad posterior. (o segundo nivel) para su ejecución en un futuro próximo con la prioridad de programación adecuada; una vez invocada, la mitad posterior opera en su propio contexto de proceso con menos restricciones y completa la operación lógica del controlador (como transmitir los datos recién recibidos a una cola de datos del sistema operativo).
Manejadores divididos en sistemas operativos modernos
En varios sistemas operativos—Linux, Unix, macOS, Microsoft Windows, z/OS, DESQview y algunos otros sistemas operativos utilizados en el pasado—los controladores de interrupciones se dividen en dos partes: el controlador de interrupciones de primer nivel (FLIH) y los Manejadores de interrupciones de segundo nivel (SLIH). Los FLIH también se conocen como controladores de interrupciones duras o controladores de interrupciones rápidas, y los SLIH también se conocen como controladores de interrupciones lentas/suaves o llamadas a procedimientos diferidos. en Windows.
Un FLIH implementa como mínimo un manejo de interrupciones específico de la plataforma similar a las rutinas de interrupción. En respuesta a una interrupción, hay un cambio de contexto y el código de la interrupción se carga y ejecuta. El trabajo de un FLIH es atender rápidamente la interrupción o registrar información crítica específica de la plataforma que solo está disponible en el momento de la interrupción y programar la ejecución de un SLIH para un manejo adicional de interrupciones de larga duración.
Los FLIH provocan fluctuaciones en la ejecución del proceso. Los FLIH también enmascaran las interrupciones. Reducir la fluctuación es más importante para los sistemas operativos en tiempo real, ya que deben mantener una garantía de que la ejecución de un código específico se completará dentro de un período de tiempo acordado. Para reducir la fluctuación y la posibilidad de perder datos debido a interrupciones enmascaradas, los programadores intentan minimizar el tiempo de ejecución de un FLIH, moviéndose tanto como sea posible al SLIH. Con la velocidad de las computadoras modernas, los FLIH pueden implementar todo el manejo dependiente de la plataforma y el dispositivo, y usar un SLIH para un manejo de larga duración independiente de la plataforma.
Los FLIH que dan servicio a hardware generalmente enmascaran su interrupción asociada (o la mantienen enmascarada según sea el caso) hasta que completan su ejecución. Un FLIH (inusual) que desenmascara su interrupción asociada antes de que se complete se denomina controlador de interrupciones reentrantes. Los manejadores de interrupciones reentrantes pueden provocar un desbordamiento de la pila debido a múltiples apropiaciones por parte del mismo vector de interrupción, por lo que generalmente se evitan. En un sistema de interrupción prioritaria, el FLIH también enmascara (brevemente) otras interrupciones de igual o menor prioridad.
Un SLIH completa tareas de procesamiento de interrupciones largas de manera similar a un proceso. Los SLIH tienen un subproceso del kernel dedicado para cada controlador o son ejecutados por un grupo de subprocesos de trabajo del kernel. Estos subprocesos se encuentran en una cola de ejecución en el sistema operativo hasta que haya tiempo de procesador disponible para realizar el procesamiento de la interrupción. Los SLIH pueden tener un tiempo de ejecución prolongado y, por lo tanto, normalmente se programan de manera similar a los subprocesos y procesos.
En Linux, los FLIH se denominan mitad superior y los SLIH se denominan mitad inferior o mitad inferior. Esto es diferente de los nombres utilizados en otros sistemas similares a Unix, donde ambos son parte de la mitad inferior.
Contenido relacionado
Tarjeta perforada
CPython
Arquitectura Harvard