海门做网站,WordPress分享无法显示,wordpress虚拟资源主题教程,专业门户网站建设文章目录 1. 前言2. Linux 中断是否会嵌套#xff1f;2.1 分析背景2.2 中断处理抢占、嵌套可能性分析2.3 中断处理抢占、嵌套小结 3. 参考资料 1. 前言
限于作者能力水平#xff0c;本文可能存在谬误#xff0c;因此而给读者带来的损失#xff0c;作者不做任何承诺。
2. … 文章目录 1. 前言2. Linux 中断是否会嵌套2.1 分析背景2.2 中断处理抢占、嵌套可能性分析2.3 中断处理抢占、嵌套小结 3. 参考资料 1. 前言
限于作者能力水平本文可能存在谬误因此而给读者带来的损失作者不做任何承诺。
2. Linux 中断是否会嵌套
2.1 分析背景
本文基于 ARMv7 架构Linux 4.14 内核进行分析。
2.2 中断处理抢占、嵌套可能性分析
ARMv7 架构下的 IRQ 中断处理流程概要如下(使用 GICv2 中断芯片)
/* arch/arm/kernel/entry-armv.S *//** Interrupt dispatcher*/
vector_stub irq, IRQ_MODE, 4 /* vector_irq */.long __irq_usr 0 (USR_26 / USR_32).......long __irq_svc 3 (SVC_26 / SVC_32).......align 5
__irq_svc:// 保存被中断的上下文切换到 SVC 模式复用内核栈svc_entry// 中断处理irq_handler...// 恢复被中断的上下文 使能 CPU 中断同时从中断返回svc_exit r5, irq 1 return from exception
ENDPROC(__irq_svc)/** Interrupt handling.*/
.macro irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER// GICv2 芯片中断处理接口ldr r1, handle_arch_irq /* r1 gic_handle_irq() */mov r0, spbadr lr, 9997fldr pc, [r1]
#elsearch_irq_handler_default
#endif
9997:.endm
在一进步描述之前必须先说明一个很重要的事实ARMv7 架构 CPU 在进入中断模式的时候会自动禁用当前 CPU 上中断这是硬件自动完成的无需软件干预。也就是说当前的代码时工作在 CPU 中断禁用的状态下。继续看后续中断处理流程
gic_handle_irq() /* drivers/irqchip/irq-gic.c */.../** 这个循环试图一次处理多个中断, 而不是每次中断都从 CPU 的* 中断向量地址重新开始执行, 这是 GIC 对中断处理的一个优化。*/do {/** 读取硬件中断号.* 从寄存器 GIC_CPU_INTACK 读取硬件中断号, GIC 认为 CPU 响应了中断.*/irqstat readl_relaxed(cpu_base GIC_CPU_INTACK);...if (likely(irqnr 15 irqnr 1020)) { /* 处理 PPI, SPI */if (static_key_true(supports_deactivate))// EOI mode 1: Priority drop 和 Deactive 分离// 这里 GIC 做 Priority drop目的是允许同等优先级的中断进来writel_relaxed(irqstat, cpu_base GIC_CPU_EOI);handle_domain_irq(gic-domain, irqnr, regs);isb();continue; /* 继续处理下一中断, 可以避免反复的中断上下文切换 */}if (irqnr 16) { /* 处理 SGI */// 这里不细表不影响本文分析的主题......}} while (1);;
handle_domain_irq(gic-domain, irqnr, regs);__handle_domain_irq(domain, hwirq, true, regs)irq_enter();__irq_enter();...// 标示在硬件中断处理过程中即 top halfpreempt_count_add(HARDIRQ_OFFSET);...#ifdef CONFIG_IRQ_DOMAINif (lookup)irq irq_find_mapping(domain, hwirq); /* 查找 硬件中断号 hwirq 对应的 Linux 中断号 */#endifgeneric_handle_irq(irq); /* 中断处理 */struct irq_desc *desc irq_to_desc(irq);...generic_handle_irq_desc(desc);desc-handle_irq(desc);handle_fasteoi_irq(desc) // 处理 SPI 中断......irq_exit(); /* 软中断, RCU 等等处理 */
handle_fasteoi_irq(desc) // 处理 SPI 中断...handle_irq_event(desc); // 调用具体的中断 handler, 我们不关注这里的细节...cond_unmask_eoi_irq(desc, chip);...chip-irq_eoi(desc-irq_data);gic_eoimode1_eoi_irq()...// 这里是关注的重点。// 在前面 gic_irq_handler() 的处理逻辑里面已经做了 // Priority drop使得同等优先级的中断可以进来在这里// 做 Deactive interrupt使得中断状态由 Active 转为// Idle 状态。到此GIC 将能够重新向 CPU 发送中断信号了。// 从前面的分析中我们了解到CPU 的中断此时处于禁用状态// 只等 CPU 重新启用中断就能够收到 GIC 发给它的中断信号。writel_relaxed(gic_irq(d), gic_cpu_base(d) GIC_CPU_DEACTIVATE);......
handle_fasteoi_irq() 的流程告诉我们在调用完中断 handler 后GIC 已经能够发送中断信号给 CPU 了只等 CPU 重新启用中断就可以接收中断信号了。接着看 handle_fasteoi_irq() 之后的中断退出流程主要是 irq_exit()
irq_exit() /* kernel/softirq.c */...// 标示退出硬件中断处理过程即退出 top halfpreempt_count_sub(HARDIRQ_OFFSET);// 这里 !in_interrupt() 判定在 ARMv7 下实际等同于 !in_softirq()// 防止在 中断抢占、嵌套下的 softirq 处理过程 (bottom half) 的重入.if (!in_interrupt() local_softirq_pending())invoke_softirq(); // 处理 softirq 即进入 bottom half...
这里的 !in_interrupt() 判定要重点关注下先看下它的定义
#define softirq_count() (preempt_count() SOFTIRQ_MASK)
#define irq_count() (preempt_count() (HARDIRQ_MASK | SOFTIRQ_MASK \| NMI_MASK))#define in_softirq() (softirq_count())
#define in_interrupt() (irq_count())
看到了吧在不支持 NMI 的 ARMv7 架构下随着 preempt_count_sub(HARDIRQ_OFFSET); 将 HARDIRQ 的计数归 0此处的 !in_interrupt() 已经等同于 !in_softirq()目的是防止在中断抢占、嵌套下的 softirq 处理过程 (bottom half) 的重入。这里提前做出结论会发生 中断抢占、嵌套有点提前后面会分析原因。继续看中断退出接下来的流程
invoke_softirq(); // 处理 softirq 即进入 bottom half...__do_softirq();...pending local_softirq_pending(); /* 读取当前 CPU 挂起的 softirq 事件 */...__local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET); /* 禁用 softirq (禁用 bottom half) */...restart: set_softirq_pending(0); /* 清除当前 CPU 挂起的 softirq *//* * 重点来了CPU 中断启用了同时从前面分析看到GIC 也准备好了, * 所以 CPU 可以接收中断了同时别忘记我们此时还处在当前中断* 处理上下文中!!! 如果在处理 softirq 期间 CPU 上发生了中断是* 会产生中断抢占、嵌套的。*/local_irq_enable();h softirq_vec;while ((softirq_bit ffs(pending))) {// softirq 处理不是本文重点不做细表...}// 处理完 softirq 后重新禁用 CPU 中断这和汇编代码中重启 CPU 中断对应local_irq_disable();...__local_bh_enable(SOFTIRQ_OFFSET); /* 使能 softirq (使能 bottom half) */...
2.3 中断处理抢占、嵌套小结
从上面分析我们知道中断抢占、嵌套在 Linux 内核下是可能发生的不过这些 中断抢占、嵌套 的发生大概率是合理且无害的。为什么呢首先中断的抢占、嵌套是不可能发生在中断处理 top half 部分的因为 top half 的处理期间CPU 中断都是处于禁用状态中断的抢占、嵌套只可能发生在 bottom half(即 softirq 处理期间)其次通过 !in_interrupt() 限制了抢占、嵌套的中断无法在前一中断处理 bottom half 期间再次进入 bottom half这样就避免中断内核栈溢出的风险最后由于 CPU 进入中断处理时自动禁用了中断所以抢占、嵌套的中断处理期间在有前一中断处理 bottom half(即 softirq) 前提下不会在中途使能 CPU 中断这样就避免了中断反复嵌套的问题。发生嵌套后后续中断必定是一个接一个的串行处理简单来讲中断嵌套最多只会有两重第一个中断 bottom half 期间被中断第二个抢占、嵌套中断进来。当然这一切的前提是中断内核栈空间能够满足这两重中断嵌套的使用。另外说大概率无害是因为中断的抢占、嵌套仍然有可能导致 bottom half(即 softirq) 处理的延迟毕竟它们中断了正在处理 softirq 。
3. 参考资料
[1] Interrupts