• linux内核中断进入过程分析笔记


    说明:linux内核版本:linux-3.4

     arch/arm/kernel/entry-armv.s中:

    1. __irq_usr:
    2. /*下面两个宏分别进行栈的准备*/
    3. usr_entry
    4. kuser_cmpxchg_check
    5. irq_handler /*进一步处理中断*/
    6. get_thread_info tsk
    7. mov why, #0
    8. b ret_to_user_from_irq
    9. UNWIND(.fnend )
    10. ENDPROC(__irq_usr)
    11. ....
    12. __irq_svc:
    13. svc_entry /*进行栈准备*/
    14. irq_handler /*进一步处理中断*/

            汇编代码会根据中断发生前的模式最终调用__irq_usr 或 __irq_svc;分别对应了中断是打断了应用层的代码还是内核层的代码;

             上面知道了调用了irq_handle; 而irq_handlehandle_arch_irq相关联;

    1. /*arch\arm\include\asm\mach/irq.h中定义函数指针*/
    2. #ifdef CONFIG_MULTI_IRQ_HANDLER
    3. extern void (*handle_arch_irq)(struct pt_regs *);
    4. #endif
    1. /*在arch\arm\kernel\setup.c中有定义*/
    2. setup_arch
    3. {
    4. mdesc = setup_machine_fdt(__atags_pointer); ->调用了
    5. for_each_machine_desc(mdesc)
    6. #ifdef CONFIG_MULTI_IRQ_HANDLER
    7. handle_arch_irq = mdesc->handle_irq;
    8. #endif
    9. }

    for_each_machine_desc函数定义在arch\arm\include\asm\mach\arch.h中;内容如下:

    1. extern struct machine_desc __arch_info_begin[], __arch_info_end[];
    2. #define for_each_machine_desc(p) \
    3. for (p = __arch_info_begin; p < __arch_info_end; p++)
    4. /*
    5. * Set of macros to define architecture features. This is built into
    6. * a table by the linker.
    7. 用于定义体系结构特征的宏集。链接器将其内置到表中。
    8. */
    9. #define MACHINE_START(_type,_name) \
    10. static const struct machine_desc __mach_desc_##_type \
    11. __used \
    12. __attribute__((__section__(".arch.info.init"))) = { \
    13. .nr = MACH_TYPE_##_type, \
    14. .name = _name,
    15. #define MACHINE_END \
    16. };

    其中的MACHINE_START宏很关键;不同开发板通过该宏将对应的handle_irq赋值后添加到表中;通过for_each_machine_desc查找后返回,并最终回调给handle_irq赋值的函数;

    如在arch\arm\mach-hi3516a\core.c中:

    1. MACHINE_START(HI3516A, "hi3516a")
    2. .atag_offset = 0x100,
    3. .map_io = hi3516a_map_io,
    4. .init_early = hi3516a_init_early,
    5. .init_irq = hi3516a_gic_init_irq,
    6. .handle_irq = gic_handle_irq,
    7. .timer = &hi3516a_sys_timer,
    8. .init_machine = hi3516a_init,
    9. .reserve = hi3516a_reserve,
    10. .restart = hi3516a_restart,
    11. MACHINE_END

    handle_irq被赋值为gic_handle_irq;

    又列如在arch\arm\mach-hi3518ev200中,被赋值为vic_handle_irq;GIC与VIC的区别自行百度;

    1. MACHINE_START(HI3518EV200, "hi3518ev200")
    2. .atag_offset = 0x100,
    3. .map_io = hi3518ev200_map_io,
    4. .init_early = hi3518ev200_init_early,
    5. .init_irq = hi3518ev200_vic_init_irq,
    6. .handle_irq = vic_handle_irq,
    7. .timer = &hi3518ev200_sys_timer,
    8. .init_machine = hi3518ev200_init,
    9. .reserve = hi3518ev200_reserve,
    10. .restart = hi3518ev200_restart,
    11. MACHINE_END

    以通用的gic_handle_irq函数分析: 源码如下:

    1. /*arch\arm\common\gic.c中*/
    2. asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
    3. {
    4. u32 irqstat, irqnr;
    5. struct gic_chip_data *gic = &gic_data[0];
    6. void __iomem *cpu_base = gic_data_cpu_base(gic);
    7. do {
    8. //得到硬件中断线的IRQ号
    9. irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
    10. irqnr = irqstat & ~0x1c00;
    11. if (likely(irqnr > 15 && irqnr < 1021)) {
    12. //转换为内核内部的IRQ号;
    13. irqnr = irq_find_mapping(gic->domain, irqnr);
    14. //处理中断
    15. handle_IRQ(irqnr, regs);
    16. continue;
    17. }
    18. if (irqnr < 16) {
    19. writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
    20. #ifdef CONFIG_SMP
    21. handle_IPI(irqnr, regs);
    22. #endif
    23. continue;
    24. }
    25. break;
    26. } while (1);
    27. }

    handle_IRQ函数中调用了irq_enter()函数对内核抢占计数器值进行了操作;然后又调用了generic_handle_irq函数;

    1. // 在arch\arm\kernel\irq.c中;
    2. void handle_IRQ(unsigned int irq, struct pt_regs *regs)
    3. {
    4. struct pt_regs *old_regs = set_irq_regs(regs);
    5. irq_enter();
    6. /*
    7. * Some hardware gives randomly wrong interrupts. Rather
    8. * than crashing, do something sensible.
    9. */
    10. if (unlikely(irq >= nr_irqs)) {
    11. if (printk_ratelimit())
    12. printk(KERN_WARNING "Bad IRQ%u\n", irq);
    13. ack_bad_irq(irq);
    14. } else {
    15. generic_handle_irq(irq);//中断上半部处理完,即该函数返回;
    16. }
    17. /* AT91 specific workaround */
    18. irq_finish(irq);
    19. irq_exit();
    20. set_irq_regs(old_regs);
    21. }

    函数generic_handle_irq中,有一个结构体类型struct irq_desc 是对系统中的单个中断的抽象;系统有多少个中断源就应该有多少个这样的对象;这些对象可能存在数组中或基数树中,并且是在内核启动过程中构建好的;

    1. int generic_handle_irq(unsigned int irq)
    2. {
    3. struct irq_desc *desc = irq_to_desc(irq); //通过中断号获取中断描述结构体
    4. if (!desc)
    5. return -EINVAL;
    6. generic_handle_irq_desc(irq, desc);
    7. return 0;
    8. }

    generic_handle_irq_desc函数中继续调用desc->handle_irq(irq, desc);

     handle_irq函数根据中断的触发类型是电平还是边沿,对应调用kernel\irq\chip.c中的函数handle_level_irq 或 handle_edge_irq 函数;

    这两个函数都调用了 handle_irq_event(desc);  在该函数中又调用了kernel\irq\handle.c下的 handle_irq_event_percpu(desc, action); 函数,通过do while循环遍历链表中的action, 并调用action->handler(irq, action->dev_id)而action中的handler所指向的函数指针指向驱动开发者编写的中断处理函数的指针

     其中的action成员为struct irqaction结构类型,定义如下:

    1. struct irqaction {
    2. irq_handler_t handler; //指向驱动开发者编写的中断处理函数的指针;
    3. unsigned long flags;//IRQF_*开头的一组标志;
    4. void *dev_id; //区别共享中断中的不同设备的ID
    5. void __percpu *percpu_dev_id;
    6. struct irqaction *next;//将共享同一IRQ号的struct irqaction对象链接在一起的指针;
    7. int irq; //中断号
    8. irq_handler_t thread_fn;//线程中断的中断能处理程序函数;
    9. struct task_struct *thread;
    10. unsigned long thread_flags;
    11. unsigned long thread_mask;
    12. const char *name;//设备名
    13. struct proc_dir_entry *dir;
    14. } ____cacheline_internodealigned_in_smp;

    共享中断: 由于中断控制器管脚有限,某些体系结构上有两个或两个以上的设备被接到同一根中断线上,这样这两个设备中的任何一个设备产生了中断;都会触发;

    linux内核不能判断新产生的中断属于哪个设备,于是将一根中断线上的中断处理函数通过不同的struct irqaction结构体包装起来,然后通过链表连接;内核得到IRQ号后,就遍历该链表上的每一个struct irqaction结构体对象,找到后调用其handle成员所指向的中断处理函数;

  • 相关阅读:
    端到端拥塞控制的本质
    基础篇010.2 STM32驱动RC522 RFID模块之二:STM32硬件SPI驱动RC522
    【单目3D目标检测】GUPNet论文精读与代码解析
    PyCharm改变代码背景图片的使用教程
    缓存击穿、穿透、雪崩和布隆过滤器
    【跟小嘉学 Rust 编程】二十六、Rust的序列化解决方案(Serde)
    LeetCode50天刷题计划(Day 11—— 最接近的三数之和(8.40-10.00)
    CUDA学习笔记(十四) Constant Memory
    12 分布式锁加入看门狗
    入门力扣自学笔记108 C++ (题目编号952)
  • 原文地址:https://blog.csdn.net/qq_39048131/article/details/126111052