如何从IDT表中打印IRQ处理函数的实际名称?
如何从IDT表地址获取实际中断处理函数名
你通过内核模块打印IDT表时,发现Handler Name显示的是irq_entries_start+0x10/0x660这类通用中断存根地址,而非实际的设备驱动处理函数(比如NVMe的nvme_irq)。这是因为Linux内核的中断处理采用分层设计,IDT直接指向的是通用中断入口存根,而非最终的设备handler。
核心原因
Linux内核x86架构中,中断向量首先会进入通用中断存根(irq_entries_start系列),这些存根负责上下文保存、调用中断分发逻辑(如handle_irq),最终通过struct irq_desc结构体找到绑定到该IRQ的实际设备处理函数。因此IDT表中直接存储的是存根地址,而非设备handler的地址。
解决步骤
要获取实际的中断处理函数名,需要将IRQ编号关联到内核的irq_desc结构体,从中提取绑定的irqaction链表:
获取IRQ对应的
irq_desc结构体
使用内核函数irq_to_desc(unsigned int irq),传入IRQ编号即可得到对应的描述符结构体。遍历
irqaction链表获取实际handlerirq_desc中的action成员是一个链表,每个节点struct irqaction包含:handler:实际中断处理函数的指针name:处理函数的名称字符串next:链表下一个节点
打印实际handler信息
遍历链表时,使用内核打印的%pF格式符可以直接将函数指针解析为符号名(需内核开启符号表支持),同时可以直接读取name字段。
代码示例片段
修改你的内核模块,在打印IDT信息的同时,添加以下逻辑获取实际handler:
#include <linux/interrupt.h> #include <linux/spinlock.h> // 假设你已经遍历到对应的irq_num struct irq_desc *desc = irq_to_desc(irq_num); if (desc) { struct irqaction *action; // 加锁保护irq_desc的并发访问 raw_spin_lock_irq(&desc->lock); action = desc->action; while (action) { printk(KERN_INFO "IRQ %d: Actual Handler: %pF (Name: %s)\n", irq_num, action->handler, action->name); action = action->next; } raw_spin_unlock_irq(&desc->lock); }
注意事项
- 内核版本适配:不同内核版本中
irq_desc的结构或辅助函数可能有差异,例如部分版本需使用irq_desc_get_irq_handler等函数,需根据目标内核版本调整。 - 符号表依赖:
%pF解析函数名需要内核开启CONFIG_KALLSYMS配置,若内核未开启,可能只能显示函数地址。 - 并发安全:访问
irq_desc时必须使用raw_spin_lock_irq/raw_spin_unlock_irq保证并发安全,避免竞态问题。
内容的提问来源于stack exchange,提问作者Hansen Idden
相关产品推荐
相关产品推荐

