Lazy loaded image
中断专题01-Linux系统对中断处理的演进
Words 7332Read Time 19 min
2025-5-7
一、Linux中断系统演进二、Linux对中断的扩展:硬件中断、软件中断2.1 概念2.2 硬件与软件中断区别2.3 硬件和软件中断关系三、硬件中断处理流程3.1 中断处理原则1:不能嵌套3.2 中断处理原则2:越快越好四、上半部和下半部机制4.1 源码分析4.2 流程图说明情景 a. 硬件中断 A 处理过程中,没有其他中断发生情景 b. 硬件中断 A 处理过程中,又再次发生了中断 A情景 c. 硬件中断 A 处理过程中,又再次发生了中断 B五、软件中断详解5.1 主要数据结构5.2 软件中断执行流程5.3 多种下半部机制的并存六、Tasklet机制6.1 简介Tasklet 概念应用领域6.2 实现细节Tasklet 数据结构Tasklet 与软中断的关系Tasklet 执行流程Tasklet 调度函数Tasklet 处理函数Per-CPU 变量Tasklet 初始化与控制函数Tasklet 设计优势Tasklet vs. 其他延迟执行机制6.3 源码示例与应用基本 Tasklet 使用方法高优先级 Tasklet 示例Tasklet 控制示例网络驱动中的 Tasklet 应用七、工作队列(Workqueue7.1 简介7.2 实现细节7.2.1 核心数据结构7.2.1.1 工作项 (work_struct)7.2.1.2 工作队列 (workqueue_struct)7.2.1.3 工作者线程池 (worker_pool)7.2.2 函数调用栈7.2.2.1 初始化工作项7.2.2.2 提交工作项7.2.2.3 工作线程执行循环7.2.2.4 执行工作项7.2.3 工作队列的状态管理 7.3代码示例:使用工作队列7.3.1 基本用法7.3.2 延迟执行工作7.3.3 创建自定义工作队列八、线程化中断(Threaded IRQ)
 
参考资料
  1. 异常和中断,以及线程和进程处理机制可参考原理篇03-实现上下文处理关键-异常处理 | Felix’s Micro Space
     

    一、Linux中断系统演进

    • 早期Linux内核(2.4之前):使用较为简单的中断处理机制,所有中断处理程序都在中断上下文中完成,容易导致中断响应延迟。
    • Linux 2.6:引入了"上半部"(top half)和"下半部"(bottom half)的概念,分离紧急和非紧急处理,提升中断响应能力。
    • Linux 4.x:进一步优化了中断处理框架,引入通用中断控制器(GIC)支持,增强了多核SMP系统中的中断亲和性(Interrupt Affinity)支持。
    • Linux 4.9.88:完善了中断线程化处理机制,改进了实时性能和中断控制器抽象层。

    二、Linux对中断的扩展:硬件中断、软件中断

    2.1 概念

    Linux系统中的中断机制可以分为两大类:硬件中断和软件中断。这两类中断共同构成了Linux完整的中断处理框架,使操作系统能够高效地响应外部事件并平衡实时性(快速响应中断)与系统吞吐量(高效处理大量工作)。
    • 硬件中断(Hardware Interrupt)是由外部设备(如键盘、网卡、硬盘控制器等)或CPU内部事件(如时钟、异常等)触发的中断信号。它们通过物理中断线连接到中断控制器,然后传递给CPU,强制CPU暂停当前执行的程序,转而执行中断处理程序。硬件中断可用于设备驱动、时钟中断、外部事件响应等。
    • 软件中断(Software Interrupt)是由操作系统内部代码触发的中断,本质上是一种延迟执行机制,用于处理不需要立即完成且可能耗时较长的任务。Linux中,软件中断是实现"下半部"(Bottom Half)中断处理的主要机制之一。软中断可用于网络协议栈处理、块设备I/O完成、调度、定时器等

    2.2 硬件与软件中断区别

    2.3 硬件和软件中断关系

    三、硬件中断处理流程

     
    对硬件中断的处理有2个原则:不能嵌套,越快越好

    3.1 中断处理原则1:不能嵌套

    中断嵌套会导致栈被消耗完,为了简单化中断的处理,在Linux系统上中断无法嵌套:即当前中断A没处理完之前,不会响应另一个中断B(即使它的优先级更高)。

    3.2 中断处理原则2:越快越好

    在中断的处理过程中,该CPU是不能进行进程调度的,所以中断的处理要越快越好,尽早让其他中断能被处理──进程调度靠定时器中断来实现。
    在Linux系统中使用中断是挺简单的,为某个中断irq注册中断处理函数handler,可以使用request_irq函数:
    在handler函数中,代码尽可能高效。
    假如某个中断工作量很大,没办法加快。比如对于按键中断,需要等待几十毫秒消除机械抖动。当一个中断要耗费很多时间来处理时,它的坏处是:在这段时间内,其他中断无法被处理。因为这段时间是关闭中断的。
    要如何避免中断服务程序阻塞太长时间?

    四、上半部和下半部机制

    如果某个中断就是要做那么多事,我们能不能把它拆分成两部分:紧急的、不紧急的?在handler函数里只做紧急的事,然后就重新开中断,让系统得以正常运行;那些不紧急的事,以后再处理,处理时是开中断的。
    1. 上半部(Top Half)
        • 硬件中断处理
        • 快速响应,最小化中断屏蔽时间
        • 只处理关键和紧急的任务
    1. 下半部(Bottom Half)
        • 软件中断处理
        • 延迟执行,可被抢占
        • 处理非紧急但耗时的任务

    4.1 源码分析

    notion image
    硬件中断处理流程:
    • 调用 irq_enter(),该函数会增加 preempt_count 的值(通过 preempt_count_add(HARDIRQ_OFFSET))。preempt_count 用于表示当前 CPU 进入了硬件中断上下文。
    • generic_handle_irq(irq): 调用与该中断号关联的中断处理函数,通常是通过 request_irq 注册的函数。
    • 调用 irq_exit 函数,从 preempt_count 中减去 HARDIRQ_OFFSET,表示硬件中断处理完成。
    软中断的处理是通过 do_softirq 函数完成的。流程如下:
    • local_bh_disable_ip(RET_IP, SOFTIRQ_OFFSET):
      • 通过 local_bh_disable 禁用软中断。
      • preempt_count++ 表明进入软中断上下文。
    • local_irq_enable()
      • 执行软中断时会短暂打开硬件中断(即重新允许处理中断),以防止阻塞其他高优先级中断。
    • invoke_softirq():
      • 调用所有挂起的软中断处理函数。
      • 针对系统中的每种类型的软中断,都会分别执行其对应的处理函数(比如网络子系统处理 NET_RX_SOFTIRQ、块设备 I/O 处理 BLOCK_SOFTIRQ 等)。
    • 软中断完成后,调用 local_bh_enable 重新关闭软中断,并减少 preempt_count
    preempt_count 和中断嵌套管理
    • preempt_count 是 Linux 内核中用于管理中断上下文、抢占以及嵌套处理的计数器。

    4.2 流程图说明

    情景 a. 硬件中断 A 处理过程中,没有其他中断发生

    • 纯粹的中断 A 响应即按流程完成处理。
    • 中断进入后,通过 irq_enter()preempt_count++,执行硬件中断的上半部处理逻辑。
    • 上半部处理完成后,调用 irq_exit(),通过 preempt_count--退出硬件中断。
    • 若有挂起的软中断,执行软中断处理,上半部、下半部的代码各执行一次。

    情景 b. 硬件中断 A 处理过程中,又再次发生了中断 A

    出现中断嵌套的情况,此时需要处理两次硬件中断 A,可能导致 preempt_count 值的连续增加与减少。
    • 第一个硬件中断 A 进入时,preempt_count++ 进入硬件中断上下文并开始处理上半部。
    • 在上半部处理期间,再次发生硬件中断 A,嵌套触发,再次调用 irq_enter()preempt_count++ 值累加(变为 2),继续处理新中断。
    • 第二个硬件中断完成后退出时,preempt_count-- 降到 1,回到外层中断的上下文。
    • 第2次中断发生后,打断了第一次中断的第⑦步处理。当第2次中断处理完毕,CPU会继续去执行第⑦步。可以看到,发生2次硬件中断A时,它的上半部代码执行了2次,但是下半部代码只执行了一次。所以,同一个中断的上半部、下半部,在执行时是多对一的关系

    情景 c. 硬件中断 A 处理过程中,又再次发生了中断 B

    此情景与情景 b 类似,但处理的嵌套中断类型不同,这里分析两个不同中断类型的执行顺序。
    • 第一个硬件中断 A 进入处理,preempt_count++ 表明进入中断上下文。
    • 在上半部处理中,另一硬件中断 B 发生,再次触发进入硬件中断上下文,preempt_count++ 累加。
    • 内核优先完成硬件中断的处理:B 的上半部完成后退出上下文,同时 preempt_count-- 更新,④步发现preempt_count等于1,所以直接结束当前第2次中断的处理;。
    • 第2次中断发生后,打断了第一次中断A的第⑦步处理。当第2次中断B处理完毕,CPU会继续去执行第⑦步。在第⑦步里,它会去执行中断A的下半部,也会去执行中断B的下半部。所以,多个中断的下半部,是汇集在一起处理的。

    五、软件中断详解

    5.1 主要数据结构

    Linux软件中断系统的主要数据结构:
    这里NR_SOFTIRQS在Linux 4.9.88中定义了10种不同类型的软中断:
    核心数据结构分析:
     

    5.2 软件中断执行流程

    在Linux 4.9.88中,软中断的核心代码位于kernel/softirq.c。软中断执行的主要入口是__do_softirq()函数:

    5.3 多种下半部机制的并存

    Linux提供多种下半部机制,每种适用于不同场景:
    1. 软中断(Softirq)
        • 最底层的机制
        • 静态分配、高性能
        • 适合对延迟要求严格的场景
    1. Tasklet
        • 基于软中断
        • 动态创建,易于使用
        • 同一类型Tasklet串行执行
    1. 工作队列(Workqueue)
        • 在进程上下文中执行
        • 可以睡眠/阻塞
        • 适合需要阻塞操作的场景
    1. 线程化IRQ
        • 将中断处理放入内核线程
        • 简化驱动开发
        • 适合复杂处理且不需要极低延迟的场景

    六、Tasklet机制

    6.1 简介

    Tasklet 是 Linux 内核中一种基于软中断的延迟执行机制,是中断"下半部"(Bottom Half)处理的重要实现方式之一。它提供了一种在中断上下文之外执行非紧急任务的轻量级方法,平衡了系统的实时响应能力和处理效率。

    Tasklet 概念

    Tasklet 是一种特殊的软中断处理机制,具有以下特点:
    • 轻量级:比工作队列(workqueue)更轻量,但功能不如工作队列灵活
    • 动态创建:可以在运行时动态创建和调度
    • 执行上下文:在软中断上下文中执行,不可睡眠
    • 串行执行:同一类型的 tasklet 保证串行执行,不会并行

    应用领域

    Tasklet 在 Linux 内核中广泛应用于:
    • 网络驱动的数据包处理
    • 存储设备的 I/O 完成处理
    • 输入设备的事件处理
    • 定时器过期处理
    • 设备驱动中需要延迟执行但不需要睡眠的任务

    6.2 实现细节

    Tasklet 数据结构

    在 Linux 4.9.88 中,tasklet 的核心数据结构定义在 include/linux/interrupt.h 中:

    Tasklet 与软中断的关系

    Tasklet 基于软中断(softirq)实现,使用两种软中断:HI_SOFTIRQTASKLET_SOFTIRQ,分别用于高优先级和普通优先级的 tasklet。

    Tasklet 执行流程

    Tasklet 调度函数

    以下是 Tasklet 主要调度函数的源码分析:
    高优先级 tasklet 的调度函数:

    Tasklet 处理函数

    对应的 tasklet 处理函数在软中断处理中被调用:

    Per-CPU 变量

    Tasklet 系统使用 Per-CPU 变量来存储 tasklet 链表,避免多核系统上的锁竞争:

    Tasklet 初始化与控制函数

    Tasklet 设计优势

    1. 动态创建:与软中断不同,tasklet 可以动态创建和销毁
    1. 串行执行:同一 tasklet 不会在多个 CPU 上并行执行,简化同步
    1. 性能优化:使用 Per-CPU 变量避免了多 CPU 系统上的缓存抖动
    1. 优先级区分:提供高优先级和普通优先级两种 tasklet

    Tasklet vs. 其他延迟执行机制

    比较:
    1. Tasklet vs. 软中断:tasklet 更易于使用,软中断性能更高但数量固定
    1. Tasklet vs. 工作队列:tasklet 不能睡眠,工作队列可以但开销更大
    1. Tasklet vs. 线程化IRQ:tasklet 适用于通用延迟处理,线程化IRQ 专门用于中断处理

    6.3 源码示例与应用

    基本 Tasklet 使用方法

    高优先级 Tasklet 示例

    Tasklet 控制示例

    网络驱动中的 Tasklet 应用

    Linux 网络子系统广泛使用 tasklet 处理网络数据包:

    七、工作队列(Workqueue

    7.1 简介

    工作队列(Workqueue)是Linux内核中一种重要的延迟执行机制,它允许内核将耗时操作推迟到进程上下文中执行。工作队列通过创建内核线程(称为worker线程)来执行被延迟的工作,保证这些操作在可安全睡眠的上下文中进行。
    工作队列在Linux内核中有广泛应用,特别适合:
    • 需要在中断处理程序之外执行的耗时操作
    • 可能需要睡眠的操作
    • IO密集型任务
    • 需要访问用户空间的操作
    Linux内核从2.6版本开始引入工作队列,在Linux 4.9.88中,工作队列系统已经演化为一个复杂而高效的子系统。

    7.2 实现细节

    7.2.1 核心数据结构

    7.2.1.1 工作项 (work_struct)

    这是工作队列系统的基本单位,定义在include/linux/workqueue.h中:
    • data:包含工作项的标志位和指针信息
    • entry:用于在队列中链接工作项
    • func:工作项的回调函数,当工作项被执行时会调用

    7.2.1.2 工作队列 (workqueue_struct)

    工作队列代表一组工作线程,定义在kernel/workqueue.c中:
    • pwqs:此工作队列的所有pool workqueue列表
    • name:工作队列的名称
    • flags:控制工作队列行为的标志位
    • cpu_pwqs:每CPU的pool workqueue数组

    7.2.1.3 工作者线程池 (worker_pool)

    管理工作者线程的结构,定义在kernel/workqueue_internal.h中:
    • worklist:等待执行的工作项列表
    • workers:此线程池中所有工作线程的列表
    • idle_list:空闲工作线程列表
    • nr_workers:此池中的工作线程总数
    • nr_idle:当前空闲的工作线程数量

    7.2.2 函数调用栈

    让我们详细分析工作队列调用栈的关键函数:

    7.2.2.1 初始化工作项

    在使用工作队列前,需要初始化工作项:
    此宏初始化工作项的列表入口,设置回调函数,并标记工作项为等待状态。

    7.2.2.2 提交工作项

    有多种方式可以提交工作项到工作队列:
    queue_work()将工作项提交到默认CPU,而queue_work_on()允许指定CPU。
    核心的__queue_work()函数负责将工作项插入到适当的工作队列:

    7.2.2.3 工作线程执行循环

    工作线程的主循环由worker_thread()函数实现:

    7.2.2.4 执行工作项

    process_one_work()是实际执行工作项的函数:

    7.2.3 工作队列的状态管理

    工作项可能处于以下几种状态:
    1. 初始化:已创建但尚未提交到队列
    1. 排队:已提交到工作队列,等待处理
    1. 运行中:正在被工作线程执行
    1. 完成:工作项已执行完成
    1. 取消:工作项被取消,不会执行
    Linux内核提供了丰富的API来管理工作项的状态:
    • cancel_work():取消尚未执行的工作项
    • cancel_work_sync():取消工作项并等待正在执行的完成
    • flush_work():等待特定工作项完成
    • flush_workqueue():等待工作队列中所有工作项完成

    7.3代码示例:使用工作队列

    7.3.1 基本用法

    7.3.2 延迟执行工作

    7.3.3 创建自定义工作队列

    八、线程化中断(Threaded IRQ)

    线程化中断是Linux内核的一个强大功能,允许将中断处理程序放在内核线程中执行:
     
    上一篇
    模板设计模式:让你的代码结构更清晰
    下一篇
    Guide to Linux System

    Comments
    Loading...
    Catalog