硬中断 软中断

硬中断 软中断 一、为什么需要软中断要理解软中断首先要明确硬中断的局限性硬中断执行时会关闭本地 CPU 的中断至少关闭对应中断线如果硬中断处理函数耗时过长会导致其他硬件中断无法及时响应甚至丢失中断比如网卡丢包。硬中断是硬件触发的执行上下文是中断上下文不能睡眠、不能调度只能处理最紧急的硬件操作。硬中断无法并行同一个中断线的硬中断不能嵌套即使在多核 CPU 上同一个硬中断处理函数也只能串行执行。因此内核将中断处理拆分为两个阶段上半部Top Half由硬中断处理只做最紧急、最快速的操作比如读取网卡寄存器、把数据从硬件拷贝到内核 Ring Buffer、标记中断状态然后触发软中断立即返回。下半部Bottom Half由软中断执行处理耗时但不紧急的操作比如解析网络包、协议栈处理、数据拷贝到用户态执行时开中断可以被硬中断抢占多核 CPU 上还能并行执行。二、软中断的核心特性1. 与硬中断的核心区别特性硬中断软中断触发方式硬件设备主动触发通过中断控制器内核代码主动触发raise_softirq()执行时机硬件中断发生后立即执行硬中断返回时、ksoftirqd 线程中、显式调度时中断状态执行时关闭本地对应中断执行时开启所有中断执行上下文中断上下文不能睡眠、不能调度中断上下文不能睡眠、不能调度并发性不可嵌套同中断线串行执行多核 CPU 上可并行执行不同 CPU优先级最高低于硬中断高于普通进程2. 软中断的本质软中断本质是内核预定义的一组全局函数指针数组每个数组元素对应一个软中断类型包含该软中断的处理函数。内核通过pending 位图标记哪些软中断需要执行当触发软中断时只需要将对应位图位置 1内核会在合适的时机执行对应的处理函数。三、软中断的内核实现机制1. 软中断的定义与注册Linux 内核中软中断的最大数量固定为 32 个目前只使用了前 10 个通过softirq_vec数组管理定义如下struct softirq_action { void (*action)(struct softirq_action *); // 软中断处理函数 }; struct softirq_action softirq_vec[32];内核启动时通过open_softirq()函数注册不同类型软中断的处理函数例如网络接收软中断的注册open_softirq(NET_RX_SOFTIRQ, net_rx_action); // 注册网络接收软中断 open_softirq(NET_TX_SOFTIRQ, net_tx_action); // 注册网络发送软中断2. 常见的软中断类型内核预定义的软中断类型优先级从高到低软中断类型数值用途HI_SOFTIRQ0高优先级 tasklet处理紧急的小任务TIMER_SOFTIRQ1内核定时器处理超时事件NET_TX_SOFTIRQ2网络数据包发送NET_RX_SOFTIRQ3网络数据包接收最常用的软中断之一BLOCK_SOFTIRQ4块设备 I/O 处理磁盘、SSD 等IRQ_POLL_SOFTIRQ5中断轮询替代传统硬中断的轮询机制TASKLET_SOFTIRQ6普通 tasklet驱动开发常用SCHED_SOFTIRQ7进程调度负载均衡、唤醒进程HRTIMER_SOFTIRQ8高精度定时器RCU_SOFTIRQ9RCU 锁的延迟回收内核同步机制3. 软中断的触发软中断通过raise_softirq()函数触发核心操作是将对应软中断的 pending 位图位置 1。例如网卡硬中断处理函数中触发网络接收软中断raise_softirq(NET_RX_SOFTIRQ);注意如果触发软中断时当前 CPU 正在处理硬中断软中断不会立即执行而是等待硬中断返回后再执行。4. 软中断的执行时机内核会在以下 3 个关键时机检查并执行 pending 的软中断硬中断处理完成后这是最常见的执行时机硬中断返回用户态 / 内核态前会调用do_softirq()处理所有 pending 的软中断。内核线程 ksoftirqd 中如果软中断执行时间过长比如网络突发大量数据包内核会唤醒每个 CPU 对应的ksoftirqd/n内核线程专门处理软中断避免软中断占满 CPU 导致用户进程饿死。显式调用local_bh_enable()时当内核代码主动开启下半部时会检查并执行 pending 的软中断。5. 软中断的处理流程以网络包接收为例完整的软中断处理流程如下网卡收到数据包通过 DMA 将数据拷贝到内核的 Ring Buffer触发硬中断。网卡硬中断处理函数执行读取网卡状态确认是接收中断。关闭网卡接收中断NAPI 机制避免持续触发硬中断。调用raise_softirq(NET_RX_SOFTIRQ)标记网络接收软中断为 pending。硬中断处理完成返回。硬中断返回前内核调用do_softirq()遍历 pending 位图找到需要执行的软中断NET_RX_SOFTIRQ。调用对应的处理函数net_rx_action()。net_rx_action()从 Ring Buffer 中批量读取数据包封装为sk_buff结构体交给 TCP/IP 协议栈逐层处理IP 层→TCP 层→Socket 层。处理完成后清除 pending 位图开启网卡接收中断NAPI 机制。如果net_rx_action()处理超时默认最多处理 2ms剩余的数据包会交给ksoftirqd内核线程继续处理。四、软中断的衍生机制Tasklet软中断虽然性能高但存在两个问题同一个软中断的处理函数可以在多个 CPU 上并行执行驱动开发者需要自行处理多核并发问题容易出错。软中断的类型是内核预定义的驱动不能动态添加新的软中断类型。为了解决这些问题内核基于TASKLET_SOFTIRQ和HI_SOFTIRQ封装了Tasklet 机制是驱动开发中最常用的下半部方式。1. Tasklet 的核心特性串行执行同一个 Tasklet 只会在一个 CPU 上执行不会在多个 CPU 上并行运行无需考虑多核并发。动态创建驱动可以随时创建和销毁 Tasklet无需修改内核代码。优先级区分高优先级 Tasklet 基于HI_SOFTIRQ普通 Tasklet 基于TASKLET_SOFTIRQ。2. Tasklet 的使用示例// 定义Tasklet处理函数 void my_tasklet_handler(unsigned long data) { // 处理耗时但不紧急的操作不能睡眠 printk(Tasklet executed, data: %ld\n, data); } // 声明并初始化Tasklet DECLARE_TASKLET(my_tasklet, my_tasklet_handler, 123); // 硬中断处理函数中触发Tasklet irqreturn_t my_interrupt(int irq, void *dev_id) { // 上半部快速处理硬件操作 tasklet_schedule(my_tasklet); // 触发Tasklet return IRQ_HANDLED; }五、软中断 vs Tasklet vs 工作队列内核中常用的延迟执行机制有三种软中断、Tasklet、工作队列它们的适用场景完全不同表格特性软中断Tasklet工作队列执行上下文中断上下文中断上下文进程上下文能否睡眠不能不能可以睡眠、调度并发性多核可并行同 Tasklet 串行多核可并行实时性最高高低适用场景内核核心子系统网络、块设备驱动开发简单延迟任务需要睡眠的延迟任务开发难度高需处理并发低无需处理并发低和普通进程一致六、软中断的性能优化与常见问题1. 软中断 CPU 占用过高当系统出现软中断 CPU 占比过高通过top命令查看si指标时通常是以下原因网络突发流量大量网络包导致NET_RX_SOFTIRQ持续执行此时需要优化网络协议栈、开启 RPS/RFS多 CPU 分发网络包。块设备 I/O 密集大量磁盘读写导致BLOCK_SOFTIRQ占用过高此时需要优化 I/O 调度算法、使用 SSD。软中断执行时间过长软中断处理函数中存在耗时操作需要进一步拆分任务交给工作队列处理。2. ksoftirqd 内核线程的作用每个 CPU 对应一个ksoftirqd/n内核线程当软中断的执行时间超过阈值默认 2ms或者软中断被连续触发时内核会将剩余的软中断交给ksoftirqd处理。这样可以避免软中断长时间占用 CPU导致用户进程无法得到调度。3. NAPI 机制与软中断的结合NAPINew API是 Linux 网络子系统的核心优化它将中断 轮询结合大幅减少了硬中断的触发次数当网卡收到第一个包时触发硬中断关闭网卡中断触发NET_RX_SOFTIRQ。软中断处理函数net_rx_action()轮询 Ring Buffer批量处理所有已到达的数据包。处理完成后重新开启网卡中断等待下一个包的到来。这种方式避免了每个数据包都触发一次硬中断在高并发网络场景下性能提升显著。七、总结软中断是 Linux 内核实现高性能异步处理的核心机制它通过拆分中断处理的上下半部解决了硬中断耗时过长的问题。其核心要点软中断是内核态、不可睡眠、可并行的延迟执行机制优先级高于普通进程。网络、块设备、定时器等内核核心子系统都依赖软中断实现高性能处理。Tasklet 是软中断的封装简化了驱动开发工作队列则用于需要睡眠的延迟任务。软中断的性能瓶颈通常出现在高并发网络或 I/O 场景可通过 NAPI、多 CPU 分发、硬件加速等方式优化。