Linux 中断执行流程

Linux 中断执行流程

中断执行流程

本文基于 x86_64 构架分析 Linux5.18.2代码。
总结了硬软中断的执行流程。

1 硬中断

通过 DEFINE_IDTENTRY_IRQ(common_interrupt)函数定义了通用的中断处理程序。

DEFINE_IDTENTRY_IRQ(common_interrupt)
{
    struct pt_regs *old_regs = set_irq_regs(regs);
    struct irq_desc *desc;
 
    RCU_LOCKDEP_WARN(!rcu_is_watching(), "IRQ failed to wake up RCU");
 
    desc = __this_cpu_read(vector_irq[vector]);
    if (likely(!IS_ERR_OR_NULL(desc))){
        handle_irq(desc, regs);
    } else {
        ack_APIC_irq();
    
        if (desc == VECTOR_UNUSED) {
            pr_emerg_ratelimited("%s: %d.%u No irq handler for vector\n", __func__, smp_processor_id(), vector);
        } else {
            __this_cpu_write(vector_irq[vector], VECTOR_UNUSED);
        }

    }

    set_irq_regs(old_regs);
}             

irq_enter/exit_rcu() 在函数体之前调用,并且设置 KVM L1D 刷新请求。
如果需要,堆栈切换到中断堆栈必须在函数体内完成。

该函数采用 DEFINE_IDTENTRY_IRQ(common_interrupt)宏来定义。具体的宏实现为:

#define DEFINE_IDTENTRY_IRQ(func)                   \
static void __##func(struct pt_regs *regs, u32 vector);         \
                                    \
__visible noinstr void func(struct pt_regs *regs,           \
                unsigned long error_code)           \
{                                   \
    irqentry_state_t state = irqentry_enter(regs);          \
    u32 vector = (u32)(u8)error_code;               \
                                    \
    instrumentation_begin();                    \
    kvm_set_cpu_l1tf_flush_l1d();                   \
    run_irq_on_irqstack_cond(__##func, regs, vector);       \
    instrumentation_end();                      \
    irqentry_exit(regs, state);                 \
}                                   \
                                    \
static noinline void __##func(struct pt_regs *regs, u32 vector)   

DEFINE_IDTENTRY_IRQ()宏定义了发出设备中断 IDT入口点的代码。其中 @func为入口点的函数名称,中断向量号由低级入口存根推送,并作为 error_code参数传递给函数。

该宏中有三个重要的部分:

  • irqentry_enter()
  • run_irq_on_irqstack_cond()
  • irqentry_exit()

irqentry_enter()/ irqentry_exit()函数会在真正的中断处理程序执行前/后进行一些准备/善后工作,例如是否是从用户空间产生中断,是否需要更新 RCU ,在中断退出时检查是否需要重新调度等。

run_irq_on_irqstack_cond()宏负责将真正的中断处理程序放到中断栈上运行,并在中断处理程序结束时调用软中断响应的函数来处理挂起的软中断。

其中重要的宏为 run_irq_on_irqstack_cond(__##func, regs, vector)

#define run_irq_on_irqstack_cond(func, regs, vector)            \    
{                                   \
    assert_function_type(func, void (*)(struct pt_regs *, u32));    \
    assert_arg_type(regs, struct pt_regs *);            \
    assert_arg_type(vector, u32);                   \                
                                    \                                
    call_on_irqstack_cond(func, regs, ASM_CALL_IRQ,         \        
                  IRQ_CONSTRAINTS, regs, vector);       \            
}

该宏通过call_on_irqstack_cond()宏在中断栈上执行__##func函数。

硬中断调用流程图

硬中断调用流程图

通用硬中断处理函数流程图

通用硬中断处理函数流程图

2 软中断

软中断并不是真正的中断,而是由硬中断在通用处理函数在退出中断前调用的子函数。由于其在中断的上下文中执行,所以称之为软中断。

软中断可以分为在中断上下文执行的软中断和在软中断线程执行的软中断。当一次年软中断的执行超过约束的时候就会从中断上下文切换到软中断线程上下文中运行(软中断线程运行在 CFS 调度类下,优先级为 20,调度策略为 SCHED_NORMAL),这样做的目的是防止软中断长时间占用 CPU 的资源,从而使其他进程/线程处于饥饿状态。

软中断约束:

  • 一次软中断持续执行时间不能超过 2 ms;
  • 没有需要重新调度的进程;
  • 一次软中断中的循环次数不能超过 10 次;

软中断处理流程图

软中断处理流程图

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值