免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2269 | 回复: 3
打印 上一主题 下一主题

[FreeBSD] [原创]Freebsd22的中断处理过程,有谁看汇编吗? [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2005-12-13 11:58 |只看该作者 |倒序浏览
这是基于Freebsd2_2版本的中断处理过程,到5.x后好象改成线程了,没有了这些古老的代码.写出来的原因是因为不知是否理解的正确,希望大家批评指正,以后我不会看代码了,一方面是太辛苦,另一方面是没从这些知识中看到前(钱)途,虽然不会没饭吃,工作也很稳定,很闲 ,但对家庭来说还是没有帮助,兴趣将转向厚黑学,哈哈.生活就是这样.

_cpl 其初值是全'0xFFFFFFFF' ,all off 屏蔽所有的中断.系统初始化完成后将调用spl0()从而开放所
有中断,此时cpl将是"0".一个中断进入处理之前会置相应的位为'1' ,同时也要把之前的cpl保存,一般是保存在STACK中到doreti再处理,cpl 中0-15位对应8259A的硬件中断,16-31是软中断用.

_ipending是OS的全局变量是interrupt pending 意思是还没处理的中断,其初值是'0',如果低优先级的中断在高优先级中断开放中断(sti)处理时得到响应会在ipending中相应地方置位,不进行中断服务程序的处理就退回到高优先级中断处理过程.

u_int intr_mask[ICU_LEN]  “sets of intrs masked during handling of 1”这是英文注解,对应某一中断的中断屏蔽集,这个变量我是花了不少时间但是还是看不懂,希望有朋友给说说,我只是猜测一个我认为合理的解释.每个硬件中断都应该在ipl中把比它优先级低的中断屏蔽,这样的目的是为了高优先级中断能在适当的时候注意到有低优先级的中断发生了,从而能把低优先级中断挂在ipending中而不至于被低优先级中断,等到高优先级中断结束时低优先级中断得到处理.cpl中从0-31位优先级递增,最高是0,最低是31位对应的中断,硬中断比软中断优先级高,比如中断7的是intr_mask[7]
=0xffffff80,0到6位的优先级比7要高所以是不能屏蔽的,8到31位对应的中断优先级比7低所以会屏蔽.中断7本身也屏蔽,由于中断并不是用完所有的32位,所以为了减少无用的处理,没有和中断联系的位将不会是’1’而是’0’.

中断处理(包括TRAP)一般是通过_doreti退出,在进入doreti前都会在STACK中压入这次中断前的cpl和向量号.
static inthand_t *fastintr[ICU_LEN] = {
        &IDTVEC(fastintr0), &IDTVEC(fastintr1),
        . . .
        &IDTVEC(fastintr14), &IDTVEC(fastintr15)
}

static inthand_t *slowintr[ICU_LEN] = {
        &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3),
        . . .
        &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15)
}

fastintrXX和intrXX在i386isavectors.s 里定义,象时钟中断,硬盘中断采用FAST_INTR.以下是宏定义和解释.

#define FAST_INTR(irq_num, vec_name, enable_icus)
        .text  
        SUPERALIGN_TEXT  
IDTVEC(vec_name)  
        pushl   %eax         
        pushl   %ecx  
        pushl   %edx  
        pushl   %ds        保护现场,快速中断只保护部分现场.
        MAYBE_PUSHL_ES  
        movl    $KDSEL,%eax  
        movl    %ax,%ds     ds选择符指向系统数据段
      MAYBE_MOVW_AX_ES  
        FAKE_MCOUNT((4+ACTUALLY_PUSHED)*4(%esp))  可能是时间片的统计,没细看
        pushl   _intr_unit + (irq_num) * 4  压入中断号做参数
        call    *_intr_handler + (irq_num) * 4  * 调用中断服务处理程序 *
        enable_icus  * 使中断控制器进行工作,如选出高优先级中断,但现在CPU不会响应 *
        addl    $4,%esp  恢复STACK
        incl    _cnt+V_INTR    * 应该是系统的中断统计 *
        movl    _intr_countp + (irq_num) * 4,%eax  *针对某中断的统计*
        incl    (%eax)  
        movl    _cpl,%eax      
        notl    %eax  
        andl    _ipending,%eax  这个比较的意思是看是否有没被处理的中断
     jne     2f   
1:  
     MEXITCOUNT  
        MAYBE_POPL_ES  
        popl    %ds  
        popl    %edx  
        popl    %ecx  
        popl    %eax  
        iret  

