中断处理过程

1.  在entry_32.S 里面,会建立一个interrupt数组
    interrupt数组,是一个函数指针数组。数组的每个元素都指向一个函数
   其实呢,数组的每个元素,指向的是一段代码。
   这段代码的功能为:
          push   $0x5f   //这个其实就是将中断向量号压入
jmp    3fa <irq_entries_start+0x1a>
而3fa其实也是一个跳转,跳转到由common_interrupt进行处理。
jmp    7e0 <common_interrupt>
    当然,每个数组的内容指向的地址不一样,但是,指向的地址的内容跟上面的类似。
    
2.  interrupt数组大概有32*7=224个。
3. interrupt数组的初始化比较有意思,是用汇编来写的。 编译的时候,初始化
    
初始化代码如下:
.section .init.rodata,"a"
ENTRY(interrupt)
.text
     .p2align 5
     .p2align CONFIG_X86_L1_CACHE_SHIFT
ENTRY(irq_entries_start)
     RING0_INT_FRAME
vector=FIRST_EXTERNAL_VECTOR=0x20=32
.rept (NR_VECTORS-FIRST_EXTERNAL_VECTOR+6)/7 ; 重复32次, 即gcc重复生成代码32次
     .balign 32
  .rept     7  ; 重复7次,这样,其实一种重复32×7=224次
    .if vector < NR_VECTORS
      .if vector <> FIRST_EXTERNAL_VECTOR  
     CFI_ADJUST_CFA_OFFSET -4
      .endif
1:     pushl $(~vector+0x80)  
   /* Note: always in signed byte range */   ; 这一行也会生成224次,但是push的内容在改。 标号1的地址也会改。
     CFI_ADJUST_CFA_OFFSET 4
      .if ((vector-FIRST_EXTERNAL_VECTOR)%7) <> 6   ; 上面的7次循环中,0-5次都有这行代码,但是第6次没有
                                                                                ; 为什么第6次没有? 原因很简单,前0-5次,
                                                                                ;  都需要jmp 2f,2f所指向的在jmp到common_interrupt进行处理
                                                                                ;  而第6次,生成完pushl $(~vector+80) 之后,接下来正好就是 2: jmp common_interrupt 语句了
                                                                                ;  当然不要生成一个浪费的jmp了。
     jmp 2f    ; 跳转到标号2
      .endif
      .previous  ; 把下面的内容放到
.init.rodata,"a", 即填到数组里面。 数组当让在数据段了。

     .long 1b    ; 把标号1的地址放到interrupt数组里。注意, interrupt里面,只有标号1的地址,当然由于循环的原因,标号1的地址在变
                    
      .text
vector=vector+1
    .endif
  .endr
2:     jmp common_interrupt
.endr
END(irq_entries_start)
.previous
END(interrupt)
.previous

关于这段代码的更详细的分析,可以参见:


4. 看看entry_32.S 由entry_32.O objump -dS 出来的代码;
3e0:     6a 5f                     push   $0x5f                                         ; 第0次, 也是标号1的地址
3e2:     eb 16                     jmp    3fa <irq_entries_start+0x1a>      ; jmp 到标号2
3e4:     6a 5e                     push   $0x5e                                        ; 第2次,也是标号1的地址
3e6:     eb 12                     jmp    3fa <irq_entries_start+0x1a>
3e8:     6a 5d                     push   $0x5d
3ea:     eb 0e                     jmp    3fa <irq_entries_start+0x1a>
3ec:     6a 5c                     push   $0x5c
3ee:     eb 0a                     jmp    3fa <irq_entries_start+0x1a>
3f0:     6a 5b                     push   $0x5b
3f2:     eb 06                     jmp    3fa <irq_entries_start+0x1a>
3f4:     6a 5a                     push   $0x5a
3f6:     eb 02                     jmp    3fa <irq_entries_start+0x1a>   
3f8:     6a 59                     push   $0x59                                       ; 第6次,也是标号1的地址
3fa:     e9 e1 03 00 00            jmp    7e0 <common_interrupt>      ; 这里就是标号2啦,所以上面的不需要象0-6那样,生成jmp 2f啦。

5. 总结:
   上面的代码,编译后的结果为:
  •    interrupt[224]为一个函数指针数组,
  •    interrupt[]的每个元素,指向位于irq_entries_start开始一段段代码,每段代码,都会执行push,然后跳转执行common_interrupt.

6. 上面的代码可以看到,标号1里面,push的时候,是一个负值。
    为什么是负值? 

7.  IRQ分配到外部设备  
-------------------------------------------------------- 
但为了兼容IBM PC体系,必须将以下设备静态的连接到指定的IRQ线: 
(1)间隔定时设备必须连接到IRQ0线; 即时间中断必须连到IRQ0,而IRQ0对应的中断向量号为0x20 = 32.
        即intel 8253/8254的输出管脚必须8259A的0号输入管脚
(2)8259A必须连接到IRQ2线; 
(3)外部数学协处理器必须连接到IRQ13线; 
(4)一个I/O设备可以连接到多个IRQ线; 


8. 时钟中断的处理过程
   1. 注册
       1. 在start_kernel函数中,调用late_time_init函数。 late_time_init其实被初始化为函数指针 x86_late_time_init
       2. x86_late_time_init 函数里面又会调用hpet_time_init函数
       3. hpet_time_init里面则会调用setup_irq(irq=0, act=&irq0)
       3. setup_irq函数里面,关键的是desc->action=&irq0, 这里很重要,即将irq0 查到desc的action 链表里面了。
   2. 处理
       1. 处理过程跟基本的interrupt的过程一样。
       2. 直接根据中断向量跳转到common_interrupt处进行处理。
       3.  在common_interrupt里面,先调用do_IRQ, 然后执行ret_from_intr函数。
       4. 在do_IRQ函数中,则
             1. 先调用handle_irq函数。
             2. 然后调用irq_exit函数, 这个函数很重要,softirq就是在这里进行处理的。
       5. handle_irq函数里面呢,调用desc->handle_irq(irq, desc)函数。
       6. desc->handle_irq函数指针在start_kernel里面调用init_IRQ函数时,前面16个desc->handle_irq = handle_level_irq
            即水平触发
       7. 而  handle_level_irq函数里面,关键是调用 action_ret = handle_IRQ_event(irq, action);函数。
       8.  handle_IRQ_event函数里面呢, 则又调用action->handler(irq, action->dev_id);来进行处理。
       9. 关键来了,处理的函数为action->handler(irq, action->dev_id);
       10. 前面我们说了, action=irq0, irq0的handler函数指针被初始化为timer_interrupt
       11. 所以呢, 时钟中断的处理函数是timer_interrupt
       12. timer_interrupt函数里面呢,执行了global_clock_event->event_handler(global_clock_event);函数。
       13. global_clock_event->event_handler这个就比较特别了, 根据当前的时钟发生器来进行处理的。
             即当时钟发生器是8253和APIC的时候,处理函数是不一样的。
       14. 当时钟发生器是8253的时候,global_clock_event->event_handler = tick_handle_periodic
       15. 这函数里面呢,
    3. 中断返回。
       1. 从上面的第3步可以看出,当执行完中断处理程序之后,会执行ret_from_intr函数。
      
  9. 软中断的处理过程 
     1. 前面的2.4里面提到,在do_IRQ函数里面,调用中断处理函数之后,会调用irq_exit函数。
     2. irq_exit里面呢,执行了下面的语句
         if (!in_interrupt() && local_softirq_pending())
          invoke_softirq();
          这里in_interrupt()指,只要在硬中断,软中断,NMI里面,都算在中断里面, 就不再执行了。
          也就说, 如果现在正在处理软中断,然后来硬中断了,然后又在irq_exit里面,再次进行判断, 就直接退出了。
          就是这个软中断处理函数, 不会被多次进入。
     3. 即执行了invoke_softirq()函数。
         这里invoke_softirq是个宏, 如果说, 在CPU Arch上面IRQ退出的时候,Irq是disable的,则直接等同于 __do_softirq
         而如果是enable的,则调用do_softirq, 这个函数里面呢,则先关中断,然后调用__do_softirq, 然后在开中断。
     4. 由此可见, 在__do_softirq里面, 进来之前,中断是被关了的。
     5. __do_softirq函数里面呢, 
       0. 先local_irq_enable, 也就是说, 在执行软中断对应的处理函数的时候, 中断是打开的。
       1. 循环执行挂在softirq_vector里面的函数
               trace_softirq_entry(h, softirq_vec);
               h->action(h);
               trace_softirq_exit(h, softirq_vec);
       2. 做完上面的循环之后呢, 尝试若干次(10次,代码里面定义好)之后,则如果还有软中断没有处理,
             调用wakeup_softirqd函数来处理。
       3. 然后呢,接下来
     6. wakeup_softirqd函数里面,调用了wake_up_process(tsk);函数。
     7. 
   10. 软中断的处理
     1. 除了上面所说的在中断返回的时候, 在irq_exit里面,会处理软中断
     2. 同时呢, 在ksoftirqd daemon线程里面, 也会定期来处理。
     3. 但是在执行软中断处理函数之前,已经打开中断了(如5.0所示)
     4. 中断处理函数(也包含软中断)里面是不能睡眠的,这里只是中断处理例程,睡眠了, 谁来调度再次执行呀。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值