中断详解(六)——do_IRQ函数

中断处理程序在完成一些堆栈和寄存器的处理后,调用do_IRQ函数。do_IRQ是中断在C语言的总入口。

common_interrupt:
    addl $-0x80,(%esp)  /* Adjust vector into the [-256,-1] range */
    SAVE_ALL
    TRACE_IRQS_OFF
    movl %esp,%eax
    call do_IRQ                                                                                                                         
    jmp ret_from_intr
ENDPROC(common_interrupt)
    CFI_ENDPROC
do_IRQ{)函数执行与一个中断相关的所有中断服务例程. 其原型如下:

unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
{                                                                                                                                       
    struct pt_regs *old_regs = set_irq_regs(regs);

    /* high bit used in ret_from_ code  */
    unsigned vector = ~regs->orig_ax;
    unsigned irq;

    exit_idle();
    irq_enter(); //执行__irq_enter( )宏,它使表示中断处理程序嵌套数量的计数器递增.

    irq = __get_cpu_var(vector_irq)[vector];//取得中断号

    if (!handle_irq(irq, regs)) { //重要
        ack_APIC_irq();

        if (printk_ratelimit())
            pr_emerg("%s: %d.%d No irq handler for vector (irq %d)\n",
                __func__, smp_processor_id(), vector, irq);
    }   

    irq_exit(); //重要

    set_irq_regs(old_regs);
    return 1;
}



bool handle_irq(unsigned irq, struct pt_regs *regs)
{
    struct irq_desc *desc;
    int overflow;
 
    overflow = check_stack_overflow();
 
    desc = irq_to_desc(irq);  //根据中断号在数组irq_desc中找到一项                                                                      
    if (unlikely(!desc))
        return false;
 
    if (!execute_on_irq_stack(overflow, desc, irq)) {
        if (unlikely(overflow))
            print_stack_overflow();
        desc->handle_irq(irq, desc);
    }   
 
    return true;

desc -> handle_irq初始化:
desc->handle_irq初始化发生在系统初始化时:
//linux-2.6.32/arch/x86/kernel/irqinit.c
void __init init_IRQ(void)                                                                                                              
{
    x86_init.irqs.intr_init();
}
//linux-2.6.32/arch/x86/kernel/x86_init.c
struct x86_init_ops x86_init __initdata = { 
......
    .irqs = { 
        .pre_vector_init    = init_ISA_irqs, //被.intr_init调用
        .intr_init      = native_init_IRQ,   
        .trap_init      = x86_init_noop,
    },  
......
}
//linux-2.6.32/arch/x86/kernel/irqinit.c
void __init native_init_IRQ(void)
{
......
    /* Execute any quirks before the call gates are initialised: */
    x86_init.irqs.pre_vector_init(); //init_ISA_irqs
......
}
 
void __init init_ISA_irqs(void)
{   
    ......
    for (i = 0; i < NR_IRQS_LEGACY; i++) {
        ......
        set_irq_chip_and_handler_name(i, &i8259A_chip,
                          handle_level_irq, "XT");                                                                                      
    }  
}
 
 
void
set_irq_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,                                                                  
                  irq_flow_handler_t handle, const char *name)
{       
    set_irq_chip(irq, chip);
    __set_irq_handler(irq, handle, 0, name);
}   
 
void __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,const char *name)
{  
    ....
    desc->handle_irq = handle;//handle 即为handle_level_irq
    ....
}
 

可见desc->handle_irq(irq, desc);执行的是handle_level_irq (irq, desc)。
我们进入handle_level_irq(irq, desc)可靠desc->handle_irq(irq, description)做了哪些操作:

void handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
    mask_ack_irq(desc, irq); //屏蔽中断
    ......
    action = desc->action;
    action_ret = handle_IRQ_event(irq, action);
    ......
        desc->chip->unmask(irq); //打开中断
}
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)                                                                
{
    ......
    do {
        ret = action->handler(irq, action->dev_id);//指向我们注册的中断处理函数
        ......
    } while (action);
    .....                                                                                                      
}
  action->handler中断处理函数是通过irq申请函数 can_request_irq/request_irq 注册的。

回到do_IRQ

最后执行了 irq_exit(); 
void irq_exit(void)
{
    account_system_vtime(current);
    trace_hardirq_exit();
    sub_preempt_count(IRQ_EXIT_OFFSET);
    if (!in_interrupt() && local_softirq_pending())
        invoke_softirq();  //调用软irq处理                                                                                              

#ifdef CONFIG_NO_HZ
    /* Make sure that timer wheel updates are propagated */
    rcu_irq_exit();
    if (idle_cpu(smp_processor_id()) && !in_interrupt() && !need_resched())
        tick_nohz_stop_sched_tick(0);
#endif
    preempt_enable_no_resched();
}



你可能感兴趣的:(Linux网络内核协议栈,Linux,中断,内核)