2:   处理挂起的中断,包括硬中断和软中断
        cmpb    $3,_intr_nesting_level         * 限制中断嵌套数,防止栈溢出 *
        jae     1b              超过将中断返回
        movl    _cpl,%eax  
        movl    $HWI_MASK|SWI_MASK,_cpl
        incb    _intr_nesting_level   
        sti      开中断,前面enable_icus后中断控制器选出的中断无论优先级如何都得到CPU的响应,但不一定得到处理,可能只是被挂在ipending中.
        MAYBE_POPL_ES         
        popl    %ecx           
        popl    %edx  
        xchgl   %eax,4(%esp)   
        pushal                 
        pushl   %ecx        
        pushl   %es  
        movl    $KDSEL,%eax  
        movl    %ax,%es  
        movl    (2+8+0)*4(%esp),%ecx  
        movl    %ecx,(2+6)*4(%esp)     
        movl    (2+8+1)*4(%esp),%eax   
        pushl   %eax  
        subl    $4,%esp      

       * Sti开中断后到这里的指令是构造一个统一的栈祯(stack frame)以方便进入doreti中 *
        MEXITCOUNT  系统的时间片统计.
        jmp     _doreti

#define INTR(irq_num, vec_name, icu, enable_icus, reg)
  
IDTVEC(vec_name)  
        pushl   $0   
        pushl   $0
        pushal      保存通用寄存器,eax,ebx,ecx,edx,ebp. . .
        pushl   %ds            
        pushl   %es  到这里所有的寄存器都保存在堆栈中了.
        movl    $KDSEL,%eax   
        movl    %ax,%ds        
        movl    %ax,%es  
以下的四条汇编是为了在8259中把当前中断的中断位 置位以屏蔽当前中断
     movb    _imen + IRQ_BYTE(irq_num),%al  
       orb     $IRQ_BIT(irq_num),%al        
       movb    %al,_imen + IRQ_BYTE(irq_num)  
      outb    %al,$icu+ICU_IMR_OFFSET        
        enable_icus   使中断控制器(8259)正常工作以选出优先级最高的中断给CPU      
        movl    _cpl,%eax  
        testb   $IRQ_BIT(irq_num),%reg  
        jne     2f  

跳转的条件是cpl中的相应位被置'1',也就是该中断被屏蔽,因此要把中断挂在ipending,这个被挂起的中断应该是比当前正在处理的中断优先级有低.
        incb    _intr_nesting_level  

__CONCAT(Xresume,irq_num): 被挂起的中断得到处理的时候都是从这里开始的
     FAKE_MCOUNT(12*4(%esp))        
        incl    _cnt+V_INTR   
        movl    _intr_countp + (irq_num) * 4,%eax  
        incl    (%eax)  
        movl    _cpl,%eax  
        pushl   %eax  用于传递给doreti
        pushl   _intr_unit + (irq_num) * 4  
        orl     _intr_mask + (irq_num) * 4,%eax  提升优先级的地方.
        movl    %eax,_cpl  设置当前中断的cpl
        sti  开中断.8259中断控制器选出的中断从这里开始将得到CPU的注意
      call    *_intr_handler + (irq_num) * 4  
        cli                    
         
        以下的四条汇编的目的是把8259中中断进入时被屏蔽的当前中断打开

     movb    _imen + IRQ_BYTE(irq_num),%al  
        andb    $~IRQ_BIT(irq_num),%al     
        movb    %al,_imen + IRQ_BYTE(irq_num)        
        outb    %al,$icu+ICU_IMR_OFFSET  
        sti                    
        MEXITCOUNT  时间片计数
      
        jmp     _doreti  

2:  
         orb     $IRQ_BIT(irq_num),_ipending + IRQ_BYTE(irq_num)  把中断挂在ipending中
     popl    %es  
        popl    %ds  
        popal  
        addl    $4+4,%esp  
        iret 中断返回,在这里一般是返回更高优先级的中断,继续高优先级中断的处理.

