Linux Kernel Interrupt Handling Explained (Kernel 6.x Guide)-Best Linux Device Driver Training Online

← Previous Lecture    Next Lecture →

Linux Kernel Interrupt Handling Explained (Kernel 6.x Guide)
A beginner-friendly lecture from the Free Linux Kernel Programming Course by EmbeddedPathashala
Lecture 1 of Device Driver Interrupts Series
Level: Beginner to Intermediate
Reading Time: 12 min

If you are learning Linux device driver development, understanding linux kernel interrupt handling is one of the first real milestones. Every keyboard press, network packet, button push on a GPIO pin, or timer tick on your embedded board reaches the operating system through an interrupt. In this lecture of our free Linux kernel programming course, we break down what a hardware interrupt actually is, how the kernel manages it, and why the rules around interrupt context exist — explained in plain language, with fresh examples built for modern kernels (6.x), not outdated hardware.

What You Will Learn
What a hardware interrupt is IRQ numbers and IRQ lines Interrupt context restrictions Why interrupt handlers must be fast Top half vs bottom half concept Checking active interrupts on your system
Prerequisites

Before this lecture, you should be comfortable with:

Basic C programming Writing a simple loadable kernel module Basic idea of CPU registers and memory-mapped I/O

If you haven’t covered kernel modules yet, please finish that lecture first before continuing here.

What Is a Hardware Interrupt?

A hardware interrupt is an electrical signal sent by a peripheral device to the processor, asking it to stop whatever it is currently doing and immediately attend to the device. Think of it like a phone ringing while you are cooking — you pause chopping vegetables, answer the call, and return to cooking afterward. The CPU does the same thing: it saves its current state, jumps to a piece of kernel code called an interrupt handler, lets that code do its job, and then resumes exactly where it left off.

Every interrupt-capable device is wired (physically or logically, through an interrupt controller) to a specific IRQ line. On modern ARM64 and x86_64 systems, this routing goes through an interrupt controller such as the GIC (Generic Interrupt Controller) on ARM SoCs, or the APIC on x86 platforms, rather than the legacy 8259 PIC found on very old PCs. The kernel abstracts all of this hardware detail away and gives driver authors a single, consistent numeric IRQ that identifies the line.

How a Hardware Interrupt Reaches Your Driver
Peripheral Device
→
Interrupt Controller (GIC / APIC)
→
CPU Core
→
Kernel Generic IRQ Layer
→
Your Driver’s Handler Function

Interrupts Are Equal, But Never Overlap on the Same Core

A common misconception is that some interrupts are more “important” than others in Linux, similar to priority levels in older RTOS designs. On Linux, all hardware interrupts are treated as peers — there is no built-in priority scheme between them. What Linux does guarantee is this: while your handler for IRQ number N is executing on a CPU core, that same IRQ is masked (disabled) globally across all cores, and all other interrupts on that particular core are also temporarily disabled. This is why interrupt handlers must never run long — every microsecond your handler spends inside is a microsecond another device might miss its own interrupt on that core.

The one exception to this masking rule is the NMI (Non-Maskable Interrupt), which is reserved for critical events like hardware failures, watchdog timeouts, or debugging traps, and cannot be blocked even by a running interrupt handler.

Why Interrupt Handlers Must Be Fast

Because an interrupt handler runs with other interrupts on that core disabled, and because it runs in a special restricted execution context called hardirq context, the golden rule of driver development is simple: get in, do the minimum, get out. A commonly used guideline among kernel developers is to keep the actual work inside the top-half handler under roughly 50–100 microseconds. If your device genuinely needs more processing time than that, the correct approach is to defer the heavy work using a bottom-half mechanism, which we will cover in the next lecture.

Allowed in Hardirq Context Not Allowed in Hardirq Context
Reading/writing device registers Calling functions that can sleep (e.g. mutex_lock())
Acknowledging/clearing the interrupt on the device Allocating memory with GFP_KERNEL
Waking up a waiting thread or scheduling a bottom half Performing blocking I/O or long computation loops
Using spinlocks (with the IRQ-safe variants) Copying data to/from user space

Checking Interrupts on a Real Kernel 6.x System

Before writing any driver code, it helps to see interrupts in action on a running Linux system. This works identically on a modern kernel 6.x distribution, whether you’re on an x86_64 laptop or an ARM64 single-board computer:

cat /proc/interrupts

This file lists every active IRQ number, how many times it has fired on each CPU core, and which driver registered it. It’s a genuinely useful debugging habit — if a device seems unresponsive, checking whether its interrupt count is increasing is often the fastest way to tell whether the hardware side is even signaling the CPU.

