再次梳理会用到的一些数据结构和名词
中断向量表(中断描述符表) |
idt_table |
全局,8字节64位,从低到高位16位段选择符,32位偏移量,16位状态信息 |
256项 |
起始地址在内核数据节的idt中 用于寻找各种门,门的作用是防止用户程序访问陷阱门、中断门等特殊资源,出于安全考虑,linux为用户留有3,4,5,128号系统调用门供用户使用 |
中断描述符表寄存器 |
IDTR |
寄存器,6字节48位,低16位界限,高32位基址 |
1项 |
用于快速寻找中断描述符,linux定义的16位界限为8*256-1,即使用了2048字节来存储中断向量表;32位基址就是idt地址,整个48位内容在内存的位置为idt地址-2 |
中断服务程序数组 |
Irq_desc |
全局,20字节160位,5个unsigned int |
224项 |
中断线状态status,中断控制器描述符hw_irq_controller和中断服务例程链表irqaction,中断嵌套depth和多cpu锁lock,通过这些信息可以对中断线进行管理 |
中断服务例程描述符 |
irqaction |
结构体,24字节,6个32位 |
用于挂载到中断线服务例程链表上的结构体,真正的对中断进行个性化处理,也是设备驱动程序主要实现内容 |
|
现场寄存器结构 |
pt_regs |
函数参数,60字节,15个32位 |
通常作为中断或异常处理程序的参数,这些函数前面通常有asmlinkage,通过栈传参数,即第一个参数就是ESP |
《深入分析linux内核》一书74页对request_irq()函数解释为“将对应的中断服务例程挂入中断请求队列”,我产生了一些误解,个人认为解释为“请求将中断服务例程挂入中断服务队列(单链表)”更好理解一点,因为内核初始化挂载中断服务例程都是直接操作断服务例程的指针的,服务例程在内核而且肯定存在内存,所以不用管这些,但其他设备初始化的时候,挂载操作就需要严格要求,比如全局唯一div_id,中断号大小,申请空间存储中断服务例程指针等都需要确认,所以需要先请求,这也就是对挂载操作封装一层request_irq()的原因吧,所以只留一个中断服务队列的说法,请求队列容易误解。
接下来就说说do_irq()
asmlinkage unsigned int do_IRQ(struct pt_regs regs)
{
/* |
|
* We ack quickly, we don‘t want the irq controller |
|
* thinking we‘re snobs just because some other CPU has |
|
* disabled global interrupts (we have already done the |
|
* INT_ACK cycles, it‘s too late to try to pretend to the |
|
* controller that we aren‘t taking the interrupt). |
|
* |
|
* 0 return value means that this irq is already being |
|
* handled by some other CPU. (or is disabled) |
|
*/ |
|
int irq = regs.orig_eax & 0xff; |
/* high bits used in ret_from_ code */因为之前压栈压的irq-256,所以这里需要& 0xff来恢复,真是神奇,&可以这样用,irq在0-223之间 |
int cpu = smp_processor_id(); |
获取cpu号 |
irq_desc_t *desc = irq_desc + irq; |
获取中断服务描述符(中断服务程序)的入口,不是中断服务例程描述符哦 |
struct irqaction * action; |
|
unsigned int status; |
|
kstat.irqs[cpu][irq]++; |
记录中断请求次数 |
spin_lock(&desc->lock); |
锁cpu,中断设计成单cpu不可重入,因而一条中断线在一个时间点只能由一个cpu来处理 |
desc->handler->ack(irq); |
对中断请求给予确认,也不知道确认什么,8259A控制器里面都没这个函数。。 |
/* |
|
REPLAY is when Linux resends an IRQ that was dropped earlier |
|
WAITING is used by probe to mark irqs that are being tested |
|
*/ |
|
status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); |
设置状态为未删除,非探测,因为是由硬件真正发出的中断请求 |
status |= IRQ_PENDING; |
/* we _want_ to handle it */接受中断,并准备处理 |
/* |
|
* If the IRQ is disabled for whatever reason, we cannot |
|
* use the action we have. |
|
*/ |
|
action = NULL; |
中断服务例程队列(单链表)初始为空 |
if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { |
如果不是中断线被禁用或者有中断服务例程正在处理 |
action = desc->action; |
获得中断服务例程队列 |
status &= ~IRQ_PENDING; |
/* we commit to handling */提交处理 |
status |= IRQ_INPROGRESS; |
/* we are handling it */开始处理 |
} |
|
desc->status = status; |
更新中断线状态 |
/* |
|
* If there is no IRQ handler or it was disabled, exit early. |
|
Since we set PENDING, if another processor is handling |
|
a different instance of this same irq, the other processor |
|
will take care of it. |
|
*/ |
|
if (!action) |
如果获取中断服务队列失败 |
goto out; |
退出 |
/* |
|
* Edge triggered interrupts need to remember |
|
* pending events. |
|
* This applies to any hw interrupts that allow a second |
|
* instance of the same irq to arrive while we are in do_IRQ |
|
* or in the handler. But the code here only handles the _second_ |
|
* instance of the irq, not the third or fourth. So it is mostly |
|
* useful for irq hardware that does not mask cleanly in an |
|
* SMP environment. |
|
*/ |
|
for (;;) { |
循环 |
spin_unlock(&desc->lock); |
释放多cpu锁,但此时中断线状态是IRQ_INPROGRESS,所以即使其他cpu无法获取当前中断线的中断服务例程队列,而且此时应该是关中断状态吧,这里有些疑问 |
handle_IRQ_event(irq, ®s, action); |
中断服务例程队列对中断进行处理,就是让队列里的每个例程都去试着处理一下 |
spin_lock(&desc->lock); |
获取多cpu锁,由于上面的中断服务例程处理会先关中断,然后再开中断,就有可能在关中断后获取锁之前再发生一次中断,也就是中断嵌套,虽然这应该尽量避免。但不理解的是即使这条中断线再次发生中断,也因为status为IRQ_INPROCESS而无法设置status为IRQ_PENDING啊,难道是怕驱动程序设置这个status?反正操作系统为我们考虑很全面了,有嵌套也能在这儿串行执行 |
if (!(desc->status & IRQ_PENDING)) |
再检测中断线是否有中断请求,如果没有嵌套 |
break; |
退出 |
desc->status &= ~IRQ_PENDING; |
提交请求 |
} |
再次处理中断 |
desc->status &= ~IRQ_INPROGRESS; |
恢复中断线为没有中断例程正在服务状态 |
out:
/* |
|
* The ->end() handler has to deal with interrupts which got |
中断控制器的end()函数需要处理自身被打 |
* disabled while the handler was running. |
断的特殊情形 |
*/ |
|
desc->handler->end(irq); |
不明,根据标志位是否启用中断线吧? |
spin_unlock(&desc->lock); |
释放多cpu锁 |
if (softirq_pending(cpu)) |
不明,64位cpu才有的数据结构 |
do_softirq(); |
处理软中断 |
return 1; |
}
时间: 2024-10-01 06:42:17