x86中断基础

news2024/10/1 5:35:52

x86中断基础

原文:Basic x86 interrupts 

作者:Alex Dzyoba

原文发表日期:2016年4月2日

在我的关于多重引导内核的文章中,我们看到了如何加载内核、打印文本,然后停止。然而要让操作系统可用,需要支持键盘输入,将敲击的按键打印在屏幕上。

所需的工作会超出你最初的估计,因为需要初始化x86中断。x86中断是一种古怪而繁琐的机制,拥有超过40年的历史。

1. x86中断

中断是设备发送给CPU的事件,用以通知CPU设备有新的消息:比如用户在键盘上输入,或者网络报文到达。如果没有中断,系统需要轮询所有外部设备,这会浪费CPU时间,增加延迟。

中断按照来源可以分为三种类型:

  1. 硬件中断。来自硬件设备,比如键盘或网卡。
  2. 软件中断。由软件通过int指令产生。在引入sysenter和sysexit指令之前,Linux的系统调用就是通过中断0x80实现的。
  3. 异常。在遇到诸如“除零”或“页错误”的异常时,由CPU产生。

x86中断系统由三个部分共同构成,初始化操作也分为三步:

  1. 配置可编程中断控制器(PIC)从设备接收中断请求(IRQs),发送给CPU。
  2. 配置CPU从PIC接收中断请求,通过中断描述符表(IDT)中定义的调用门(gate),调用相应的中断处理程序。
  3. 操作系统需要提供中断服务程序(ISRs)来处理中断。操作系统要支持被中断抢占,并配置PIC和CPU启用中断。

请在阅读文章时回顾下面的参考图。

 

在配置中断之前,我们需要像之前那样设置好GDT。

2. 可编程中断控制器(PIC)

PIC是替代CPU连接各种外部设备的硬件,本质上它是一个多路复用器或代理。PIC可以减少CPU引脚,提供多项功能:

  • 通过级联支持更多的中断线。2个级联的PIC可以支持15个中断线。
  • 可以屏蔽特定中断,无需屏蔽全部中断(cli)。
  • 中断排队,即对传递给CPU的中断排序。当某些中断被屏蔽时,PIC将中断加入队列延迟发送,不会直接丢弃中断。

最初的IBM PC有独立的8259 PIC芯片。后来它被集成为南桥/ICH/PCH的一部分。现代PC系统使用高级可编程中断控制器(APIC)来解决多处理器机器上的中断路由问题。但为了向后兼容,APIC可以模拟8259 PIC。只要不使用旧硬件,你实际上使用的是手动配置或由BIOS自动配置的APIC。在本文中,我将使用BIOS自动配置的APIC,不会手动配置PIC。原因有两个:首先PIC有很多常人难以理解的怪异之处。其次,后续我们将为SMP配置APIC。BIOS自动按照IBM PC AT机器的模式配置APIC,即2个级联的PCI和15个中断线。

除了连接引发CPU中断的中断线,PIC还连接CPU数据总线。数据总线用于将IRQ号从PIC发送到CPU,并将配置命令从CPU发送到PIC。配置命令包括PIC初始化、IRQ屏蔽、中断结束(EOI)命令等。

3. 中断描述符表(IDT)

中断描述符表(IDT)是一个系统表,保存了中断服务程序(ISR)或简单中断处理程序的描述符。

实模式下有一个中断向量表(IVT)。中断向量表固定在0x0位置,包含以CS和IP描述的中断处理函数指针。这确实不是一种灵活的方式,并且依赖于分段内存管理。自从80286以来,保护模式使用中断描述符表(IDT)。

IDT是内存中的表,由操作系统建立和维护。IDT地址保存在idtr寄存器中。使用lidt指令可以将IDT地址加载到idtr寄存器。IDT只能在保护模式下使用。IDT条目包含门描述符,其中有32位的中断处理程序(ISR)地址、标志位和特权等级。IDT条目是中断门的描述符,类似于GDT中的段描述符。

描述符的主要部分是偏移量。偏移量实质上是由段选择子指定的代码段中ISR的指针。段选择子包含GDT表索引、表指示器(指定GDT或LDT)和特权级别(RPL)。对于中断门,选择子指定的是GDT中的内核代码段。

调用门描述符中的类型(type)域定义了调用门类型:任务、陷阱或中断。对于中断处理函数,需要使用中断调用门。调用中断门时,CPU会清除IF标志位。基本上在设置IDT时,各描述符只有偏移量,即ISR函数地址是不同的。

4. 中断服务程序(ISR)

IDT的主要目的是存储指向ISR的指针,CPU在收到中断后自动调用ISR。你不能手动调用中断处理程序。一旦配置好了IDT并打开中断(sti),在发生中断时,在完成一些幕后工作后,CPU将控制传递给中断处理函数。了解这些幕后工作的内容是很重要的。

如果中断发生在用户空间(即中断发生在不同特权级别的代码段),CPU执行下列操作:

  1. 在内部临时保存SS、ESP、EFLAGS、CS和EIP的值。
  2. 从TSS为新栈(即中断处理函数栈)加载段选择子和栈指针到SS和ESP,切换到新栈。
  3. 将临时保存的SS、ESP、EFLAGS、CS和EIP压栈。
  4. 如果有错误代码,将错误码压栈。
  5. 从中断门或陷阱门加载新代码段的段寄存器和指令指针到CS和EIP寄存器。
  6. 如果调用通过中断门进行,清除EFLAGS寄存器的IF标志位。
  7. 以新的特权等级开始执行中断处理函数。

如果中断发生在内核空间,CPU不会切换堆栈,这意味着内核空间的中断没有自己的堆栈,而是使用被中断函数的堆栈。在x64系统上,由于red zone的存在,内核中断可能会导致堆栈损坏。这就是内核代码需要使用参数-mno-red-zone编译的原因。关于这点我有一个有趣的故事。

当内核模式下发生中断时,CPU将:

  1. 将EFLAGS、CS和EIP的值压栈。
  2. 如果有错误码,将其压栈。
  3. 从中断门或陷阱门中加载新代码段的段选择子和指令指针到CS和EIP寄存器。
  4. 如果调用的是中断门,清除EFLAGS寄存器的IF标志位。
  5. 开始执行中断处理程序。

请注意这两种情况的区别在于压栈数据不同。EFLAGS、CS和EIP一定会压栈。当用户空间中断时,还会将SS和ESP压栈。

这意味着当中断处理程序开始执行时,它拥有下面的堆栈:

 

那么,当控制传递给中断处理程序时,它应该做什么呢?

记住,中断发生在用户空间或内核空间某些代码执行过程的中间,因此首先要做的是在处理中断之前保存被中断过程的状态。过程状态由寄存器的值定义。指令pusha可以将通用寄存器的值保存到栈中。

下一步是修改段寄存器,完全切换到中断处理程序的运行环境。CPU会自动切换CS,因此中断处理程序需要加载4个段寄存器DS、FS、ES和GS。不要忘记保存这些寄存器的原值,并在将来恢复。

当状态已保存、执行环境就绪后,中断处理程序可以开始工作。不过首要的是向PIC发送命令EOI来确认中断。

最后,在完成所有工作之后,中断处理程序要干净的退出。恢复被中断过程的状态(恢复数据段寄存器,执行popa),开启在CPU进入ISR时关闭的中断(sti),并调用iret将控制权返回被中断过程。

下面是基本的ISR处理过程:

  1. 保存被中断过程的状态。
  2. 保存数据段寄存器。
  3. 使用内核数据描述符加载数据段寄存器。
  4. 向PIC发送EOI命令确认中断。
  5. 处理中断。
  6. 恢复数据段寄存器。
  7. 恢复被中断过程的状态。
  8. 启用中断。
  9. 调用iret退出中断处理程序。

5. 汇总

