中断描述符表

 

中断描述符表

中断描述符表(Interrupt Descriptor TableIDT)是用来告诉处理器在遇到异常或 INT”操作码(汇编中)时所应调用的中断服务例程( Interrupt Service RoutineISR)。在一个设备完成请求并且需要获得服务时,中断请求也调用IDT入口。更多关于异常和ISR的详细内容在本指南的下一节里,点击这里查看。

每个IDT入口都与一个GDT入口类似,它们都有一个基地址,都有一个访问标志,并且总长度都是32位的。两种描述符的主要区别是这些域有不同的含义。在IDT中,描述符所指定的基地址实际上是中断产生时处理器所应调用的ISR的地址。IDT入口本身没有限制,但你需要指定一个给定ISR所在的段。这样做的好处是,当处理器处于某个环时(比如一个应用程序正在运行),依然可以通过一个已经发生的中断使其将控制权交给内核。

IDT入口的访问标志也与GDT的类似。有一位用来表示描述符是否真实存在。还有一个域用来存放描述符优先级(Descriptor Privilege LevelDPL),即允许使用给定中断的最大数目的环。主要的区别在于访问标志的剩余部分的定义。访问位的低五位经常设置为二进制的01110,即十进制中的14。下面用一个表来更好地图形表示IDT入口的访问位。

7

6

5

4

0

 

P

DPL

常为01110 (14)

 

P-段是否存在?(1 = )
DPL
-描述符所处优先级(0 3

在你的内核目录下创建一个名为“idt.c”的新文件。在你的“build.bat”文件中添加一行以使GCC同样编译“idt.c”。最后,把“idt.o”添加到LD需要链接以创建自定义内核的不断增长的文件列表中。“idt.c”将声明一个结构,这个结构定义了每个IDT入口、装载IDT时必须用到的特定IDT指针结构(与装载GDT类似,但更简单),并且声明一个包含256IDT入口的数组(这就是我们自己的IDT)。

#include < system.h >

/* 定义一个IDT入口。 */
struct idt_entry
{
    unsigned short base_lo;
    unsigned short sel; /* 我们的内核段从这里开始! */
    unsigned char always0; /* 这个变量将一直设置为0! */
    unsigned char flags; /* 设置使用上面的表! */
    unsigned short base_hi;
} __attribute__((packed));

struct idt_ptr
{
    unsigned short limit;
    unsigned int base;
} __attribute__((packed));

/* 声明一个有256个入口的IDT,尽管在本指南中我们将只使用前32个入口。
* 如果访问任何未定义的IDT入口,正常情况下将导致一个“未知中断”的异
* 常。访问任何“存在”位被清除(为0)的描述符则将产生一个“无法处理
* 中断”的异常。
*/
struct idt_entry idt[256];
struct idt_ptr idtp;

/* 这存在于“start.asm”,用来装载我们的IDT。 */
extern void idt_load();
这是“idt.c”的开始部分,定义了重要的数据结构!

接下来,就像“gdt.c”,你将注意到这里声明了一个已经在其它文件中实际存在的函数。“idt_load”和“gdt_flush”一样,由汇编语言编写。所有“idt_load”使用我们的特定 IDT指针来调用“lidt”汇编操作码,我们随后将在“idt_install”中创建这个指针。 打开“start.asm”,并在对应“_gdt_flush”的“ret”后面添加下面几行:

; 把“_idtp”中定义的IDT装载到处理器。
; 在C语言里,这样的声明应写为“extern void idt_load();”。
global _idt_load
extern _idtp
_idt_load:
lidt [_idtp]
ret
把这段添加到“start.asm”

设置IDT入口比设置GDT入口简单很多。我们有一个“idt_set_gate”函数,这个函数接收IDT入口数目、我们的ISR基地址、我们的内核代码段(Kernel Code Segment)、以及我们在前面的介绍中用表描述的访问标志。接下来,我们有一个“idt_install”函数,这个函数用来设置我们的特定IDT指针,同时将IDT设置为 默认的已知的清除状态。最后,我们将通过调用“idt_load”装载IDT。请注意,在IDT装载之后,你仍可以在任意时刻将ISR添加到你的IDT中。更多关于ISR的内容将在下一节中讲解。

/* 用这个函数来设置IDT中的一个入口。同样比GDT中的简单。 */
void idt_set_gate(unsigned char num, unsigned long base, unsigned short sel, unsigned char flags)
{
    /* 这里留给你去试着编写这个函数:把参数“base”划分为高16位和低16位,
    * 并将他们存储到idt[num].base_hi和idt[num].base_lo。idt[num]中
    * 你还需要设置的其它域,在设置时都是不言而喻的。
    */
}

/* 安装IDT */
void idt_install()
{
    /* 设置特点的IDT指针,就像“gdt.c”中的一样。 */
    idtp.limit = (sizeof (struct idt_entry) * 256) - 1;
    idtp.base = &idt;

    /* 清除整个IDT,初始化其为零。 */
    memset(&idt, 0, sizeof(struct idt_entry) * 256);

    /* 使用idt_set_gate将任意新的ISR添加到IDT。 */

    /* 将处理器的内部寄存器指向新的IDT。 */
    idt_load();
}
“idt.c”的剩余部分。请试着写出“idt_set_gate”,很容易的!

最后,确保将“idt_set_gate”和“idt_install”作为函数原型添加到“system.h”中。我们需要在其它文件中调用这些函数,比如“main.c”。在我们的“main.c”中紧接着“gdt_install ”之后调用“idt_install”。你应该可以毫无问题地编译你的内核。用一些时间在你的新内核下做一些试验。如果你试着做一些非法操作,比如除零,你会发现你的计算机 重启了!我们可以通过在新IDT中安装ISR来捕获这些“异常”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值