Linux内核19-中断描述符表IDT的初始化

简介: Linux内核19-中断描述符表IDT的初始化

至此,我们已经理解了X86架构如何在硬件层面如何处理中断和异常,那么接下来,我们看看Linux内核管理这些中断和异常。

同所有的设备一样,我们在使能硬件之前,必须先初始化其相关的数据结构。而Linux使用中断描述符表IDT记录管理所有的中断和异常。那么,首先,Linux内核应该把IDT的起始地址写入idtr寄存器,然后初始化所有的表项。这一步在初始化系统时完成。

因为汇编指令int允许用户进程发送任意编号的中断(0-255)。为此,IDT的初始化必须考虑阻止由用户进程int指令引发的非法中断和异常。可以通过将中断描述符表中的DPL域设为0来实现。如果用户进程试图发送非法中断信号,CPU控制单元比较CPL和DPL的值,发出常规保护的异常。

但是,大部分时候,用户态进程必须能够发送可编程异常。那么把相应的中断或陷阱门描述符的DPL域设为3即可。比如系统调用。

让我们看看Linux如何实现这种策略。


中断、陷阱和系统门


在之前的文章中,我们已经介绍过,Intel提供了三种类型的中断描述符:任务,中断和陷阱门描述符。Linux的分类有些不同,它们如下所示:

  1. 中断门
    和Intel的中断门相同。所有的用户进程不能访问(该门的DPL设为0)。所有Linux的中断处理程序都是通过中断门激活的,也就是说只能在内核态访问。
  2. 系统门
    属于Intel的陷阱门,可以被用户态进程访问(该门的DPL设为3)。三个Linux异常处理程序对应的中断号分别是4、5和128,分别使用intoboundint $0x80三条汇编指令发出对应的中断信号。
  3. 系统中断门
    属于Intel的中断门,用户态进程可以访问(该门的DPL域设为3)。中断号为3的异常处理程序通过系统中断门激活,可以使用在用户态使用int3指令实现。
  4. 陷阱门
    属于Intel陷阱门,不能被用户态程序访问(该门的DPL设为0)。用来访问大部分的异常处理程序。
  5. 任务门
    属于Intel任务门,用户态进程不能访问(该门的DPL设为0)。专门访问处理Double fault异常的处理程序。

对应上面的5种分类,分别有相应的函数可以初始化IDT(这些函数与硬件架构息息相关),如下所示:

  • set_intr_gate(n,addr)
    插入中断门。该门内的端选择器设为内核态代码所在的段。Offset被设为addr,就是中断处理程序的地址。DPL域设为0。
  • set_system_gate(n,addr)
    插入系统门。其余描述与上面的函数相同。
  • set_system_intr_gate(n,addr)
    插入系统中断门。该门内的端选择器设为内核态代码所在的段。Offset被设为addr,就是异常处理程序的地址。DPL域设为3。
  • set_trap_gate(n,addr)
    插入陷阱门,DPL被设为3。其余与上面函数相同。
  • set_task_gate(n,gdt)
    插入任务门。段选择器设为要执行的函数所在的段。Offset设为0,而DPL设为3。


IDT第一次初始化


其实,IDT被初始化两次。第一次是在BIOS程序中,此时CPU还工作在实模式下。一旦Linux启动,IDT会被搬运到RAM的受保护区域并被第二次初始化,因为Linux不会使用任何BIOS程序。

IDT结构被存储在idt_table表中,包含256项。idt_descr变量存储IDT的大小和它的地址,在系统的初始化阶段,内核用来设置idtr寄存器,专用汇编指令是lidt。

内核初始化的时候,汇编函数setup_idt()用相同的中断门填充idt_table表的所有项,都指向ignore_int()中断处理函数:

setup_idt:
    lea ignore_int, %edx
    movl $(__KERNEL_CS << 16), %eax
    movw %dx, %ax           /*  = 0x0010 = cs */
    movw $0x8e00, %dx       /* 中断门,DPL=0 */
    lea idt_table, %edi     /* 加载idt表的地址到寄存器edi中 */
    mov $256, %ecx
rp_sidt:
    movl %eax, (%edi)       /* 设置中断处理函数 */
    movl %edx, 4(%edi)      /* 设置段描述符 */
    addl $8, %edi           /* 跳转到IDT表的下一项 */
    dec %ecx                /* 自减 */
    jne rp_sidt
    ret

