Excepto para el último capítulo, todo lo que hemos hecho hasta ahora en el núcleo ha sido como respuesta a un proceso que lo pide, ya sea tratando con un fichero especial, enviando un ioctl(), o a través de una llamada al sistema. Pero el trabajo del núcleo no es sólo responder a las peticiones de los procesos. Otro trabajo no menos importante es hablar con el hardware conectado a la máquina.
Hay dos tipos de interacción entre la CPU y el resto del hardware de la computadora. El primer tipo es cuando la CPU da órdenes al hardware, el el otro es cuando el hardware necesita decirle algo a la CPU. La segunda, llamada interrupción, es mucho más difícil de implementar porque hay que tratar con ella cuando le conviene al hardware, no a la CPU. Los dispositivos hardware típicamente tienen una pequeña cantidad de RAM, y si no lees su información cuando está disponible, se pierde.
Bajo Linux, las interrupciones hardware se llaman IRQs (abreviatura de Interrupt Requests)11.1. Hay dos tipos de IRQs, cortas y largas. Una IRQ corta es la que se espera que dure un periodo de tiempo muy corto, durante el cual el resto de la máquina estará bloqueado y ninguna otra interrupción será manejada. Una IRQ larga es una que puede durar más tiempo, y durante la cual otras interrupciones pueden ocurrir (pero no interrupciones que vengan del mismo dispositivo). Si es posible, siempre es mejor declarar un manejador de interrupciones como largo.
Cuando la CPU recibe una interrupción, detiene lo que quiera que esté haciendo (a menos que se encuentre procesando una interrupción más prioritaria, en cuyo caso tratará con esta interrupción sólo cuando la más prioritaria se haya acabado), salva ciertos parámetros en la pila y llama al manejador de interrupciones. Esto significa que ciertas cosas no se permiten dentro del propio manejador de interrupciones, porque el sistema se encuentra en un estado desconocido. La solución a este problema es que el manejador de interrupciones haga lo que necesite hacer inmediatamente, normalmente leer algo desde el hardware o enviar algo al hardware, y después planificar el manejo de la nueva información en un tiempo posterior (esto se llama `bottom half') y retorna. El núcleo está garantizado que llamará al bottom half tan pronto como sea posible; y cuando lo haga, todo lo que está permitido en los módulos del núcleo estará permitido.
La forma de implementar esto es llamar a request_irq() para que se llame a tu manejador de interrupciones cuando se reciba la IRQ relevante (hay 15 de ellas, más una que se utiliza para disponer en cascada los controladores de interrupción, en las plataformas Intel). Esta función recibe el número de IRQ, el nombre de la función, banderas, un nombre para /proc/interrupts y un parámetro para pasarle al manejador de interrupciones. Las banderas pueden incluir SA_SHIRQ para indicar que estás permitiendo compartir la IRQ con otro manejador de interrupciones (normalmente porque un número de dispositivos hardware están en la misma IRQ) y SA_INTERRUPT para indicar que esta es una interrupción rápida. Esta función sólo tendrá éxito si no hay ya un manejador para esta IRQ, o si ya la estais compartiendo.
Entonces, desde dentro del manejador de interrupciones, nos comunicamos con el hardware y después usamos queue_task_irq() con tq_immediate() y mark_bh(BH_IMMEDIATE) para planificar el bottom half. El motivo por el que no podemos usar la queue_task estándar en la versión 2.0 es que la interrupción podría producirse en el medio de la queue_task de alguien 11.2. Necesitamos mark_bh porque las versiones anteriores de Linux sólo tenían un array de 32 bottom half's, y ahora uno de ellos (BH_IMMEDIATE) se usa para la lista enlazada de bottom half's para los controladores que no tenían una entrada de bottom half asignada.