现在可以完成全景图,让我们看看键盘事件是如何被处理的:

  1. 设置中断:
    1. 创建IDT表。
    2. 设置编号为9的IDT条目,将中断门指向键盘ISR。
    3. 使用lidt加载IDT地址。
    4. 发送中断屏蔽码0xfd(11111101)到主PIC,恢复IRQ1。
    5. 使用sti启用中断。
  2. 用户点击键盘按钮。
  3. 键盘控制器在主PIC中引发中断IRQ1。
  4. PIC确认中断IRQ1没有被屏蔽,向CPU发送中断号9。
  5. CPU检查EFLAGS的IF标志位,确认中断未被屏蔽。
  6. (假设当前正在内核模式下运行)
  7. CPU将EFLAGS、CS和EIP压栈。
  8. 如果PIC有错误码,将其压栈。
  9. 查询idtr寄存器指向的IDT,从描述符9中获得段选择子。
  10. 检查特权级别,将段选择器和ISR地址加载到CS:EIP。
  11. 因为IDT条目是中断门,CPU清除IF标志。
  12. 将控制权传递给ISR。
  13. ISR接收中断:
    1. 使用cli屏蔽中断。
    2. 使用pusha保存被中断过程的状态。
    3. 将DS压栈。
    4. 从内核数据段加载DS、ES、FS、GS。
  14. 发送命令EOI(0x20)到主PIC(I/O端口0x20),确认中断。
  15. 从键盘控制器(I/O端口0x64)读取键盘状态。
  16. 如果状态是1,从键盘控制器(I/O端口0x60)读取键码。
  17. 最后,写VGA缓冲区或发送字符到TTY。
  18. 从中断处理程序返回:
    1. 从堆栈中弹出并恢复DS。
    2. 使用popa恢复被中断过程状态。
    3. 使用sti启用中断。
    4. 调用iret退出中断处理程序。

