中断(Interrupt)是计算机系统中一种重要的机制,用于处理异步事件。当硬件设备需要 CPU 的注意时,会通过中断通知 CPU。Linux 内核通过中断机制来响应硬件事件,提高系统的响应性和效率。
中断的基本概念 什么是中断 中断是一种硬件和软件机制,用于:
异步事件处理 :硬件设备可以在任何时候通知 CPU
提高效率 :避免 CPU 轮询设备状态
实时响应 :快速响应硬件事件
中断的分类 Linux 系统中的中断主要分为两类:
类型
来源
特点
处理方式
硬中断(Hard Interrupt)
硬件设备
异步、不可预测
中断处理程序(ISR)
软中断(Soft Interrupt)
软件触发
可延迟、可调度
软中断处理程序
中断处理的目标
快速响应 :尽快响应硬件事件
最小化延迟 :减少中断处理时间
避免阻塞 :不阻塞其他中断和进程
负载均衡 :在多核系统中平衡中断负载
硬中断 硬中断(Hardware Interrupt)是由硬件设备产生的中断,是 CPU 与外部设备通信的主要方式。
硬中断的特点
异步性 :硬件设备可以在任何时候产生中断
不可预测 :无法预知中断何时发生
高优先级 :硬中断可以打断正在执行的代码
快速处理 :需要在中断上下文中快速处理
硬中断的触发 硬件中断源 常见的硬件中断源包括:
设备
中断类型
说明
键盘
IRQ 1
按键按下
鼠标
IRQ 12
鼠标移动/点击
网卡
IRQ 11
数据包到达
硬盘
IRQ 14/15
磁盘 I/O 完成
定时器
IRQ 0
时钟中断
串口
IRQ 3/4
串口数据到达
中断请求(IRQ) 每个硬件设备都有一个唯一的中断请求线(IRQ):
1 2 3 4 5 6 7 8 9 10 11 ➜ cat /proc/interrupts CPU0 CPU1 CPU2 CPU3 0: 45 0 0 0 IO-APIC 2-edge timer 1: 3 0 0 0 IO-APIC 1-edge i8042 8: 0 0 0 1 IO-APIC 8-edge rtc0 9: 0 0 0 0 IO-APIC 9-fasteoi acpi 11: 1234 0 0 0 IO-APIC 11-edge eth0 14: 56 0 0 0 IO-APIC 14-edge ata_piix NMI: 0 0 0 0 Non-maskable interrupts LOC: 123456 123456 123456 123456 Local timer interrupts
输出说明:
第一列 :IRQ 号
CPU0-CPU3 :每个 CPU 处理的中断次数
类型 :中断类型(edge/level)
设备 :产生中断的设备名称
硬中断处理流程 flowchart TD
A[硬件设备产生中断] --> B[CPU 保存当前上下文]
B --> C[跳转到中断向量表]
C --> D[执行中断处理程序 ISR]
D --> E[快速处理 Top Half]
E --> F{是否需要延迟处理?}
F -->|是| G[标记软中断]
F -->|否| H[恢复上下文]
G --> H
H --> I[继续执行被中断的代码]
style A fill:#e1f5ff
style D fill:#fff4e1
style E fill:#fff4e1
style G fill:#e8f5e9
style I fill:#f3e5f5
详细步骤 1. 中断产生
2. CPU 响应中断
3. 中断处理程序(ISR) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 irqreturn_t my_interrupt_handler (int irq, void *dev_id) { return IRQ_HANDLED; }
4. 中断处理的两部分(Top Half / Bottom Half) Top Half(上半部):
在中断上下文中执行
必须快速完成
只做最关键的处理
不能阻塞
Bottom Half(下半部):
延迟处理
可以执行较长时间的操作
可以睡眠和阻塞
通过软中断、tasklet、工作队列实现
硬中断处理示例 内核模块中的中断处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <linux/interrupt.h> #include <linux/module.h> #include <linux/kernel.h> static int irq = 10 ; static int irq_counter = 0 ;static irqreturn_t my_interrupt_handler (int irq, void *dev_id) { irq_counter++; printk(KERN_INFO "Interrupt %d occurred, count: %d\n" , irq, irq_counter); return IRQ_HANDLED; } static int __init my_init (void ) { if (request_irq(irq, my_interrupt_handler, IRQF_SHARED, "my_device" , NULL )) { printk(KERN_ERR "Failed to register IRQ %d\n" , irq); return -EIO; } printk(KERN_INFO "IRQ %d registered\n" , irq); return 0 ; } static void __exit my_exit (void ) { free_irq(irq, NULL ); printk(KERN_INFO "IRQ %d freed\n" , irq); } module_init(my_init); module_exit(my_exit); MODULE_LICENSE("GPL" );
中断上下文的特点 在硬中断处理程序中:
可以做的:
快速处理硬件事件
读取/写入设备寄存器
标记软中断
使用自旋锁(spinlock)
不能做的:
睡眠(sleep)
阻塞操作
访问用户空间
使用可能睡眠的函数(如 kmalloc(GFP_KERNEL))
软中断 软中断(Soft Interrupt)是由软件触发的中断,用于延迟处理硬中断中无法完成的工作。
软中断的特点
可延迟 :可以在合适的时机执行
可调度 :由内核调度执行
可中断 :可以被硬中断打断
可并发 :可以在多个 CPU 上并发执行
软中断的类型 Linux 内核预定义了多种软中断类型:
软中断类型
说明
HI_SOFTIRQ
高优先级 tasklet
TIMER_SOFTIRQ
定时器软中断
NET_TX_SOFTIRQ
网络发送软中断
NET_RX_SOFTIRQ
网络接收软中断
BLOCK_SOFTIRQ
块设备软中断
IRQ_POLL_SOFTIRQ
IRQ 轮询软中断
TASKLET_SOFTIRQ
普通 tasklet
SCHED_SOFTIRQ
调度器软中断
HRTIMER_SOFTIRQ
高精度定时器软中断
RCU_SOFTIRQ
RCU 软中断
软中断处理流程 flowchart TD
A[硬中断处理完成] --> B[标记软中断 raise_softirq]
B --> C{检查软中断的时机}
C -->|从硬中断返回| D[检查待处理软中断]
C -->|从系统调用返回| D
C -->|ksoftirqd 线程| D
D --> E{有待处理软中断?}
E -->|是| F[执行软中断处理程序]
E -->|否| G[继续正常执行]
F --> H[处理完成]
H --> I{还有待处理软中断?}
I -->|是| F
I -->|否| G
style A fill:#e1f5ff
style B fill:#fff4e1
style F fill:#e8f5e9
style H fill:#f3e5f5
软中断的触发时机 软中断在以下时机被检查和执行:
从硬中断返回时
从系统调用返回时
ksoftirqd 内核线程
软中断处理示例 查看软中断统计 1 2 3 4 5 6 7 8 9 10 11 12 13 ➜ cat /proc/softirqs CPU0 CPU1 CPU2 CPU3 HI: 0 0 0 0 TIMER: 123456 123456 123456 123456 NET_TX: 123 456 789 1011 NET_RX: 456789 456789 456789 456789 BLOCK: 12 34 56 78 IRQ_POLL: 0 0 0 0 TASKLET: 23 45 67 89 SCHED: 234567 234567 234567 234567 HRTIMER: 0 0 0 0 RCU: 345678 345678 345678 345678
内核中的软中断处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <linux/interrupt.h> #include <linux/kernel.h> static void my_softirq_handler (struct softirq_action *action) { printk(KERN_INFO "Soft interrupt handled\n" ); } static int __init my_init (void ) { open_softirq(MY_SOFTIRQ, my_softirq_handler); raise_softirq(MY_SOFTIRQ); return 0 ; }
Tasklet 和工作队列 Tasklet Tasklet 是基于软中断的延迟处理机制:
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <linux/interrupt.h> void my_tasklet_function (unsigned long data) { printk(KERN_INFO "Tasklet executed\n" ); } DECLARE_TASKLET(my_tasklet, my_tasklet_function, 0 ); tasklet_schedule(&my_tasklet);
Tasklet 特点:
基于软中断实现
同一类型的 tasklet 不能并发执行
不能睡眠
快速执行
工作队列(Work Queue) 工作队列是另一种延迟处理机制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <linux/workqueue.h> void my_work_function (struct work_struct *work) { printk(KERN_INFO "Work executed\n" ); } DECLARE_WORK(my_work, my_work_function); schedule_work(&my_work);
工作队列特点:
在进程上下文中执行
可以睡眠
可以执行长时间操作
由内核线程执行
硬中断 vs 软中断对比
特性
硬中断
软中断
触发源
硬件设备
软件
执行上下文
中断上下文
中断上下文或进程上下文
优先级
高
较低
执行时间
必须快速
可以较长
能否睡眠
不能
不能(但工作队列可以)
并发性
可能嵌套
可以并发
使用场景
快速响应硬件
延迟处理工作
中断处理的最佳实践 Top Half / Bottom Half 设计 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 static irqreturn_t my_interrupt_handler (int irq, void *dev_id) { struct my_device *dev = dev_id; u32 status = readl(dev->regs + STATUS_REG); writel(status, dev->regs + STATUS_REG); memcpy (dev->buffer, dev->data, sizeof (dev->data)); raise_softirq(MY_SOFTIRQ); return IRQ_HANDLED; } static void my_softirq_handler (struct softirq_action *action) { struct my_device *dev = get_device(); process_data(dev->buffer); wake_up_interruptible(&dev->wait_queue); }
中断处理的原则
快速处理 :Top Half 必须快速完成
延迟处理 :耗时操作放到 Bottom Half
避免阻塞 :中断处理中不能睡眠
保护数据 :使用适当的锁机制
避免嵌套 :合理设计避免中断嵌套过深
中断相关的系统工具 查看中断信息 1 2 3 4 5 6 7 8 9 10 11 ➜ cat /proc/interrupts ➜ cat /proc/interrupts | grep CPU0 ➜ cat /proc/softirqs ➜ watch -n 1 'cat /proc/interrupts'
CPU 亲和性(CPU Affinity) CPU 亲和性是指将中断或进程绑定到特定的 CPU 核心上执行。在多核系统中,合理设置 CPU 亲和性可以:
提高缓存命中率 :同一中断始终在同一 CPU 处理,数据更可能在缓存中
减少 CPU 迁移开销 :避免中断在不同 CPU 间迁移
负载均衡 :合理分配中断负载到不同 CPU
性能隔离 :将关键中断隔离到特定 CPU
CPU 亲和性的原理 每个中断都有一个 CPU 亲和性掩码(affinity mask),用位图表示哪些 CPU 可以处理该中断:
1 2 3 4 5 6 7 ➜ cat /proc/irq/11/smp_affinity 00000001 ➜ cat /proc/irq/11/smp_affinity_list 0
smp_affinity 格式说明 smp_affinity 使用位掩码(bitmask)表示 CPU 亲和性:
格式:
十六进制 :00000001 表示 CPU 0,00000003 表示 CPU 0 和 1
二进制对应 :每一位代表一个 CPU
00000001 (0x1) = 0000 0001 (二进制) = CPU 0
00000003 (0x3) = 0000 0011 (二进制) = CPU 0 和 1
0000000f (0xf) = 0000 1111 (二进制) = CPU 0, 1, 2, 3
ffffffff (0xffffffff) = 所有 CPU
计算示例:
查看中断亲和性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ➜ for irq in /proc/irq/*/smp_affinity; do echo "IRQ $(basename $(dirname $irq) ): $(cat $irq) " done ➜ cat /proc/irq/11/smp_affinity 00000001 ➜ cat /proc/irq/11/smp_affinity_list 0 ➜ cat /proc/interrupts | grep eth0 11: 123456 0 0 0 eth0 ➜ cat /proc/irq/11/smp_affinity 00000001
设置中断亲和性 基本设置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ➜ echo 1 > /proc/irq/11/smp_affinity ➜ echo 0 > /proc/irq/11/smp_affinity_list ➜ echo 2 > /proc/irq/11/smp_affinity ➜ echo 1 > /proc/irq/11/smp_affinity_list ➜ echo 4 > /proc/irq/11/smp_affinity ➜ echo 2 > /proc/irq/11/smp_affinity_list ➜ echo 3 > /proc/irq/11/smp_affinity ➜ echo 0-1 > /proc/irq/11/smp_affinity_list ➜ echo f > /proc/irq/11/smp_affinity ➜ echo 0-3 > /proc/irq/11/smp_affinity_list
高级设置 1 2 3 4 5 6 7 8 9 10 11 12 ➜ nproc 64 ➜ echo 0,1,2,3 > /proc/irq/11/smp_affinity_list ➜ echo 00000000,0000000f > /proc/irq/11/smp_affinity
CPU 亲和性的应用场景 1. 网络中断绑定 将网卡中断绑定到特定 CPU,提高网络性能:
1 2 3 4 5 6 7 8 9 ➜ cat /proc/interrupts | grep eth0 11: 123456 0 0 0 eth0 ➜ echo 1 > /proc/irq/11/smp_affinity ➜ echo 3 > /proc/irq/11/smp_affinity
2. 多队列网卡的中断绑定 现代网卡支持多队列,每个队列有独立的中断:
1 2 3 4 5 6 7 8 9 10 11 12 ➜ cat /proc/interrupts | grep eth0 11: 12345 0 0 0 eth0-TxRx-0 12: 0 12345 0 0 eth0-TxRx-1 13: 0 0 12345 0 eth0-TxRx-2 14: 0 0 0 12345 eth0-TxRx-3 ➜ echo 1 > /proc/irq/11/smp_affinity ➜ echo 2 > /proc/irq/12/smp_affinity ➜ echo 4 > /proc/irq/13/smp_affinity ➜ echo 8 > /proc/irq/14/smp_affinity
3. 磁盘 I/O 中断绑定 将磁盘中断绑定到特定 CPU,减少 I/O 延迟:
1 2 3 4 5 6 ➜ cat /proc/interrupts | grep sda 14: 56 0 0 0 ata_piix ➜ echo 1 > /proc/irq/14/smp_affinity
4. 隔离关键中断 将关键中断隔离到专用 CPU,避免被其他任务干扰:
1 2 3 4 5 ➜ echo 1 > /proc/irq/11/smp_affinity ➜ taskset -cp 1-7 <pid>
中断负载均衡策略 策略 1:单 CPU 绑定(性能优先) 1 2 3 4 5 6 for irq in 11 12 13 14; do echo 1 > /proc/irq/$irq /smp_affinity done
策略 2:轮询分配(负载均衡) 1 2 3 4 5 6 7 echo 1 > /proc/irq/11/smp_affinity echo 2 > /proc/irq/12/smp_affinity echo 4 > /proc/irq/13/smp_affinity echo 8 > /proc/irq/14/smp_affinity
策略 3:多 CPU 共享(高吞吐量) 1 2 3 4 echo f > /proc/irq/11/smp_affinity
CPU 亲和性与进程绑定的配合 1 2 3 4 5 6 7 ➜ echo 1 > /proc/irq/11/smp_affinity ➜ taskset -cp 0 <network_process_pid>
使用脚本自动化设置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #!/bin/bash INTERFACE="eth0" CPUS="0,1" IRQS=$(cat /proc/interrupts | grep $INTERFACE | awk '{print $1}' | cut -d: -f1) for irq in $IRQS ; do if [ "$CPUS " = "0,1" ]; then MASK="3" elif [ "$CPUS " = "0" ]; then MASK="1" else echo "$CPUS " > /proc/irq/$irq /smp_affinity_list continue fi echo "Setting IRQ $irq to CPUs $CPUS (mask: $MASK )" echo $MASK > /proc/irq/$irq /smp_affinity done echo "Current affinity settings:" for irq in $IRQS ; do echo "IRQ $irq : $(cat /proc/irq/$irq/smp_affinity) " done
CPU 亲和性的监控 1 2 3 4 5 6 7 8 9 10 11 ➜ watch -n 1 'cat /proc/interrupts | head -20' ➜ for cpu in 0 1 2 3; do echo "CPU $cpu interrupts:" cat /proc/interrupts | awk -v cpu=$cpu "NR==1 || \$$((cpu+2) ) > 0" | head -10 done ➜ mpstat -I SUM 1
CPU 亲和性的最佳实践
网络密集型应用
将网卡中断绑定到专用 CPU
将网络处理进程绑定到同一 CPU
使用多队列网卡时,每个队列绑定不同 CPU
I/O 密集型应用
将磁盘中断绑定到特定 CPU
避免与计算密集型任务竞争同一 CPU
实时系统
将实时任务隔离到专用 CPU
将中断绑定到非实时 CPU
多核系统
使用 irqbalance 自动平衡(适合大多数场景)
手动设置(适合特定性能要求)
CPU 亲和性的注意事项
持久性 :通过 /proc 设置的亲和性在重启后会丢失
权限 :设置 CPU 亲和性需要 root 权限
irqbalance 冲突 :如果启用了 irqbalance,手动设置可能被覆盖
1 2 3 sudo systemctl stop irqbalancesudo systemctl disable irqbalance
性能测试 :设置前后进行性能测试,验证效果
持久化 CPU 亲和性设置 方法 1:使用 systemd 服务 创建服务文件 /etc/systemd/system/set-irq-affinity.service:
1 2 3 4 5 6 7 8 9 10 11 [Unit] Description =Set IRQ CPU AffinityAfter =network.target[Service] Type =on eshotExecStart =/usr/local/bin/set-irq-affinity.shRemainAfterExit =yes [Install] WantedBy =multi-user.target
方法 2:使用启动脚本 在 /etc/rc.local 中添加:
1 2 3 4 #!/bin/bash echo 3 > /proc/irq/11/smp_affinityexit 0
使用 irqbalance 1 2 3 4 5 6 7 8 sudo apt-get install irqbalancesudo systemctl start irqbalancesudo systemctl status irqbalance
使用 perf 分析中断 1 2 3 4 5 6 7 8 9 10 11 sudo apt-get install linux-perfsudo perf top -e irq:*sudo perf record -e irq:* -a sleep 10sudo perf report
中断性能优化 中断合并(Interrupt Coalescing) 网卡等设备支持中断合并,减少中断频率:
1 2 3 4 5 ethtool -c eth0 ethtool -C eth0 rx-usecs 100
CPU 亲和性设置(详细说明见上方章节) CPU 亲和性是将中断绑定到特定 CPU 核心的机制,详细说明、设置方法和最佳实践请参考上方的”CPU 亲和性(CPU Affinity)”章节。
快速参考:
1 2 3 4 5 6 7 8 cat /proc/irq/11/smp_affinityecho 1 > /proc/irq/11/smp_affinityecho 3 > /proc/irq/11/smp_affinity
中断负载均衡 在多核系统中,合理分配中断负载是性能优化的关键。有两种主要方式:
自动负载均衡(irqbalance) 1 2 3 4 5 6 7 8 9 10 sudo apt-get install irqbalancesudo systemctl enable irqbalancesudo systemctl start irqbalancesudo systemctl status irqbalance
irqbalance 的工作原理:
监控每个 CPU 的中断负载
自动将中断迁移到负载较低的 CPU
考虑 CPU 拓扑和缓存局部性
适合大多数通用场景
手动负载均衡 1 2 3 4 5 6 7 8 9 10 11 for irq in $(cat /proc/interrupts | grep eth0 | awk '{print $1}' | cut -d: -f1); do echo 3 > /proc/irq/$irq /smp_affinity done irqs=($(cat /proc/interrupts | grep eth0 | awk '{print $1}' | cut -d: -f1)) cpus=(1 2 4 8) for i in "${!irqs[@]} " ; do echo ${cpus[$i]} > /proc/irq/${irqs[$i]} /smp_affinity done
选择建议:
通用场景 :使用 irqbalance(自动、智能)
特定优化 :手动设置(精确控制、性能调优)
实时系统 :手动设置(可预测性)
常见问题和调试 中断风暴(Interrupt Storm) 问题 :某个设备产生大量中断,占用 CPU
诊断:
1 2 3 4 5 watch -n 1 'cat /proc/interrupts' top
解决:
中断延迟过高 问题 :中断响应时间过长
诊断:
1 2 3 4 echo function_graph > /sys/kernel/debug/tracing/current_tracerecho irq_handler_entry > /sys/kernel/debug/tracing/set_eventcat /sys/kernel/debug/tracing/trace
解决:
优化中断处理函数
减少 Top Half 处理时间
使用 NAPI(网络设备)
软中断占用 CPU 过高 问题 :ksoftirqd 线程占用大量 CPU
诊断:
1 2 3 4 5 cat /proc/softirqstop -H -p $(pgrep ksoftirqd)
解决:
优化软中断处理函数
使用工作队列替代
调整软中断处理策略
实际应用场景 网络数据包处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 static irqreturn_t net_interrupt_handler (int irq, void *dev_id) { receive_packets(); disable_interrupts(); napi_schedule(); return IRQ_HANDLED; } static void net_rx_action (struct softirq_action *action) { while (has_packets()) { process_packet(); } enable_interrupts(); }
定时器中断 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void timer_interrupt_handler (void ) { jiffies++; update_wall_time(); raise_softirq(TIMER_SOFTIRQ); } static void run_timer_softirq (struct softirq_action *action) { run_timers(); raise_softirq(SCHED_SOFTIRQ); }
总结 Linux 系统的中断机制是系统响应性和效率的关键:
硬中断 :快速响应硬件事件,在中断上下文中执行
软中断 :延迟处理耗时操作,可以并发执行
Top Half / Bottom Half :分离快速处理和延迟处理
性能优化 :通过中断合并、CPU 亲和性等提高性能
理解中断机制对于:
系统性能优化
设备驱动开发
系统调试和故障排查
实时系统设计
都非常重要。
参考文献