Top Half and Bottom Half: A Quick Preview

Since a handler cannot do heavy work inside hardirq context, Linux splits interrupt processing into two conceptual stages:

  • Top half — the actual hardware interrupt handler, registered with the kernel, runs immediately and must be extremely quick.
  • Bottom half — deferred work, scheduled by the top half, that runs later in a context where sleeping and blocking are allowed (using mechanisms like threaded IRQs, tasklets, or workqueues).

We dedicate the next lecture entirely to writing an actual interrupt handler function, registering it with the kernel using the modern APIs, and returning the correct status code — so keep this concept in mind as you move forward.

Common Mistakes Beginners Make

  • Assuming interrupts have priority levels like an RTOS — on Linux, they don’t.
  • Writing handlers that call sleeping functions, causing kernel warnings like “scheduling while atomic.”
  • Forgetting to check whether the interrupt actually belongs to their device before doing work (important for shared IRQ lines).
  • Not testing with cat /proc/interrupts before assuming the driver’s handler logic is at fault.

Best Practices

  • Always acknowledge/clear the interrupt on the device as the very first step in your handler.
  • Keep any shared state between your handler and the rest of the driver protected with an IRQ-safe spinlock.
  • Design your driver so buffers and memory are allocated at probe time, not inside the handler.
  • Push anything that can wait — and especially anything that can sleep — into a bottom half.

Performance and Security Considerations

Performance: A slow interrupt handler doesn’t just delay its own device — it can starve other interrupts sharing the same core, increasing system-wide latency. This matters especially on embedded boards with few CPU cores.

Security: Interrupt handlers often deal directly with untrusted external input (e.g. data coming from a USB device or network card). Never trust register values blindly; validate lengths and ranges before using them to index buffers, since a malformed or malicious device could otherwise trigger a kernel-level memory corruption bug.

Summary / Key Takeaways

  • A hardware interrupt lets a device tell the CPU “attend to me now.”
  • Linux treats all interrupts as equal peers — there is no built-in priority system.
  • While an IRQ’s handler runs, that IRQ is masked globally and other IRQs on that core are disabled.
  • Handlers run in hardirq context and must never sleep, block, or run long.
  • Heavy work belongs in a bottom half, covered in the next lecture.

Conclusion

Understanding how linux kernel interrupt handling works at a conceptual level is the foundation for everything else in device driver development — from GPIO buttons to network cards to storage controllers. Once the top-half/bottom-half split and the “keep it fast” rule feel natural, the actual code for registering a handler (coming up next) will make a lot more sense. This lecture is part of EmbeddedPathashala’s free Linux kernel development course, built for learners who want a practical, no-cost path into embedded Linux and device driver programming.

FAQ

Q1. What is the difference between an interrupt and a system call?
A system call is requested deliberately by a user-space program running on the CPU. An interrupt is triggered asynchronously by external hardware and can happen at almost any point in time, regardless of what the CPU was doing.

Q2. Can two interrupts fire on the same CPU core at the same time?
No. While one interrupt’s handler is running on a core, all other interrupts on that specific core are disabled until the handler finishes, except for the NMI.

Q3. What happens if my interrupt handler takes too long?
It delays every other interrupt waiting on that CPU core, which can cause dropped data, missed events, or noticeably higher system latency. Long-running work should always be deferred to a bottom half.

Q4. Is IRQ numbering the same across all Linux devices?
No. IRQ numbers are assigned dynamically by the kernel at boot or device-probe time and depend on the specific board, interrupt controller, and device tree or ACPI configuration. Never hard-code an IRQ number in driver code.

Q5. What tool can I use to see live interrupt activity on my Linux machine?
Running cat /proc/interrupts shows every registered IRQ, the owning driver, and a live per-core firing count, which is the fastest way to confirm whether a device is signaling the CPU.

Q6. Do all Linux devices use the same interrupt controller?
No. x86_64 systems typically use the APIC family of controllers, while ARM64 SoCs commonly use the GIC (Generic Interrupt Controller). The kernel’s generic IRQ layer hides these differences from driver authors.

Q7. What is hardirq context?
It’s the restricted execution environment your interrupt handler runs in — no sleeping, no blocking, and minimal work only, since the kernel is trusting you to return control quickly.

Continue the Free Linux Kernel Programming Course

Next up: writing your first interrupt handler function and registering it with the modern kernel APIs.

Go to Next Lecture

← Previous Lecture    Next Lecture →

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *