Bran的内核开发教程(bkerndev)-07 中断描述符表(IDT)
中断描述符表(IDT)
中断描述符表(IDT)用于告诉处理器调用哪个中断服务程序(ISR)来处理异常或汇编中的"int"指令。每当设备完成请求并需要服务事, 中断请求也会调用IDT条目。异常和ISR将在下一节进行详细的说明。
每一项IDT都与GDT相似, 两者都有一个基地址, 一个访问标志, 而且都长64bits。这两类描述符表最主要的区别在于这些字段的含义: 在IDT中的基地址是中断时应调用的ISR的地址。IDT也没有边界(limit), 而是需要一个指定的段, 该段与给定的ISR所在段相同。这让处理器即使处于不同级别的Ring中, 在发生中断时也能将控制权交给内核。
IDT条目的访问标志位也和GDT相似。需要一个字段说明描述符是否存在。描述符特权级别(DPL)用于说明哪个Ring是给定中断允许使用的最高级别。主要区别在于访问字节的低5位始终为二进制01110, 也就是十进制中的14。下面这张表让你更好地理解IDT访问字节。

- P - 段是否存在? (1 = Yes)
- DPL - 哪个Ring (0~3)
在你的自制内核目录下创建一个新文件"idt.c"。编辑"build.bat"文件, 添加新的一行gcc命令编译"idt.c"。最后添加"idt.o"到链接文件列表中。"idt.c"中将会声明一个结构体用于定义每个IDT条目, 和一个用于加载IDT的特殊IDT指针结构体(类似于加载GDT, 但工作量更少), 并声明一个256大小的IDT数组: 这将成为我们的IDT。
idt.c
#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被集中,
* 将会导致"未处理的中断(Unhandled Interrupt)"异常,
* 描述符的"presence"位如果为0, 将生成"未处理的中断"异常。*/
struct idt_entry idt[256];
struct idt_ptr idtp;
/*该函数在"start.asm"中定义, 用于加载我们的IDT */
extern void idt_load();
idt_load函数的函数定义在其他文件中, 和gdt_flash一样是使用汇编语言编写的。我们之后将在idt_install中使用创建的IDT指针来调用lidt汇编操作码。打开"start.asm"文件, 把下面几行添加到_gdt_flush的re后面。
start.asm
; 加载idtp指针所指的IDT到处理器中
; 这在C文件中声明为"extern void idt_load();"
global _idt_load
extern _idtp
_idt_load:
lidt [_idtp]
ret
设置IDT条目比GDT简单得多。我们又一个idt_set_gate函数用于接收IDT索引号、中断服务程序基地址、内核代码段以及上表中提到的访问标志。同样, 我们又一个idt_install函数用来设置IDT指针, 并将IDT初始化为默认清除状态。最后, 我们将通过调用idt_load来加载IDT。在加载IDT后, 我们可以随时将ISR添加到IDT中。本教程将在下一节介绍ISR。下面是"idt.c"文件的剩余部分, 请尝试弄明白idt_set_gate函数, 它其实很简单。
idt.c
/* 使用该函数来设置每项IDT*/
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指针 */
idtp.limit = (sizeof (struct idt_entry) * 256) - 1;
idtp.base = &idt;
/* 清空整个IDT, 并初始化该片区域为0 */
memset(&idt, 0, sizeof(struct idt_entry) * 256);
/* 使用idt_set_gate将ISR添加到IDT中 */
/* 将处理器的内部寄存器指向新的IDT */
idt_load();
}
最后, 确保在"system.h"中添加idt_set_gate和idt_install作为函数原型, 因为我们需要从其他文件(例如"main.c")中调用这些函数。在main()函数调用了gdt_install后立即调用idt_install。这是你应该可以成功编译你的内核。尝试使用一下你的新内核, 在进行除零之类的非法操作时, 计算机将重置。我们可以通过在新的IDT中安装ISR来不活这些异常。
如果你不知道怎么编写idt_set_gate, 则可以在此处找到本教程的解决方案。
idt.c
void idt_set_gate(unsigned char num, unsigned long base, unsigned short sel, unsigned char flags)
{
/* 中断程序的基地址 */
idt[num].base_lo = (base & 0xFFFF);
idt[num].base_hi = (base >> 16) & 0xFFFF;
/* 该IDT使用的段或区域以及访问标志位将在此设置 */
idt[num].sel = sel;
idt[num].always0 = 0;
idt[num].flags = flags;
}
此文原创禁止转载,转载文章请联系博主并注明来源和出处,谢谢!
作者: Raina_RLN https://www.cnblogs.com/raina/
Bran的内核开发教程(bkerndev)-07 中断描述符表(IDT)的更多相关文章
- Bran的内核开发教程(bkerndev)-06 全局描述符表(GDT)
全局描述符表(GDT) 在386平台各种保护措施中最重要的就是全局描述符表(GDT).GDT为内存的某些部分定义了基本的访问权限.我们可以使用GDT中的一个索引来生成段冲突异常, 让内核终止执行异 ...
- Bran的内核开发教程(bkerndev)-08 中断服务程序(ISR)
中断服务程序(ISR) 中断服务程序(ISR)用于保存当前处理器的状态, 并在调用内核的C级中断处理程序之前正确设置内核模式所需的段寄存器.而工作只需要15到20行汇编代码来处理, 包括调用C中的 ...
- Bran的内核开发教程(bkerndev)-02 准备工作
准备工作 内核开发是编写代码以及调试各种系统组件的漫长过程.一开始这似乎是一个让人畏惧的任务,但是并不需要大量的工具集来编写自己的内核.这个内核开发教程主要涉及使用GRUB将内核加载到内存中.GR ...
- Bran的内核开发教程(bkerndev)-01 介绍
介绍 内核开发不是件容易的事,这是对一个程序员编程能力的考验.开发内核其实就是开发一个能够与硬件交互和管理硬件的软件.内核也是一个操作系统的核心,是管理硬件资源的逻辑. 处理器或是CPU是内核 ...
- Bran的内核开发教程(bkerndev)-04 创建main函数和链接C文件
目录 创建main函数和链接C文件 PS: 下面是我自己写的 Win10安装gcc编译器 本节教程对应的Linux下的编译脚本 _main的问题 创建main函数和链接C文件 一般C语言使用mai ...
- Bran的内核开发教程(bkerndev)-03 内核初步
目录 内核初步 内核入口 链接脚本 汇编和链接 PS: 下面是我自己写的 64位Linux下的编译脚本 内核初步 在这节教程, 我们将深入研究一些汇编程序, 学习创建链接脚本的基础知识以及使用它的 ...
- Bran的内核开发教程(bkerndev)-05 打印到屏幕
打印到屏幕 现在, 我们需要尝试打印到屏幕上.为此, 我们需要管理屏幕滚动, 如果能允许使用不同的颜色就更好了.好在VGA视频卡为我们提供了一片内存空间, 允许同时写入属性字节和字符字节对, 可以 ...
- 中断描述符表 IDT
保护模式下三个重要的系统表——GDT.LDT和IDT 这里主要是解释中断描述符表 中断描述符表IDT将每个异常或中断向量分别与它们的处理过程联系起来.与GDT和LDT表类似,IDT也是由8字节长描述符 ...
- Bran的内核开发指南_中文版
http://www.cnblogs.com/liloke/archive/2011/12/21/2296004.html 最近在看<orange’s>一书,有点想自己写一个轻量级OS的想 ...
随机推荐
- 23种设计模式之原型模式(Prototype Pattern)
原型模式 使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象 分析: 孙悟空:根据自己的形状复制(克隆)出多个身外身 软件开发:通过复制一个原型对象得到多个与原型对象一模一样的新对象 ...
- 报表统计——java实现查询某年某月每天数据,没数据补0
一般图表绘制例如echarts等,返回数据格式都大同小异.重点是利用sql或者java实现数据格式的转型,接下来是关键部分: 1.前提:提供的工具方法——获取某月有多少天 //通过年份和月份确定该月的 ...
- Python数据结构 - 利用headp模块寻找最大N个元素并实现优先队列
用headp找到最大最小的N个值 import heapq nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2] print(heapq.nlargest(3, ...
- java程序猿如何练习java版的易筋经?
故事背景 电视剧<天龙八部>中,阿朱易容后进入少林寺偷走了<易筋经>,她一直想把这本书送给乔峰.耿直的乔峰觉得此书来历不正,不肯接受.几番波折,这本书最后落到聚贤庄庄主游坦之手 ...
- linux无法安装应用
需安装flex 和bison 一般需要更新软件源 root权限 下 apt-get update apt-get upgrade 如果出现以下问题,先查看网络是否畅通: ping 192.168.0. ...
- Swoole4-swoole创建Mysql连接池
一 .什么是mysql连接池 场景:每秒同时有1000个并发,但是这个mysql同时只能处理400个连接,mysql会宕机. 解决方案:连接池,这个连接池建立了200个和mysql的连接,这1000个 ...
- asp.net core learn
.NET Core WebApi RESTful规范 RESTful API 最佳实践 理解RESTful架构 接口版本控制 Support multiple versions of ASP.NET ...
- ES三节点重启后报错no known master node
问题 一直在研究ES的监控怎么做,想偷点懒,不去通过API获取然后计算,就想找个现成的插件或者监控软件,只要装个agent就可以,然后就找到了x-pack,插件装好了之后,需要重启ES集群,线上的ES ...
- sql 外键 on update cascade 和 on delete cascade 作用区别?
这是数据库外键定义的一个可选项,用来设置当主键表中的被参考列的数据发生变化时,外键表中响应字段的变换规则的.update 则是主键表中被参考字段的值更新,delete是指在主键表中删除一条记录:on ...
- Java基础-开篇
之前在新浪博客写了不少springmvc的相关技术,但新浪博客毕竟不是专业的技术博客,添加代码很不方便,就开始在博客园试试了. 使用java开发也不少年了,准备再次整理一些java基础知识,当然,这次 ...