中断处理函数ignore_int(),也是一个汇编语言编写的函数,相当于一个null函数,它执行:

  1. 保存一些寄存器到堆栈中。
  2. 调用printk()函数打印Unknown interrupt系统消息`。
  3. 从堆栈中恢复寄存器的内容。
  4. 执行iret指令回到调用处。

正常情况下,此时的中断处理函数ignore_int()是不应该被执行的。如果在console或者log日志中出现Unknown interrupt的消息,说明发生硬件错误或者内核错误。

完成这次IDT表的初始化之后,内核还会进行第二次初始化,用真正的trap或中断处理函数代替刚才的null函数。一旦这两步初始化都完成,IDT表就包含具体的中断、陷阱和系统门,用以控制每个中断请求。

对于IDT表的第二次初始化过程,我们将分别以异常和中断的视角分开阐述。请参考后面的文章。

相关文章
|
3月前
|
安全 网络协议 Linux
深入理解Linux内核模块:加载机制、参数传递与实战开发
本文深入解析了Linux内核模块的加载机制、参数传递方式及实战开发技巧。内容涵盖模块基础概念、加载与卸载流程、生命周期管理、参数配置方法,并通过“Hello World”模块和字符设备驱动实例,带领读者逐步掌握模块开发技能。同时,介绍了调试手段、常见问题排查、开发规范及高级特性,如内核线程、模块间通信与性能优化策略。适合希望深入理解Linux内核机制、提升系统编程能力的技术人员阅读与实践。
400 1
|
3月前
|
Ubuntu Linux
Ubuntu 23.04 用上 Linux 6.2 内核,预计下放到 22.04 LTS 版本
Linux 6.2 带来了多项内容更新,修复了 AMD 锐龙处理器设备在启用 fTPM 后的运行卡顿问题,还增强了文件系统。
|
3月前
|
Ubuntu Linux
Ubuntu 23.10 现在由Linux内核6.3提供支持
如果你想在你的个人电脑上测试一下Ubuntu 23.10的最新开发快照,你可以从官方下载服务器下载最新的每日构建ISO。然而,请记住,这是一个预发布版本,所以不要在生产机器上使用或安装它。
|
3月前
|
传感器 监控 Ubuntu
10 月发布,Ubuntu 23.10 已升级到 Linux Kernel 6.3 内核
硬件方面,Linux 6.3 引入了在 HID 中引入了原生的 Steam Deck 控制器接口,允许罗技 G923 Xbox 版赛车方向盘在 Linux 上运行;改善 8BitDo Pro 2 有线控制器的行为;并为一系列华硕 Ryzen 主板添加传感器监控。
|
3月前
|
Ubuntu Linux
Ubuntu24.04LTS默认采用Linux 6.8内核,实验性版本可通过PPA获得
IT之家提醒,当下的 Ubuntu 23.10 也是一个“短期支持版本”,该版本将在今年 7 月终止支持,而今年 4 月推出的 Ubuntu 24.04 LTS 长期支持版本将获得 5 年的更新支持。
|
3月前
|
监控 Ubuntu Linux
什么Linux,Linux内核及Linux操作系统
上面只是简单的介绍了一下Linux操作系统的几个核心组件,其实Linux的整体架构要复杂的多。单纯从Linux内核的角度,它要管理CPU、内存、网卡、硬盘和输入输出等设备,因此内核本身分为进程调度,内存管理,虚拟文件系统,网络接口等4个核心子系统。
293 0
|
3月前
|
Web App开发 缓存 Rust
|
3月前
|
Ubuntu 安全 Linux
Ubuntu 发行版更新 Linux 内核,修复 17 个安全漏洞
本地攻击者可以利用上述漏洞,攻击 Ubuntu 22.10、Ubuntu 22.04、Ubuntu 20.04 LTS 发行版,导致拒绝服务(系统崩溃)或执行任意代码。
|
3月前
|
Ubuntu 机器人 物联网
Linux Ubuntu 22.04 LTS 测试版实时内核已可申请
请注意,在启用实时内核后您需要手动配置 grub 以恢复到原始内核。更多内容请参考: