2021.09.17
GDK7 IDT
1.中断描述符表概述
中断应该是一个覆盖范围较广的概念,在很多时候我们都会需要“中断”。
例如:当你用水壶烧水时,你并不需要一直盯着它,可以抽身去做一些别的的事情,当水壶完成烧水的任务,它可能会发出“嘟嘟嘟”的声音来告诉你水烧好了。这样你既完成了一些别的事情,同时又有了热水喝。
在计算机中,中断也是如此,CPU的时间较为宝贵,并不可能所有的事情都亲历亲为,也不可能只盯着一件事情,等这件事情结束后再去做下一件事情。因此,CPU会通过下达任务的方式给其他执行单元,在执行单元做任务的过程中,CPU可以抽身去做其他的事情。当该执行单元完成任务后,通知CPU即可,这样任务也完成了,CPU也可以利用宝贵的时间去做其他的事情。
在计算机中,中断机制可以让CPU随时被打断,然后去出来其他事情,当事情处理好后再回来做原来的事情;中断通常可以分为内部中断(异常)和外部中断(硬件中断)。
《在调试器下理解计算机系统》课程截屏
上面只是关于中断及中断机制的一些较为浅显的解释,中断实际上是操作系统中较为重要的一种概念及机制,关于中断的一些详细的说明可以在由张银奎老师主讲的《在调试器下理解计算系统》中观看到。(视频在Nano Code中)
Nano Code下载链接:
https://nanocode.cn/#/download
《在调试器下理解计算机系统》课程截屏
在上面的截屏中我们可以看到CPU在当前操作系统三大机制(中断、陷阱、任务)之下,CPU所发生的旅途轨迹。
当CPU可以被中断后,又出现了另一个问题,即CPU应该去哪里执行事情呢,总要有个目标吧,而且这个目标应该非常精确才行,不然差之毫厘,CPU就去处理其他的事情了。因此我们就非常的需要IDT中断描述符表了,IDT中断描述符表内存储了中断的相关信息,当CPU被中断后,CPU会获取中断向量,然后查阅这张表,找到对应的中断信息,然后根据中断信息跳去相应的位置处理事情。
IVT中断向量表可以理解为“迷你版”的IDT中断描述符表,里面存储的中断向量是中断服务程序的入口地址;该入口地址由IDT中断描述符表内的segmen字段和offset_low字段组合形成的(段地址+低16位偏移地址)。
GDK7 IDT
2.使用GDK7观察IDT
关于如何使用GDK7与调试机建立连接的过程,可以参照下方链接中的文章,在此不再过多赘述。
https://mp.youkuaiyun.com/mp_blog/creation/editor/118387446
使用GDK7观察IDT,首先要让GDK7与我们的调试机建立连接,如下所示。
打开nano code,载入ndx扩展,并输出!idt扩展命令。
执行完成后输出以下结果。
从执行的结果中。我们可以看到,第一列是向量号,第二列是门描述符,第三列是函数地址,第四列是函数名。
中断向量号 异常事件 函数名
0 除法错误 Divide_error
1 调试异常 Debug
2 非屏蔽中断 Nmi
3 断点中断 Int 3
4 溢出中断 Overflow
5 边界监测中断 Bounds
6 无效操作码 Invalid_op
7 设备不可用 Device_not_available
8 双重故障 Double_fault
9 协处理器段溢出 Coprocessor_segment_overrun
10 无效 TSS Incalid_tss
11 缺段中断 Segment_not_present
12 堆栈异常 Stack_segment
13 一般保护异常 General_protection
14 页异常 Page_fault
15 intel保留 Spurious_interrupt_bug
16 协处理器出错 Coprocessor_error
17 对齐检查中断 Alignment_check
18 机器检查中断 Machine_check
19 不可屏蔽中断 Simd_coprocessoe_error
GDK7 IDT
3.!idt的实现原理
idt_table在Linux中被定义成了一个数组,每个数组所指向的均是gate_struct结构体,因此显示数组信息亦相当于显示gate_struct结构体的信息,只不过因为地址的不同,gate_struct结构体所含有的信息也会有所差别;想要从idt_table内获取对应的元素的信息应该先从idt_table全局变量中获取idt_table的基地址,然后获取gate_struct结构体的偏移值,根据偏移值来显示不同是数组信息。
当获取到基地址以后,应该根据此地址从gate_struct结构体内获取信息,我们要输出的信息有中断向量号、门描述符、函数地址、函数名;中断向量号只需要顺序向下排即可,门描述符则可以在idt_bits结构体内找到,函数地址由gate_struct结构体内的offset_low字段、offset_middle字段及offset_high字段组合而成,可以在Linux中通过[cat /proc/kallsyms | grep 函数名]来确认获取的函数地址是否正确,函数名则可以根据函数地址获取。
做循环,不断在基地址的基础上累加偏移地址,从而得到下一个数组元素所对应的地址,然后再利用此地址显示gate_struct结构体的信息,直至完部完成显示。
获取函数名示例:GetSymbol(函数地址[传入], 函数名[接受], 偏移值[接受])。
关于偏移值的说明:偏移值 = 函数实际地址 – 传入函数地址;因为即使传入一个不准确的地址,GetSymbol也会根据这个地址定位到附近的符号,从而获取函数名;如果你输入的是一个准确的函数地址,那么偏移值就会等于0。
GDK7 IDT
4.中断处理全局变量的获取
假如想要获取中断处理有关的全局变量(apic_idts、def_idts、early_idts、ist_idts、dbg_idts)的相关信息,那么我们首先知道这些全局变量在Linux内核中也被定义成了数组的形式,其数组所对应的均为idt_data结构体;因此想要显示中断处理有关全局变量的信息,也就是显示数组元素对应地址的idt_data结构体信息。
首先我们需要两个函数,一个显示idt_data结构体的信息,另一个做循环,接受全局变量名,并获取数组地址及数组个数,利用数组个数循环显示数组的不同元素的信息,当然是要传递地址给显示结构体的函数的。
需要注意的一点是,这些中断处理有关的全局变量只能在启动早期观察到,系统完成启动之后,便会释放这些全局变量;以后调试启动过程的时候,会再单独介绍。
static const __initconst struct idt_data apic_idts[]
static const __initconst struct idt_data def_idts[]
static const __initconst struct idt_data early_idts[]
static const __initconst struct idt_data ist_idts[]
static const __initconst struct idt_data dbg_idts[]
struct idt_data {
unsigned int vector;
unsigned int segment;
struct idt_bits bits;
const void *addr;
};
GDK7 IDT
5.!idt扩展命令的介绍
!idt扩展命令用于在Linux调试的时候,显示IDT中断描述符表内的相关信息。
!idt扩展命令格式:!idt [IDT的地址] [显示选项]
IDT的地址说明:
可以选择是否指定地址,若没有指定地址则地址会从Linux内核中的idt_table全局变量内获取IDT的基地址。
显示选项的含义:
-a:显示Idt表内全部的256个表项的相关信息。
-?:显示命令选项的说明。
空::没有指定显示选项,则只显示大于等于32且非空的表项。
”
盛格塾
正心诚意,格物致知
以人文情怀审视软件,以软件技术改变人生
格友公众号
盛格塾小程序
扫描上方二维码或在微信中搜索“盛格塾”小程序
可以阅读更多文章和有声读物
文字:淳于智强
排版:韩俊
”