• 结合源代码分析一个完整的中断过程【转】


    转自:http://blog.csdn.net/rosetta/article/details/49454021 

    转载请注明出处:http://blog.csdn.net/rosetta   

    结合源代码分析一个完整的中断过程

      此文详细描述了中断产生到中断处理程序执行、中断处理程序返回以及中断描述符初始化整个过程,结合linux-0.00源代码片断学习将会更直观易懂。

       中断是指当前执行程序或任务在执行到某处时出现一个事件,该事件需要程序处理。这种事件会导致程序会从当前运行程序转移到被称为中断(或异常)处理程序中执行。中断可以由硬件发生,也可以由软件调用int n指令产生。异常发生在CPU执行一条指令时,检测到一个出错条件时发生,例如被0除出错。

           那么中断产生时,当CPU去执行中断处理程序之前如何保存当前程序的信息?需要保存的信息有哪些,以便在处理完中断处理程序后恢复执行之前的程序?再者CPU怎么去找中断处理程序的?

           下面以int n产生中断为例,以linux-0.00中的head.s源码为实例,并使用bochs单步调试,理论联系实际,分析从中断产生到执行并返回整个过程。   

           Boch调试环境及linux-0.00运行环境搭建可参考《Linux-0.00运行环境搭建》

           从head.s如下代码处开始分析。完整的代码可参考《一个简单多任务内核实例的分析 》

    [cpp] view plain copy
     
    1. 236 task0:  
    2.   
    3. 237    movl $0x17, %eax  
    4.   
    5. 238    movw %ax, %ds  
    6.   
    7. 239    movb $65, %al              /*print 'A' 65*/  
    8.   
    9. 240    int $0x80  
    10.   
    11. 241     movl $0xfff, %ecx  
    12.   
    13. 242 1: loop 1b  
    14.   
    15. 243    jmp task0  
    16.   
    17.    

           运行bochs 调试脚本,按以下方法找到task0的地址,因为head程序在物理地址0x00处,所以使用objdump显示的偏移地址+段地址0x00还是其本身,即最终的断点位置。

           在源代码目录执行:

    [html] view plain copy
     
    1. [root@xxx linux-0.00-rh9]# objdump -d -j.text -t ./head.o|grep task0  
    2.   
    3. 000010e0  l      .text     00000000 task0  

           所以在bochs中直接下断到0x10e0处。

      在执行int 0x80前先停下观察cpu相应寄存器及栈的数据。

      栈中状态:

      可以看到此时的cs=0x000f(62行iret返回后设置的cs值),eip=0x10e9(当前即将执行的int 0x80代码),如果这里不产生中断的话下一条代码将是执行0x10eb,但这里使用int 0x80 产生了一个中断,从而去调用IDT中断描述符表中索引为0x80(第128个)的描述符。

           使用跟进函数指令s跟进中断处理程序并显示当前cpu、栈的情况。

       寄存器中主要变化的是cs,eip,ss,esp,而栈的变化也很大,那么他们是如何变化的呢?

      1. eip=0x10e9 => 0x0166

           eip的变化很好理解,调用int0x80之前的指令0x10e9处跳到了现在的0x0166处,之前指令的后一条将要执行的指令地址0x10eb存在了栈中(用于中断返回)。

      2.  cs=0x000f => 0x0008

      那么cs为何变成了0x0008呢?注意此时是保护模式下,所以0x0008其实为段选择符。

      因为此时已经跳转到了IDT中第0x80个描述符,那么0x80处的描述符长何样?

    [html] view plain copy
     
    1. 36 # setup timer & system call interruptdescriptors.  
    2.   
    3.  37    movl $0x00080000, %eax  
    4.   
    5.  38     movw $timer_interrupt, %ax  
    6.   
    7.  39     movw $0x8E00, %dx  
    8.   
    9.  40     movl $0x08, %ecx              # The PC default timer int.  
    10.   
    11.  41     lea idt(,%ecx,8), %esi  
    12.   
    13.  42     movl %eax,(%esi)  
    14.   
    15.  43     movl %edx,4(%esi)  
    16.   
    17.  44     movw$system_interrupt, %ax  
    18.   
    19.  45    movw $0xef00, %dx  
    20.   
    21.  46    movl $0x80, %ecx  
    22.   
    23.  47    lea idt(,%ecx,8), %esi  
    24.   
    25.  48    movl %eax,(%esi)  
    26.   
    27.  49    movl %edx,4(%esi)  

           如上代码,因为IDT中的每一项中断描述符都为8字节,以上第37行,44~49行就是设置IDT表中第0x80个位置处的中断描述符,其内容:eax为低4字节,其中eax的高16位为段选择符0x0008(即刚才cs的变化,由0x000f => 0x0008),eax的低16位为中断处理程序system_interrupt偏移地址;dx为中断描述符类型及DPL等,此时的中断描述符类型为0xf=1111为陷阱门描述符。

      3.ss=0x0017=> 0x0010, esp=0x0bd8 => 0xe4c

      代码中的tss0内容为:

    [html] view plain copy
     
    1. 205tss0:   .long 0             /* back link */  
    2.   
    3. 206     .long krn_stk0, 0x10        /* esp0, ss0 */  
    4.   
    5. 207     .long 0, 0, 0, 0, 0     /* esp1, ss1, esp2, ss2, cr3 */  
    6.   
    7. 208     .long 0, 0, 0, 0, 0     /* eip, eflags, eax, ecx, edx */  
    8.   
    9. 209     .long 0, 0, 0, 0, 0     /* ebx esp, ebp, esi, edi */  
    10.   
    11. 210     .long 0, 0, 0, 0, 0, 0      /* es, cs, ss, ds, fs, gs */  
    12.   
    13. 211     .long LDT0_SEL, 0x8000000   /* ldt, trace bitmap */  
    14.   
    15. 212  
    16.   
    17. 213     .fill 128,4,0  
    18.   
    19. 214 krn_stk0:  
    20.   
    21. 215 #   .long 0  
    22.   
    23.    

      使用objdump查看head.o中的krn_stak0地址:

    [html] view plain copy
     
    1. [root@plmlinux-0.00-rh9]# objdump -d -j .text -t ./head.o|grep krn_stk0  
    2.   
    3. 00000e60  l       .text     00000000krn_stk0  
    4.   
    5. 00000e60<krn_stk0>:  

           4.栈的变化

      ss的值加载了tss任务段描述符中的ss0字段,为0x10;esp也是加载其中的esp0字段,所以应该是0xe60,但是因为该栈已经被使用过了,所以导致esp减至0xe4c。

           栈中从0xe60开始至0xe4c处都是什么内容?此时看下system_interrupt是如何返回的?

    [html] view plain copy
     
    1. 152 .align 2  
    2.   
    3. 153system_interrupt:  
    4.   
    5. 154     push %ds  
    6.   
    7. 155     pushl %edx  
    8.   
    9. 156     pushl %ecx  
    10.   
    11. 157     pushl %ebx  
    12.   
    13. 158     pushl %eax  
    14.   
    15. 159     movl $0x10, %edx  
    16.   
    17. 160     mov %dx, %ds  
    18.   
    19. 161     call write_char  
    20.   
    21. 162     popl %eax  
    22.   
    23. 163     popl %ebx  
    24.   
    25. 164     popl %ecx  
    26.   
    27. 165     popl %edx  
    28.   
    29. 166     pop %ds  
    30.   
    31. 167     iret  

           发现其最后是iret,而执行此指令会导致:

    [html] view plain copy
     
    1. eip <= ss:esp  esp=esp+4 ; eip <= 0x10eb  
    2.   
    3. cs <= ss:esp  esp=esp+4; cs <= 0x000f  
    4.   
    5. eflags <= ss:esp esp=esp+4; eflags <= 246  
    6.   
    7. esp <= ss:esp  esp=esp+4; esp <= 0x0bd8  
    8.   
    9. ss <= ss:esp   esp=esp+4; ss <= 0x0017  

       所以,栈中保存的内容都是中断前的信息,以便返回时使用。

       System_interrupt中断处理程序调用iret返回后将继续执行int 0x80后面的指令cs:eip=0x00f:0x10eb。

      此文仅描述中断方面的知识,对于任务task的切换和加载、tss任务段描述符未做说明,此部分内容将在下一篇文章中记录。

    参考文献:

    《Linux内核完全剖析》赵炯

  • 相关阅读:
    升讯威微信营销系统开发实践:目录
    升讯威微信营销系统开发实践:订阅号和服务号深入分析( 完整开源于 Github)
    ASP.NET MVC (Razor)开发<<周报与绩效考核系统>>,并免费提供园友们使用~~~
    使用 SailingEase WinForm 框架构建复合式应用程序(插件式应用程序)
    vertica提取json字段值
    centos上配置redis从节点
    查看出网IP
    centos上tcp抓包
    修改centos服务器时区并同步最新时间
    解决centos下tomcat启动太慢 & JDBC连接oracle太慢的问题
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/5279877.html
Copyright © 2020-2023  润新知