Interrupt Handling in OS: Complete Guide to Hardware Interrupts and ISR

Introduction to Interrupt Handling

Interrupt handling is a fundamental mechanism in operating systems that enables efficient communication between hardware devices and the CPU. When a hardware device needs attention from the processor, it sends an interrupt signal to temporarily suspend the current execution and handle the device’s request through specialized routines called Interrupt Service Routines (ISR).

This mechanism ensures that the CPU can respond to time-critical events without continuously polling hardware devices, making modern computing systems both responsive and efficient.

What are Interrupts?

An interrupt is a signal sent to the processor by hardware or software indicating that an event needs immediate attention. When an interrupt occurs, the processor temporarily stops its current activity and executes a special function called an interrupt handler or ISR.

Types of Interrupts

  • Hardware Interrupts: Generated by hardware devices (keyboard, mouse, timer, disk drives)
  • Software Interrupts: Generated by executing specific instructions (system calls, exceptions)
  • Maskable Interrupts: Can be disabled by the processor
  • Non-maskable Interrupts (NMI): Cannot be disabled and must be handled immediately

Hardware Interrupts: The Foundation

Hardware interrupts are signals generated by external devices to request service from the CPU. These interrupts are essential for:

  • I/O Operations: Notifying completion of data transfer
  • Timer Events: Triggering periodic system activities
  • Error Conditions: Reporting hardware failures or exceptions
  • User Input: Processing keyboard and mouse events

Interrupt Handling in OS: Complete Guide to Hardware Interrupts and ISR

Common Hardware Interrupt Sources

Device IRQ Number Purpose Priority
System Timer IRQ 0 Clock ticks, scheduling High
Keyboard IRQ 1 Key press/release events Medium
Serial Port 1 IRQ 4 Serial communication Medium
Hard Disk IRQ 14 Disk I/O completion High
Network Card IRQ 11 Network packet arrival High

Interrupt Service Routines (ISR)

An Interrupt Service Routine is a special function that handles specific interrupt events. ISRs must be carefully designed to be fast, efficient, and non-blocking to maintain system responsiveness.

ISR Characteristics

  • Fast Execution: Must complete quickly to avoid blocking other interrupts
  • Atomic Operations: Cannot be interrupted by lower-priority interrupts
  • Minimal Stack Usage: Limited stack space available during interrupt context
  • No Blocking Calls: Cannot perform operations that might cause the handler to sleep

Interrupt Handling in OS: Complete Guide to Hardware Interrupts and ISR

Sample ISR Implementation

Here’s a simplified example of a keyboard interrupt handler in C:


// Keyboard ISR example
void keyboard_interrupt_handler() {
    // Save processor state (done automatically by hardware)
    
    // Read the scancode from keyboard port
    unsigned char scancode = inb(KEYBOARD_DATA_PORT);
    
    // Process the scancode
    if (scancode < 128) {
        // Key pressed
        process_key_press(scancode);
    } else {
        // Key released
        process_key_release(scancode - 128);
    }
    
    // Send End of Interrupt signal to PIC
    outb(PIC1_COMMAND, EOI);
    
    // Return from interrupt (restores processor state automatically)
}

Interrupt Handling Process

The interrupt handling process follows a well-defined sequence to ensure system stability and proper resource management.

Interrupt Handling in OS: Complete Guide to Hardware Interrupts and ISR

Step-by-Step Process

  1. Interrupt Detection: Hardware device raises an interrupt signal
  2. Interrupt Controller: Routes the interrupt to the appropriate CPU
  3. Context Saving: CPU automatically saves current instruction pointer and flags
  4. Interrupt Disable: Prevents nested interrupts during critical handling
  5. Vector Lookup: CPU uses Interrupt Descriptor Table (IDT) to find ISR address
  6. ISR Execution: Transfer control to the appropriate interrupt handler
  7. Interrupt Acknowledgment: Signal to hardware that interrupt is being processed
  8. Handler Logic: Execute device-specific handling code
  9. Context Restoration: Restore all saved registers and flags
  10. Return: Resume execution of interrupted program

Interrupt Priority and Nesting

Modern systems implement interrupt priority schemes to ensure that critical interrupts can preempt less important ones. This hierarchical approach prevents low-priority interrupts from blocking time-critical operations.

Interrupt Handling in OS: Complete Guide to Hardware Interrupts and ISR

Priority Levels Example

Priority Level Interrupt Type Example Can Be Preempted By
0 (Highest) Non-Maskable Memory errors, power failure None
1 System Timer Clock tick, scheduling NMI only
2 Critical I/O Disk controller, network NMI, Timer
3 (Lowest) User Input Keyboard, mouse All higher priorities

Practical Examples and Code Implementation

Timer Interrupt Handler

The system timer interrupt is crucial for task scheduling and timekeeping:


volatile unsigned long system_ticks = 0;

void timer_interrupt_handler() {
    // Increment system tick counter
    system_ticks++;
    
    // Update system time
    update_system_time();
    
    // Trigger process scheduler every 10ms
    if (system_ticks % 10 == 0) {
        schedule_next_process();
    }
    
    // Send EOI to interrupt controller
    outb(PIC1_COMMAND, EOI);
}

Network Interrupt Handler

Network card interrupts handle incoming packet notifications:


void network_interrupt_handler() {
    // Read network card status
    uint16_t status = read_network_status();
    
    if (status & RX_PACKET_READY) {
        // Packet received - add to receive queue
        packet_t *packet = read_network_packet();
        enqueue_received_packet(packet);
        
        // Wake up network processing thread
        wakeup_network_thread();
    }
    
    if (status & TX_COMPLETE) {
        // Transmission complete - free buffer
        free_transmit_buffer();
    }
    
    // Clear interrupt flags
    clear_network_interrupt();
    outb(PIC1_COMMAND, EOI);
}

Interrupt Descriptor Table (IDT)

The Interrupt Descriptor Table is a data structure used by x86 processors to determine the correct response to interrupts and exceptions. Each entry in the IDT contains information about how to handle a specific interrupt.

Interrupt Handling in OS: Complete Guide to Hardware Interrupts and ISR

IDT Entry Structure

Field Size Description
Offset Low 16 bits Lower 16 bits of ISR address
Selector 16 bits Code segment selector
Reserved 8 bits Must be zero
Type & Attributes 8 bits Gate type and privilege level
Offset High 16 bits Upper 16 bits of ISR address

Performance Considerations

Efficient interrupt handling is crucial for system performance. Poor interrupt handling can lead to system responsiveness issues and potential data loss.

Optimization Strategies

  • Minimize ISR Duration: Keep interrupt handlers as short as possible
  • Deferred Processing: Use bottom halves or tasklets for complex operations
  • Interrupt Coalescing: Batch multiple interrupts to reduce overhead
  • NAPI (New API): Polling mode for high-frequency interrupts
  • Interrupt Affinity: Bind interrupts to specific CPU cores

Top Half vs Bottom Half

Modern operating systems split interrupt processing into two parts:

Aspect Top Half (ISR) Bottom Half (Deferred)
Execution Context Interrupt context Process context
Duration Very short (microseconds) Longer (milliseconds)
Operations Critical, time-sensitive Complex processing
Interrupts Disabled Enabled
Example Acknowledge hardware Process network packets

Common Issues and Debugging

Interrupt Storm

An interrupt storm occurs when a device generates interrupts faster than the system can handle them, potentially causing system lockup.


// Example: Interrupt rate limiting
static unsigned long last_interrupt_time = 0;
static int interrupt_count = 0;

void rate_limited_isr() {
    unsigned long current_time = get_system_time();
    
    // Reset counter every second
    if (current_time - last_interrupt_time > 1000) {
        interrupt_count = 0;
        last_interrupt_time = current_time;
    }
    
    interrupt_count++;
    
    // Disable interrupt if rate too high
    if (interrupt_count > MAX_INTERRUPTS_PER_SECOND) {
        disable_device_interrupt();
        schedule_delayed_re_enable();
        return;
    }
    
    // Normal interrupt processing
    handle_device_interrupt();
}

Lost Interrupts

Interrupts can be lost due to:

  • Interrupt Masking: Interrupts disabled for too long
  • Hardware Buffer Overflow: Device buffers full
  • Slow ISR Processing: Handler takes too long to complete
  • Priority Inversion: High-priority interrupts blocked by low-priority handlers

Best Practices for Interrupt Handling

Design Guidelines

  1. Keep ISRs Short: Minimize time spent in interrupt context
  2. Avoid Blocking Operations: No sleep, mutex, or I/O operations in ISRs
  3. Use Proper Synchronization: Employ spinlocks and atomic operations
  4. Handle Errors Gracefully: Implement robust error handling
  5. Document Interrupt Behavior: Clear documentation for maintainability

Sample Best Practice Implementation


// Good ISR implementation example
void optimized_network_isr() {
    // Quick acknowledgment
    uint32_t status = read_and_clear_interrupt_status();
    
    // Minimal processing in ISR
    if (status & RX_INTERRUPT) {
        // Just set flag and schedule bottom half
        set_rx_flag();
        schedule_rx_tasklet();
    }
    
    if (status & TX_INTERRUPT) {
        set_tx_flag();
        schedule_tx_tasklet();
    }
    
    // Quick EOI
    send_eoi();
}

// Bottom half processing
void network_rx_tasklet() {
    // Complex processing in process context
    while (packets_available()) {
        packet_t *pkt = get_next_packet();
        process_network_packet(pkt);
    }
}

Modern Interrupt Handling Mechanisms

Message Signaled Interrupts (MSI)

MSI is a modern alternative to traditional pin-based interrupts, offering better performance and scalability:

  • No Shared Interrupt Lines: Each device gets unique interrupt vector
  • Better Performance: Reduced interrupt latency
  • Scalability: Supports more devices without IRQ conflicts
  • Memory-Based: Uses memory writes instead of electrical signals

IOMMU and Interrupt Remapping

Modern systems use IOMMU (Input-Output Memory Management Unit) for interrupt security and virtualization support:

  • Interrupt Isolation: Prevents malicious devices from triggering arbitrary interrupts
  • Virtual Machine Support: Enables interrupt forwarding to guest VMs
  • Interrupt Translation: Maps physical interrupts to virtual interrupt vectors

Conclusion

Interrupt handling is a cornerstone of modern operating system design, enabling efficient hardware-software communication and system responsiveness. Understanding the intricacies of hardware interrupts, ISR implementation, and performance optimization is essential for system programmers and kernel developers.

Key takeaways for effective interrupt handling include keeping ISRs minimal and fast, using deferred processing for complex operations, implementing proper synchronization mechanisms, and following established best practices for robust system design.

As hardware continues to evolve with technologies like MSI, IOMMU, and multi-core architectures, interrupt handling mechanisms will continue to adapt, but the fundamental principles of efficient, reliable interrupt processing remain constant.