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

本文涉及的产品
公网NAT网关,每月750个小时 15CU
应用型负载均衡 ALB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
简介: 转自:http://blog.csdn.net/rosetta/article/details/49454021  转载请注明出处:http://blog.csdn.net/rosetta    结合源代码分析一个完整的中断过程   此文详细描述了中断产生到中断处理程序执行、中断处理程序返回以及中断描述符初始化整个过程,结合linux-0.00源代码片断学习将会更直观易懂。

转自: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内核完全剖析》赵炯

【作者】 张昺华
【新浪微博】 张昺华--sky
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
目录
相关文章
|
10月前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
190 10
|
机器学习/深度学习 人工智能 NoSQL
【AIGC】深入浅出理解检索增强技术(RAG)
【5月更文挑战第10天】本文介绍了检索增强生成(RAG)技术,这是一种将AI模型与内部数据结合,提升处理和理解能力的方法。通过实时从大型文档库检索信息,扩展预训练语言模型的知识。文章通过示例说明了当模型需要回答未公开来源的内容时,RAG如何通过添加上下文信息来增强模型的回答能力。讨论了实际应用中令牌限制和文本分块的问题,以及使用文本嵌入技术解决相关性匹配的挑战。最后,概述了实现RAG的步骤,并预告后续将分享构建检索增强服务的详情。
860 3
|
存储 Windows
Windows 记录一次磁盘相关的PC卡顿问题
【10月更文挑战第25天】本文记录了一次 Windows 10 电脑卡顿问题的排查与解决过程。通过资源监视器、事件查看器、SMART 信息检查、磁盘扫描、后台程序排查、驱动更新等步骤,最终通过磁盘碎片整理和调整虚拟内存设置解决了卡顿问题。文章还提供了定期磁盘维护、合理设置虚拟内存及关注硬件健康的预防措施。
596 1
|
Java 开发者
通义灵码一周年:通义灵码个人版测评
本文介绍了JAVA开发工程师如何利用通义灵码个人版进行源代码分析与优化,包括源代码解释、生成代码优化、workspace和@terminal四个方面的具体操作实例,展示了该工具在提高开发效率上的显著效果,提效达40%。
|
分布式计算 Hadoop 测试技术
Ubuntu16.04安装Hadoop2.6+Spark1.6+开发实例
Ubuntu16.04安装Hadoop2.6+Spark1.6,并安装python开发工具Jupyter notebook,通过pyspark测试一个实例,調通整个Spark+hadoop伪分布式开发环境。 主要内容:配置root用户,配置Hadoop,Spark环境变量,Hadoop伪分布式安装并测试实例,spark安装并通过Jupter notebook进行开发实例
2366 0
|
3天前
|
云安全 人工智能 安全
AI被攻击怎么办?
阿里云提供 AI 全栈安全能力,其中对网络攻击的主动识别、智能阻断与快速响应构成其核心防线,依托原生安全防护为客户筑牢免疫屏障。