Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2000330
  • 博文数量: 369
  • 博客积分: 10093
  • 博客等级: 上将
  • 技术积分: 4271
  • 用 户 组: 普通用户
  • 注册时间: 2005-03-21 00:59
文章分类

全部博文(369)

文章存档

2013年(1)

2011年(2)

2010年(10)

2009年(16)

2008年(33)

2007年(146)

2006年(160)

2005年(1)

分类: LINUX

2006-08-04 17:24:29

File:arch/i386/kernel/entry.S
401 /*
402  * Build the entry stubs and pointer table with
403  * some assembler magic.
404  */
405 .data
406 ENTRY(interrupt)
407 .text
408
409 vector=0
410 ENTRY(irq_entries_start)
411 .rept NR_IRQS
412         ALIGN
413 1:      pushl $vector-256
414         jmp common_interrupt
415 .data
416         .long 1b
417 .text
418 vector=vector+1
419 .endr
420
421         ALIGN
422 common_interrupt:
423         SAVE_ALL
424         movl %esp,%eax
保存现场
425         call do_IRQ
调用中断处理函数
File:arch/i386/kernel/irq.c
49 /*
 50  * do_IRQ handles all normal device IRQ's (the special
 51  * SMP cross-CPU interrupts have their own specific
 52  * handlers).
 53  */
 54 fastcall unsigned int do_IRQ(struct pt_regs *regs)
 55 {
 56         /* high bits used in ret_from_ code */
 57         int irq = regs->orig_eax & 0xff;
 58 #ifdef CONFIG_4KSTACKS
 59         union irq_ctx *curctx, *irqctx;
 60         u32 *isp;
 61 #endif
 62
 63         irq_enter();
File:include/linux/hardirq.h
101 #define irq_enter()                                     \
102         do {                                            \
103                 account_system_vtime(current);          \
统计系统时间
104                 add_preempt_count(HARDIRQ_OFFSET);      \
将抢占计数器加上HARDIRQ_OFFSET,避免抢占
105         } while (0)
File:arch/i386/kernel/irq.c
 64 #ifdef CONFIG_DEBUG_STACKOVERFLOW
 65         /* Debugging check for stack overflow: is there less than 1KB free? */
 66         {
 67                 long esp;
 68
 69                 __asm__ __volatile__("andl %%esp,%0" :
 70                                         "=r" (esp) : "0" (THREAD_SIZE - 1));
 71                 if (unlikely(esp < (sizeof(struct thread_info) + STACK_WARN))) {
 72                         printk("do_IRQ: stack overflow: %ld\n",
 73                                 esp - sizeof(struct thread_info));
 74                         dump_stack();
 75                 }
 76         }
 77 #endif
 78
 79 #ifdef CONFIG_4KSTACKS
 80
 81         curctx = (union irq_ctx *) current_thread_info();
 82         irqctx = hardirq_ctx[smp_processor_id()];
 83
 84         /*
 85          * this is where we switch to the IRQ stack. However, if we are
 86          * already using the IRQ stack (because we interrupted a hardirq
 87          * handler) we can't do that and just have to keep using the
 88          * current stack (which is the irq stack already after all)
 89          */
 90         if (curctx != irqctx) {
 91                 int arg1, arg2, ebx;
 92
 93                 /* build the stack frame on the IRQ stack */
 94                 isp = (u32*) ((char*)irqctx + sizeof(*irqctx));
 95                 irqctx->tinfo.task = curctx->tinfo.task;
 96                 irqctx->tinfo.previous_esp = current_stack_pointer;
 97
 98                 asm volatile(
 99                         "       xchgl   %%ebx,%%esp      \n"
100                         "       call    __do_IRQ         \n"
101                         "       movl   %%ebx,%%esp      \n"
102                         : "=a" (arg1), "=d" (arg2), "=b" (ebx)
103                         :  "0" (irq),   "1" (regs),  "2" (isp)
104                         : "memory", "cc", "ecx"
105                 );
106         } else
107 #endif
108                 __do_IRQ(irq, regs);
为了简答起见,不分析DEBUG相关代码和4k堆栈的代码
File:kernel/irq/handle.c
102 /*
103  * do_IRQ handles all normal device IRQ's (the special
104  * SMP cross-CPU interrupts have their own specific
105  * handlers).
106  */
107 fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs)
108 {
109         irq_desc_t *desc = irq_desc + irq;
110         struct irqaction * action;
111         unsigned int status;
112
113         kstat_this_cpu.irqs[irq]++;
114         if (CHECK_IRQ_PER_CPU(desc->status)) {
115                 irqreturn_t action_ret;
116
117                 /*
118                  * No locking required for CPU-local interrupts:
119                  */
120                 if (desc->handler->ack)
121                         desc->handler->ack(irq);
122                 action_ret = handle_IRQ_event(irq, regs, desc->action);
123                 desc->handler->end(irq);
124                 return 1;
125         }
如果有针对某个特定CPU的中断,则直接调用相应的中断处理函数。
126
127         spin_lock(&desc->lock);
因为在SMP系统上,可能有另外的CPU响应同一个中断源的中断,所以在访问此变量时需要自旋锁
128         if (desc->handler->ack)
129                 desc->handler->ack(irq);
如果此中断需要回应,则调用回应处理函数
130         /*
131          * REPLAY is when Linux resends an IRQ that was dropped earlier
132          * WAITING is used by probe to mark irqs that are being tested
133          */
134         status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);
135         status |= IRQ_PENDING; /* we _want_ to handle it */
设置status的IRQ_PENDING位表示当前处理函数要处理它
136
137         /*
138          * If the IRQ is disabled for whatever reason, we cannot
139          * use the action we have.
140          */
141         action = NULL;
142         if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) {
143                 action = desc->action;
144                 status &= ~IRQ_PENDING; /* we commit to handling */
145                 status |= IRQ_INPROGRESS; /* we are handling it */
如果这个中断处理函数并没有正在执行,或者被禁止,则将status状态更新到IRQ_INPROGRESS,表示我们正在处理此次中断
146         }
147         desc->status = status;
更新当前中断描述符的状态
148
149         /*
150          * If there is no IRQ handler or it was disabled, exit early.
151          * Since we set PENDING, if another processor is handling
152          * a different instance of this same irq, the other processor
153          * will take care of it.
154          */
155         if (unlikely(!action))
156                 goto out;
如果这个中断没有处理函数,或者是中断处理被禁止了,或者是已经有一个此中断源引发的响应,则直接退出。
注意:如果此中断并不是没有中断处理函数,它的中断描述符的状态的IRQ_PENDING位将被设置,那么在上次中断返回之后,如果发现在它响应中断期间,又有新的中断发生,则直接响应这个嵌套了的中断。
157
158         /*
159          * Edge triggered interrupts need to remember
160          * pending events.
161          * This applies to any hw interrupts that allow a second
162          * instance of the same irq to arrive while we are in do_IRQ
163          * or in the handler. But the code here only handles the _second_
164          * instance of the irq, not the third or fourth. So it is mostly
165          * useful for irq hardware that does not mask cleanly in an
166          * SMP environment.
167          */
168         for (;;) {
169                 irqreturn_t action_ret;
170
171                 spin_unlock(&desc->lock);
与127行呼应,释放自旋锁。在SMP系统中,如果有一个中断阻塞在127行,那么此锁释放掉后,那个CPU就可以设置这个中断描述符的IRQ_PENDING位,这个CPU就可以真正响应这个中断了。
172
173                 action_ret = handle_IRQ_event(irq, regs, action);
174
File:kernel/irq/handle.c
76 /*
 77  * Have got an event to handle:
 78  */
 79 fastcall int handle_IRQ_event(unsigned int irq, struct pt_regs *regs,
 80                                 struct irqaction *action)
 81 {
 82         int ret, retval = 0, status = 0;
 83
 84         if (!(action->flags & SA_INTERRUPT))
 85                 local_irq_enable();
如果这个中断处理函数可以在开中断的环境下运行,那么将打开此CPU上的中断,因为在OS响应中断的时候,已经是把中断关闭了的。其实,打开这个中断也是无关痛痒的,因为对于同一类中断即使有表象上的中断嵌套,实际上他们之间还是简单的串行的,尽量避免开中断处理中断的建议只是对于中断间共享变量的情况的,可是实际上此类情况,即使关闭中断那么共享变量也是需要用spin_lock保护的。
 86
 87         do {
 88                 ret = action->handler(irq, action->dev_id, regs);
 89                 if (ret == IRQ_HANDLED)
 90                         status |= action->flags;
 91                 retval |= ret;
 92                 action = action->next;
 93         } while (action);
 94
 95         if (status & SA_SAMPLE_RANDOM)
 96                 add_interrupt_randomness(irq);
 97         local_irq_disable();
 98
 99         return retval;
100 }
File:kernel/irq/handle.c
175                 spin_lock(&desc->lock);
176                 if (!noirqdebug)
177                         note_interrupt(irq, desc, action_ret, regs);
178                 if (likely(!(desc->status & IRQ_PENDING)))
179                         break;
如果在这个中断处理函数运行期间,又有新的中断抵达,那么这个CPU继续处理这个中断。(这个CPU真够累的,还好这种情况并不是很多)
180                 desc->status &= ~IRQ_PENDING;
清除IRQ_PENDING位,因为只有一位,所以如果在中断处理期间又到达了多于一个的此类中断,那么也只有一个将被响应,如果中断不是很频繁,中断处理函数不是很耗时,就一切都还OK。另外,在极端情况下,有可能不停有中断抵达,那么这个OS就只能不停的响应中断,那么... ...
181         }
182         desc->status &= ~IRQ_INPROGRESS;
因为中断处理已经完成,所以将IRQ_INPROGRESS位清除
183
184 out:
185         /*
186          * The ->end() handler has to deal with interrupts which got
187          * disabled while the handler was running.
188          */
189         desc->handler->end(irq);
收尾
190         spin_unlock(&desc->lock);
因为在168行的for循环中是带锁退出的,所以不要忘了释放锁
191
192         return 1;
193 }
194
File:kernel/softirq.c
162 /*
163  * Exit an interrupt context. Process softirqs if needed and possible:
164  */
165 void irq_exit(void)
166 {
167         account_system_vtime(current);
168         sub_preempt_count(IRQ_EXIT_OFFSET);
将抢占计数器减去IRQ_EXIT_OFFSET,其实减去的数比开头加上的数少一,所以如果不是中断嵌套,
在下面的in_interrupt()判断中将返回否
169         if (!in_interrupt() && local_softirq_pending())
170                 invoke_softirq();
如果有软中断需要响应,则响应之
171         preempt_enable_no_resched();
完全恢复中断前的抢占计数器
172 }
File:arch/i386/kernel/entry.S
426         jmp ret_from_intr
跳转到ret_from_intr继续执行,有关这个的分析就参见上篇关于抢占代码分析的文章吧!
阅读(2925) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~