How ARM+Linux Interrupts Power Efficient Computing: Deep Dive into Mechanisms
This article explains the role of the interrupt system in ARM‑based Linux platforms, covering hardware modes, vector tables, exception handling, GIC architecture, and kernel integration, and provides detailed code examples and optimization techniques for developers working on embedded and high‑performance systems.
In today’s digital era, efficient computation drives technological progress, and the ARM architecture combined with the Linux interrupt system acts as a key to unlocking high‑performance, low‑power computing across devices from smartphones to industrial controllers.
The interrupt system works like an intelligent manager: when external devices (e.g., keyboard, mouse) or internal events (e.g., timer overflow) request service, the CPU temporarily pauses the current task, handles the urgent request, and then resumes the original work, greatly improving response speed and overall efficiency. In the ARM+Linux combination, the interrupt system is heavily optimized to exploit the strengths of both.
1. Interrupt System: The "Emergency Responder" of the Computing World
Just as a fire alarm instantly alerts firefighters, an interrupt instantly notifies the CPU of urgent events. The CPU stops its current execution, processes the interrupt, and then returns to the original flow. This mechanism dramatically speeds up system response.
2. ARM Interrupt Hardware Foundations
2.1 ARM Operating Modes and Exception Categories
User Mode : Normal application execution with limited privileges, similar to a citizen operating within a confined space.
System Mode : A privileged mode that can freely access system resources, akin to a city manager.
IRQ Mode : Handles general interrupt requests (e.g., keyboard, mouse).
FIQ Mode : Handles fast, time‑critical interrupts with additional banked registers for quicker context switching.
Supervisor Mode : The default mode after power‑on, used for software interrupts (SWI) and system initialization.
Abort Mode : Triggered by illegal memory accesses, similar to a security checkpoint.
Undefined Mode : Entered when the CPU cannot decode an instruction, used for software emulation of coprocessors.
Except for User Mode, the other six modes are privileged and can be entered either by software or by specific exception events.
When an exception occurs (e.g., FIQ), the CPU saves the return address in LR, copies CPSR to SPSR, switches to the appropriate mode, and jumps to the exception vector address. Exiting the exception restores LR to PC, copies SPSR back to CPSR, and clears the interrupt disable flag.
For an external IRQ, the CPU checks the I bit in CPSR; if it is 0, the CPU switches to IRQ mode, jumps to vector address 0x18, determines the interrupt source, executes the corresponding service routine, clears the interrupt flag, and returns to the pre‑interrupt context.
2.2 ARM Interrupt Pins and Enable Control
ARM cores typically have two interrupt pins: irq for normal interrupts and fiq for fast interrupts. The CPSR I and F bits enable or disable handling of these pins. When both bits are 0, the CPU will respond to signals on both pins.
3. ARM+Linux Interrupt System Basics
3.1 ARM Architecture Interrupt Mechanism
ARM defines two main interrupt types: IRQ (general external interrupt) and FIQ (fast interrupt). IRQ is used for most peripherals, while FIQ is reserved for high‑priority, time‑critical events such as high‑speed data transfer.
3.2 Linux Management of ARM Interrupts
Linux builds the interrupt vector table during early boot (e.g.,
early_trap_init()), copies the exception vectors and stubs to the configured base address (usually 0xffff0000), and registers device interrupt handlers via
request_irq(). The kernel’s
irq_descarray links interrupt numbers to
irqactionstructures that contain the handler function, flags, and device information.
4. Linux on ARM Interrupt Construction
4.1 Exception Vector Table Creation and Setup
<code>void __init early_trap_init(void)
{
unsigned long vectors = CONFIG_VECTORS_BASE;
extern char __stubs_start[], __stubs_end[];
extern char __vectors_start[], __vectors_end[];
extern char __kuser_helper_start[], __kuser_helper_end[];
int kuser_sz = __kuser_helper_end - __kuser_helper_start;
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes, sizeof(sigreturn_codes));
flush_icache_range(vectors, vectors + PAGE_SIZE);
modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
}
#define CONFIG_VECTORS_BASE 0xffff0000</code>The vector table contains entries such as
b vector_irq + stubs_offsetfor normal interrupts and
b vector_fiq + stubs_offsetfor fast interrupts.
4.2 Interrupt Handling Function Flow
When hardware raises an interrupt, the controller checks mask and priority registers. If allowed, it asserts the IRQ pin. The CPU switches to IRQ mode, jumps to address 0x18, which typically contains
LDR PC, IRQ_ADDRto branch to the specific service routine. The routine saves registers, determines the source, calls the registered handler via
irq_handler, clears the pending flag, and restores the saved context.
<code>asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
struct irq_desc *desc = irq_desc + irq;
if (irq >= NR_IRQS)
desc = &bad_irq_desc;
irq_enter();
desc_handle_irq(irq, desc);
irq_finish(irq);
irq_exit();
set_irq_regs(old_regs);
}
static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
{
desc->handle_irq(irq, desc);
}</code>The
asmlinkagemacro expands to
extern "C"on ARM, indicating that arguments are passed according to the ATPCS calling convention (register‑based), not via the stack.
5. ARM GIC Interrupt Controller Analysis
5.1 GIC Concepts and Versions
The Generic Interrupt Controller (GIC) acts as the central dispatcher for all interrupt sources. Versions V1–V4 exist; V2 is common in ARMv7‑A SoCs, while V3/V4 target ARM64 servers.
5.2 GIC Interrupt Input Types
PPI (Private Peripheral Interrupt): Up to 16 per‑CPU interrupts, e.g., per‑core timers.
SPI (Shared Peripheral Interrupt): Up to 988 shared interrupts for devices such as GPIO, UART, etc.
5.3 GIC Working Mechanism and Register Functions
The GIC consists of a Distributor and a CPU Interface.
Distributor : Enables/disables global and per‑interrupt signals, sets priorities, routes interrupts to specific CPUs, configures trigger mode (edge/level), and groups interrupts for security.
CPU Interface : Controls delivery of pending interrupts to each core, acknowledges interrupts, signals end‑of‑interrupt, masks low‑priority interrupts, and defines pre‑emptive policies.
6. ARM+Linux Interrupt System Applications and Optimizations
6.1 Interrupt Handler Optimization
Separate critical (time‑sensitive) work from non‑critical work, use fast algorithms (e.g., quicksort instead of bubble sort), and keep the handler short to avoid long interrupt latency.
6.2 Reasonable Hardware Resource Configuration
Set appropriate GIC interrupt priorities, route high‑priority interrupts to powerful cores, and balance load across CPUs to maximize throughput.
6.3 Code Implementation
Example of registering and unregistering an interrupt in a kernel module:
<code>#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
printk(KERN_INFO "My Interrupt Handler: IRQ %d\n", irq);
return IRQ_HANDLED;
}
static int __init my_module_init(void)
{
int irq_num = 50; // example interrupt number
int ret;
ret = request_irq(irq_num, my_interrupt_handler, IRQF_TRIGGER_RISING, "my_device", NULL);
if (ret) {
printk(KERN_ERR "Failed to request IRQ %d: %d\n", irq_num, ret);
return ret;
}
printk(KERN_INFO "Successfully registered IRQ %d\n", irq_num);
return 0;
}
static void __exit my_module_exit(void)
{
int irq_num = 50;
free_irq(irq_num, NULL);
printk(KERN_INFO "Successfully unregistered IRQ %d\n", irq_num);
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("ARM+Linux Interrupt Example");
</code>The handler checks the interrupt source (e.g., button or timer), performs the necessary action, and returns
IRQ_HANDLEDor
IRQ_NONEfor unknown sources.
Deepin Linux
Research areas: Windows & Linux platforms, C/C++ backend development, embedded systems and Linux kernel, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.