How Linux Kernel Handles Hardware Interrupts
This article provides an overview of how the Linux operating system kernel processes hardware interrupts. When external hardware components need the processor's attention, they issue an Interrupt Request (IRQ). Linux manages these asynchronous events through a structured two-half architecture designed to maintain system responsiveness, prevent data loss, and efficiently transition execution context between user space and kernel space.
1. The Hardware Trigger and Signal Routing
The interrupt process begins at the physical hardware layer:
- Hardware Assertion: A device (such as a network card, keyboard, or disk controller) asserts an electrical signal on an Interrupt Request (IRQ) line.
- Interrupt Controller: The signal reaches an interrupt controller—such as an Advanced Programmable Interrupt Controller (APIC) on x86 or a Generic Interrupt Controller (GIC) on ARM. The controller prioritizes the interrupt and translates the physical pin or message into an interrupt vector number.
- Signaling the CPU: The controller sends the vector number to the CPU core and raises an interrupt line on the processor.
2. CPU Context Switch and Vector Lookup
Upon detecting the interrupt line signal, the CPU interrupts its current execution flow:
- State Preservation: The CPU pauses the currently executing thread, pushes essential state information (such as the instruction pointer, flags, and general-purpose registers) onto the stack, and switches from user mode to kernel mode if necessary.
- Vector Table Lookup: The CPU uses the interrupt vector to index into the Interrupt Descriptor Table (IDT) or architecture-specific exception vector table.
- Dispatching the Handler: The vector entry points to
low-level assembly code in the kernel, which saves the remaining
register state and invokes
do_IRQ(), the Linux kernel’s primary interrupt handling dispatcher.
3. The Two-Half Interrupt Model
Because hardware interrupts pause normal system execution and frequently disable further interrupts on the executing CPU, Linux splits interrupt handling into two stages: the Top Half and the Bottom Half.
The Top Half (Hard IRQ)
The top half is the Interrupt Service Routine (ISR) registered by the
device driver using functions like request_irq().
- Characteristics: Runs immediately, executes with
local interrupts disabled (or masked), and cannot sleep or perform
operations that block (such as allocating memory with
GFP_KERNELor waiting for I/O). - Responsibilities:
- Acknowledges the interrupt to the hardware device to stop the interrupt line from asserting continuously.
- Copies critical, volatile data into a memory buffer (e.g., reading data out of hardware registers into RAM).
- Schedules the deferred processing (the bottom half).
- Exits quickly to restore system responsiveness and re-enable interrupts.
The Bottom Half (Deferred Execution)
The bottom half handles the time-consuming and computationally intensive processing associated with the event, such as protocol decoding for network packets or filesystem parsing for storage blocks. Linux provides multiple mechanisms for bottom-half processing:
- Softirqs: Statically allocated, high-priority
routines that can run concurrently across multiple CPUs. Softirqs handle
performance-critical tasks like network packet processing
(
NET_RX_SOFTIRQ) and block layer processing. - Tasklets: Built on top of softirqs, tasklets are dynamically allocatable. Unlike softirqs, two instances of the same tasklet cannot run concurrently on different CPUs, simplifying driver concurrency management.
- Workqueues: Workqueues run in kernel thread context. Because they run within normal process contexts (handled by kernel worker threads), they are allowed to sleep, acquire blocking locks, and perform memory allocation.
- Threaded IRQs: A modern Linux mechanism where the driver splits the IRQ into a tiny primary handler (top half) and a dedicated kernel thread that runs the rest of the handler. This allows the deferred work to be scheduled and prioritized by the kernel’s standard process scheduler.
4. Returning from the Interrupt
Once the top-half handler finishes and bottom-half work is scheduled:
- The kernel checks whether any high-priority bottom halves (such as
softirqs) need immediate execution before leaving the interrupt context.
If the system is under heavy interrupt load, the kernel defers remaining
softirq processing to the
ksoftirqdkernel thread to prevent starving user-space processes. - The kernel checks if the interrupted process needs to be rescheduled (e.g., if the interrupt woke up a higher-priority process).
- The CPU state (registers, stack pointer, program counter) is restored.
- An architecture-specific instruction (such as
ireton x86) executes, returning the processor to the exact state and privilege level it occupied before the interrupt occurred.