请注意每次敲击按键时,上述步骤都会发生。也别忘了有几十个其他类型的中断,如时钟、网络数据包等。有些中断是悄悄处理的,你可能都没有注意到。你能想象硬件有多快吗?你能想象你使用的操作开发得有多好吗?现在考虑一下,给操作系统作者和硬件设计师一个大大的表扬。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/428955.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[chapter 26][PyTorch][MNIST 测试实战】

前言 这里面结合手写数字识别的例子,讲解一下训练时候注意点 目录 训练问题解决方案参考代码一 训练问题 训练的时候,我们的数据集分为Train Data 和 validation Data。 随着训练的epoch次数增加,我们发现Train Data 上精度 先逐步增加,但…

协议篇之以太网ARP协议

协议篇之以太网ARP协议一、什么是ARP协议?作用是什么?二、ARP请求与ARP应答三、以太网ARP数据报格式四、总结一、什么是ARP协议?作用是什么? ARP(Address Resolution Protocol),地址解析协议&am…

GUID分区与MBR分区有什么区别? 操作系统知识

GUID分区与MBR分区有什么区别? 操作系统知识 1、MBR分区表类型的磁盘 主引导记录(Master Boot Record,缩写:MBR),又叫做主引导扇区,它仅仅包含一个64个字节的硬盘分区表。由于每个分区信息需要…

以ChatGPT为例进行自然语言处理学习——入门自然语言处理

⭐️我叫忆_恒心,一名喜欢书写博客的在读研究生👨‍🎓。 如果觉得本文能帮到您,麻烦点个赞👍呗! 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧,喜欢的小伙伴给个三…

Python办公自动化之PostgreSQL篇2——利用Python连接PostgreSQL并读取一张表

在上一篇我们已经安装好了最新的PostgreSQL,以及最方便的可视化工具,Navicat 如果错过的小伙伴,可以去上一篇查看:点我查看 今天我们来用Python连接一下PostgreSQL,然后准备一张测试表,导入PostgreSQL&am…

elasticsearch 拼音分词器 自动补全。

elasticsearch 拼音分词器 & 自动补全。 文章目录elasticsearch 拼音分词器 & 自动补全。2. 自动补全。2.1. 拼音分词器。2.2. 自定义分词器。2.3. 自动补全查询。2.4. 实现酒店搜索框自动补全。2.4.1. 修改酒店映射结构。2.4.2. 修改 HotelDoc 实体。2.4.3. 重新导入。…

Shader Graph10-Min, Max, Clamp, Saturate节点

打开UE,新建Material叫做DemoMinMaxClamp,双击打开 一、Minimum节点,两个值比较取较小的。 Min的含义是,红框的0.5为参数B的值,1.0为白色圆形的值,下面的0.5为背景颜色值。图片中每个像素值与0.5进行比较&a…

java基于mvc的停车收费系统mysql

系统需要解决的主要问题有: (1)车位管理模块 添加车位、查看车位状态、车位信息查询等。 (2)客户信息管理模块 客户基本信息录入、客户信息查询等。 (3)卡业务办理 添加卡信息、查余额查询、卡充值。 (4)车辆信息管理模块 车牌信息录入等。 (5)收费管理 可以调整相应…

【Java 数据结构】集合类 (精华篇)

🎉🎉🎉点进来你就是我的人了 博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!人生格言:当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友一起加油喔🦾&am…

一本通 3.4.3 图的连通性

1383:刻录光盘(cdrom) 【题目描述】 在FJOI2010夏令营快要结束的时候,很多营员提出来要把整个夏令营期间的资料刻录成一张光盘给大家,以便大家回去后继续学习。组委会觉得这个主意不错!可是组委会一时没有足够的空光盘&#xff…

数学术语——指数的发展历程

指数的发展历程 指数(exponents)的历史可以追溯到许多世纪以前,欧几里德(Euclid)被认为是第一个已知的指数用法。他用“幂(power)”这个词来表示我们今天所知的一个数自乘的次数(注:底数连同其右上角的指数一起的整体形式称为“幂”)。古希腊数学家使用…

寄存器:计算机中的小而强大的存储器件

目录 什么是寄存器? 寄存器的作用 提高计算机的性能 存储处理器需要快速访问的数据 存储函数调用时的参数和返回值 存储中间计算结果 寄存器的种类 程序计数器 指令寄存器 状态寄存器 通用寄存器 寄存器的进化过程 寄存器:计算机中的小而强大…

Linux操作基础(文件系统和日志分析)

文章目录一、inode与block1.1inode和block概述1.2 inode包含文件的元信息1.3 linux文件系统的三个时间戳1.4 inode的号码1.5 inode的大小1.6 inode号的特点1.7软连接与硬链接二 、文件恢复2.1 xfsdump恢复2.2 opic恢复方式三 、日志文件3.1 日志文件的分类3.2 日志的格式3.3 常…

大数据分析案例-基于决策树算法构建信用卡违约预测模型

🤵‍♂️ 个人主页:艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞&#x1f4…

定制你的专属大模型 Finetuner+体验开启!

如 ChatGPT、GPT4 这样的大型语言模型就像是你为公司请的一个牛人顾问,他在 OpenAI、Google 等大公司被预训练了不少的行业内专业知识,所以加入你的公司后,你只需要输入 Prompt 给他, 介绍一些业务上的背景知识,他就能…

Flink学习:Flink如何打印窗口的开始时间和结束时间

Window一、简介二、代码实现三、测试一、简介 大家知道,Flink用水位线和窗口机制配合来处理乱序事件,保证窗口计算数据的正确性,当水位线超过窗口结束时间的时候,就会触发窗口计算 水位线是动态生成的,根据进入窗口的最大事件时间-允许延迟时间 那么窗口的开始时间和结束时间…

力扣70爬楼梯:思路分析+优化思路+代码实现+补充思考

文章目录第一部分:题目描述第二部分:思路分析2.1 初步分析2.2 问题描述2.3 优化思路第三部分:代码实现第四部分:补充思考第一部分:题目描述 🏠 链接:70. 爬楼梯 - 力扣(LeetCode&am…

“衰老标志物”重磅综述:细胞衰老、器官衰老、衰老时钟及其应用

大家好,这里是专注表观组学十余年,领跑多组学科研服务的易基因。 随着人口老龄化程度不断加深,实现“健康老龄化(healthy aging)”已成为我国乃至世界迫切需要解决的重大社会和科学问题。据测算,我国60岁及…

LVGL界面开发之模拟器环境搭建

前言 通常我们在使用 LVGL 进行界面开发时,会先在PC上搭建模拟器环境,而不是直接烧录到硬件板子上,使用模拟器是百利而无一害的,而且它是跨平台的,任何Windows,Linux或macOS系统都可以运行PC模拟器。每当界…

网上投票系统的设计与实现(论文+源码)_kaic

摘要 随着全球Internet的迅猛发展和计算机应用的普及,特别是近几年无线网络的广阔覆盖以及无线终端设备的爆炸式增长,使得人们能够随时随地的访问网络,以获取最新信息、参与网络活动、和他人在线互动。为了能及时地了解民情民意,把…