[ 本帖最后由 bhpang2 于 2005-12-13 13:00 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2005-12-13 12:02 |只看该作者
以下都是硬件中断的汇编处理过程.
MCOUNT_LABEL(bintr)
        FAST_INTR(0,fastintr0, ENABLE_ICU1)
       . . .
        FAST_INTR(8,fastintr8, ENABLE_ICU1_AND_2)
        . . .
        FAST_INTR(15,fastintr15, ENABLE_ICU1_AND_2)
        INTR(0,intr0, IO_ICU1, ENABLE_ICU1, al)
       . . .
        INTR(7,intr7, IO_ICU1, ENABLE_ICU1, al)
        INTR(8,intr8, IO_ICU2, ENABLE_ICU1_AND_2, ah)
        . . .
        INTR(15,intr15, IO_ICU2, ENABLE_ICU1_AND_2, ah)
MCOUNT_LABEL(eintr)
_ihandlers:
ihandlers:                  
        .long   Xresume0, Xresume1, Xresume2, Xresume3
        .long   Xresume4, Xresume5, Xresume6, Xresume7
        .long   Xresume8, Xresume9, Xresume10, Xresume11
        .long   Xresume12, Xresume13, Xresume14, Xresume15
        .long   swi_tty, swi_net, dummycamisr, dummycamisr
        .long   _swi_vm, 0, 0, 0
        .long   0, 0, 0, 0
        .long   0, 0, _softclock, swi_ast

设备的中断处理过程通过涵数register_intr(...)调用setidt(...)和中断描述表联系起来,并把处理涵数放在数组*intr_handler[]里,这是一个涵数指针. 最终的处理涵数指针是在intr_handler[]里,在中断的汇编处理过程通过call 指令调用.

快速中断和一般中断的不同有两方面,首先是现场的保护内容,一般中断保护所有的寄存器内容,快速中断只保护用到的部分寄存器内容,因为它的处理过程不开中断.其次是开中断的时机不同,快速中断执行完中断处理后如果没有被挂起的中断是不开中断就返回的,也就是说快速中断是不会被挂起的,一般中断在进入中断处理前开中断.

下面是中断返回前的部分代码,主要工作是处理被挂在ipending中的软硬中断.
_doreti:
        FAKE_MCOUNT(_bintr)            
        addl    $4,%esp                 
        popl    %eax               

这是本次中断之前的cpl值,这个优先级比当前中断的优先级低.
doreti_next:
        movl    %eax,%ecx
        notl    %ecx
        正是因为用中断前的cpl才能处理比当前优先级低的挂起的中断,如果采用当前中断的cpl,当前cpl的值是从当前中断位直到31位都是’1’,指令notl %ecx后比当前中断优先级低的中断位将是’0’,后面的指令andl    _ipending,%ecx的结果将导致低优先级挂起中断的不到处理就退出了.在中断嵌套中这种优先级逐步提高后,逐步降低过程中被挂起的中断将按优先级由高到低得到处理,最终cpl的值变成’0’,所有的挂起中断都能得到处理后才结束中断处理过程.
        cli
        andl    _ipending,%ecx  * ipending放有被挂起的中断 *
        jne     doreti_unpend   如果有不被ipl屏蔽的挂起中断将得到处理
doreti_exit:
        movl    %eax,_cpl   恢复本次中断之前的cpl值,一般导致中断优先级降低.
        decb    _intr_nesting_level
        MEXITCOUNT
        .globl  doreti_popl_es
doreti_popl_es:              
        popl    %es
        .globl  doreti_popl_ds
doreti_popl_ds:
        popl    %ds
        popal                 恢复现场.
        addl    $8,%esp      
        .globl  doreti_iret
doreti_iret:
        iret   所有的都处理完了,中断返回
doreti_unpend:
        sti
        bsfl    %ecx,%ecx     

从右到左找出第一个置’1’的位,并把位置值放ecx,优先级别也是从右到左提高的
遵循着高优先级首先处理的原则.

        btrl    %ecx,_ipending   
        检查ipending中的该位是否是’1’,同时该位清零
      如果该位是’1’就意味着有挂起的中断.
         重复这过程,直到ipending中所有的位都是’0’才结束

     jnc     doreti_next        不是’1’就转移
     movl    ihandlers(,%ecx,4),%edx
        testl   %edx,%edx
        je      doreti_next            
        cmpl    $NHWI,%ecx
        jae     doreti_swi     进行软件中断处理.
        cli
        movl    %eax,_cpl
        MEXITCOUNT
        jmp     %edx  
将跳转到一般中断的__CONCAT(Xresume,irq_num)开始的地方,最后也会回到doreti中.

doreti_swi:    软件中断处理部分.
        pushl   %eax
         orl     imasks(,%ecx,4),%eax   
        movl    %eax,_cpl     
        call    %edx          调用软件中断处理,如大家熟识的TCPIP协议处理
        popl    %eax
        jmp     doreti_next

[ 本帖最后由 bhpang2 于 2005-12-13 12:57 编辑 ]

论坛徽章:
0
3 [报告]
发表于 2005-12-13 14:37 |只看该作者
LZ真执着呢

论坛徽章:
1
技术图书徽章
日期:2013-12-05 23:25:45
4 [报告]
发表于 2005-12-13 14:53 |只看该作者
在大学的时候小小的研究过。

因为我们做单片机,中断用的很多,不是BSD这样子的。不过好像远离都类似,就是硬中断和软中断
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP