linux中断调用流程(arm)
- 开源代码
- 2025-09-21 11:24:02

文章目录 ARM架构下Linux中断处理全流程解析:从硬件触发到驱动调用 ⚡**一、中断触发与硬件层响应** 🔌**1. 设备触发中断** 📡 **二、CPU阶段:异常入口与上下文处理** 🖥️**1. 异常模式切换** 🔄**2. 跳转至中断向量表** 🗺️ **三、内核中断处理框架** ⚙️**1. 中断向量表初始化** 📜**2. 中断控制器驱动注册** 🔧**3. 中断分发与设备处理** 🔀 **四、设备驱动中的中断处理流程** 🛠️**1. 驱动注册中断处理函数** 📝**2. 实现中断处理函数** 🛠️**3. 释放中断资源** 🗑️ **五、示例:网卡中断处理全流程** 🌐**六、关键数据结构与机制** 📊 ARM架构下Linux中断处理全流程解析:从硬件触发到驱动调用 ⚡
一、中断触发与硬件层响应 🔌 1. 设备触发中断 📡
当外设(如网卡、键盘)需要CPU处理时,其硬件控制器会通过物理中断线(IRQ)向中断控制器发送信号。以ARM的通用中断控制器(GICv3)为例:
中断接收:GIC Distributor模块接收中断请求,并根据中断类型(SPI/PPI/SGI)分类。 🛠️ 关键点:SPI用于共享外设中断,PPI为CPU私有中断。优先级仲裁:Distributor根据中断优先级(配置于寄存器GICD_IPRIORITYRn)和屏蔽状态,选择最高优先级中断。 ⚖️ 优先级规则:数值越小优先级越高,0为最高。路由到目标CPU:通过Redistributor模块将中断传递给目标CPU核心(支持多核负载均衡)。 🌐 多核优化:避免单核过载,提升系统吞吐量。物理信号触发:GIC通过CPU的IRQ引脚触发异常模式切换。 ⚡ 信号传递:硬件自动完成,无需软件干预。二、CPU阶段:异常入口与上下文处理 🖥️ 1. 异常模式切换 🔄
CPU收到中断信号后,硬件自动完成以下操作:
保存上下文:将当前程序状态(PSTATE、PC、SP等)压入内核栈。 📦 关键寄存器:包括通用寄存器、程序计数器、栈指针。切换异常级别: 用户态(EL0)→ 内核态(EL1):触发完整的上下文切换。 🔒 安全隔离:防止用户程序直接访问内核资源。内核态(EL1)→ EL1:仅保存关键寄存器,复用当前内核栈。 ⏩ 快速路径:减少模式切换开销。 2. 跳转至中断向量表 🗺️ 向量表基址:由寄存器VBAR_EL1指定,指向内核预定义的向量表(arch/arm64/kernel/entry.S)。 🏷️ 配置时机:内核启动时通过set_vbar()初始化。入口偏移计算: IRQ入口:VBAR_EL1 + 0x280(EL1h模式)。 🔍 偏移规则:每种异常类型有固定偏移量。同步异常入口:VBAR_EL1 + 0x400(用于系统调用)。 📌 示例:系统调用通过svc指令触发同步异常。 // arch/arm64/kernel/entry.S kernel_ventry 1, irq // EL1h模式IRQ入口三、内核中断处理框架 ⚙️ 1. 中断向量表初始化 📜
ARM64的中断向量表通过汇编宏kernel_ventry定义,每个条目对应一种异常类型:
IRQ处理入口:最终调用handle_arch_irq(全局函数指针)。 🔗 跳转逻辑:从汇编跳转到C语言函数。关键汇编跳转:irq_handler: bl handle_arch_irq // 跳转到C语言处理函数 2. 中断控制器驱动注册 🔧以GIC驱动为例,初始化时完成中断处理函数的绑定:
// drivers/irqchip/irq-gic.c void __init gic_init(...) { gic_dist_init(gic); // 初始化Distributor gic_cpu_init(gic); // 初始化CPU Interface set_handle_irq(gic_handle_irq); // 注册全局处理函数 } set_handle_irq:将gic_handle_irq赋值给handle_arch_irq,建立汇编到C的桥梁。 🌉 桥梁作用:屏蔽硬件差异,统一中断入口。 3. 中断分发与设备处理 🔀GIC驱动通过gic_handle_irq读取中断号并分发给设备驱动:
static void __exception_irq_entry gic_handle_irq(...) { u32 irqnr = gic_read_iar(); // 读取GIC中断应答寄存器 handle_domain_irq(gic_data.domain, irqnr, regs); // 映射并处理 }// kernel/irq/irqdesc.c int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq, bool lookup, struct pt_regs *regs) { ... irq_enter(); // 进入中断上下文 irq = irq_find_mapping(domain, hwirq); // 硬件中断号映射为虚拟中断号 if (irq合法) { generic_handle_irq(irq); // 调用中断处理链 } else { ack_bad_irq(irq); // 错误处理 } irq_exit(); // 退出中断上下文 ... } handle_domain_irq的核心作用: 中断上下文标记:irq_enter()进入原子上下文,禁用调度。 🚫 禁止行为:禁止睡眠、内存分配等非原子操作。硬件中断号映射:通过irq_domain将硬件IRQ转换为Linux虚拟IRQ。 🗂️ 映射策略:支持线性映射、树映射等多种方式。调用设备ISR:从irq_desc[].action链表中执行驱动注册的中断处理函数。 ⚡ 快速响应:上半部处理时间通常小于1ms。
四、设备驱动中的中断处理流程 🛠️ 1. 驱动注册中断处理函数 📝
设备驱动通过request_irq注册中断服务例程(ISR):
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev); 参数说明: irq:虚拟中断号(由irq_of_parse_and_map解析设备树获得)。 🌳 设备树示例:interrupts = <0 168 IRQ_TYPE_EDGE_RISING>; // SPI 168,上升沿触发 flags:标志位(如IRQF_SHARED表示共享中断)。 ⚠️ 共享中断:需唯一dev_id标识不同设备。dev:设备标识符(共享中断时用于区分设备)。 📌 示例:PCI设备使用pci_dev指针作为标识。 2. 实现中断处理函数 🛠️上半部(Top Half):快速响应硬件,禁止阻塞或睡眠。
static irqreturn_t my_irq_handler(int irq, void *dev_id) { struct net_device *dev = dev_id; // 1. 读取硬件状态(如网卡DMA缓冲区) u32 status = readl(dev->reg_base + STATUS_REG); // 2. 清除中断标志 writel(STATUS_CLEAR, dev->reg_base + STATUS_REG); // 3. 触发下半部(如tasklet) tasklet_schedule(&dev->tasklet); return IRQ_HANDLED; }🚨 注意事项:避免在中断上下文中调用kmalloc()或mutex_lock()。
下半部(Bottom Half):处理耗时任务,支持多种机制:
SoftIRQ:内核预定义的高优先级任务(如网络收包)。 🌟 优势:支持多CPU并行处理。Tasklet:基于SoftIRQ,单CPU串行执行。 ⚡ 适用场景:GPIO按键去抖动处理。Workqueue:运行于进程上下文,允许休眠。 🛌 示例:文件I/O或网络协议栈处理。 3. 释放中断资源 🗑️驱动卸载时需调用free_irq释放中断号:
void free_irq(unsigned int irq, void *dev_id);⚠️ 内存安全:必须在驱动卸载路径中调用,防止资源泄漏。
五、示例:网卡中断处理全流程 🌐 硬件触发:网卡接收数据包,向GIC发送IRQ信号。 📡 触发时机:DMA传输完成或FIFO缓冲区非空。GIC路由:Distributor将中断路由至CPU0,分配硬件中断号168。 🔄 负载均衡:GICv3支持动态调整目标CPU。CPU跳转:CPU0执行向量表VBAR_EL1 + 0x280处的irq入口。 ⏱️ 低延迟:硬件自动跳转,无需软件轮询。GIC处理:gic_handle_irq读取中断号168,调用handle_domain_irq。 🔍 中断号解析:通过GICC_IAR寄存器获取。中断映射:通过irq_domain将168映射为Linux虚拟IRQ 200。 🌉 映射关系:存储在irq_desc[200].irq_data.hwirq。驱动处理:执行irq_desc[200].action中的网卡ISR(如NAPI收包)。 🚀 性能优化:NAPI在收包时切换为轮询模式,减少中断风暴。中断返回:恢复上下文,触发软中断(如NET_RX_SOFTIRQ)处理数据。 📦 数据传递:sk_buff从内核空间传递到用户空间。
六、关键数据结构与机制 📊 组件/机制功能说明示例/APIGIC Distributor接收外设中断,优先级仲裁,路由到目标CPU核心。gic_dist_init()VBAR_EL1存储中断向量表基址,决定异常入口跳转位置。set_vbar()irq_domain管理硬件中断号(HW IRQ)到Linux虚拟中断号(VIRQ)的映射。irq_domain_add_linear()irq_desc[]全局中断描述符数组,存储中断处理函数链(action链表)。struct irq_descrequest_irq()驱动注册中断处理函数,关联到irq_desc[VIRQ].action链表。request_irq()
linux中断调用流程(arm)由讯客互联开源代码栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“linux中断调用流程(arm)”
上一篇
用C语言实现一个链表(四)
下一篇
Ruby数组(Array)