前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Linux内核17-硬件如何处理中断和异常

Linux内核17-硬件如何处理中断和异常

作者头像
Tupelo
发布于 2022-08-15 07:49:12
发布于 2022-08-15 07:49:12
2.1K0
举报
文章被收录于专栏:嵌入式ARM和Linux嵌入式ARM和Linux

在上一篇文章中,我们已经了解了中断和异常的一些概念,对于中断和异常也有了大概的理解。那么,系统中硬件到底是如何处理中断和异常的呢?本文我们就以常见的X86架构为例,看看中断和异常的硬件工作原理。

1 高级可编程中断控制器-APIC

之前,我们主要考虑的单处理器系统,如果是多处理器系统,主PIC控制器的INTR管脚是如何接到CPU上的?我们接下来讨论这个话题。

我们知道,多核处理系统的价值在于 并行处理。所以,如何把中断分配到每一个CPU上就至关重要了。基于这个原因,Intel从奔腾III开始,引入一个新的高级可编程中断控制器(I/O-APIC)。这个控制器是8259A中断控制器的加强版。为了兼容旧版本的操作系统,有些主板包含这两种芯片。x86架构中,每个处理器包含自己的APIC,每个APIC具有32位的寄存器,内部时钟,内部定时器以及2个额外的IRQ线,LINT0和LINT1,用作APIC的中断。所有私有的APIC都连接到I/O-APIC,组成一个多APIC系统。

图4-1展示了一个多APIC系统的原理图。I/O-APIC通过APIC总线和各个APIC连接在一起。I/O-APIC相等于一个中继的角色。

图4-1 多APIC系统

I/O-APIC由24条中断线,中断重定向表,可编程寄存器和一个通过APIC总线收发数据的消息单元组成。与8259A中断控制器不同,管脚编号不再具有优先级:重定向表中的每一项都可以被独立设置中断向量和优先级,目的处理器以及处理器如何处理该中断。也就是说,中断重定向表就是外部IRQ到私有APIC的映射关系。

中断请求被分配到CPU上的方式有两种:

  1. 静态分配 按照重定向表中的定义把IRQ请求分配到相应的私有APIC高级可编程中断控制器上。中断可以指定给单个CPU,或者一组CPU,或者所有的CPU(相当于广播)。
  2. 动态分配 IRQ请求被发送给正在运行低优先级进程的处理器的私有APIC中断控制器上。通俗地说,就是哪个处理器正在运行低优先级任务,IRQ请求就发送给谁。 每个私有APIC都有一个可编程任务优先级寄存器,用来保存当前运行任务的优先级。Intel期望每次进程切换的时候,操作系统内核修改这个寄存器。 如果有多个CPU拥有相同的最低任务优先级,则使用仲裁技术分配中断请求。根据仲裁,每个CPU被分配一个不同的优先级(0-15,数字越小,优先级越大),这个优先级存储在私有APIC的任务优先级寄存器中。 分配策略是,每当分配一个中断请求给一个CPU,则它对应的仲裁优先级被自动设为0,而其它CPU的仲裁优先级则被增加。当优先级寄存器中的值大于15时,则设为1。因为具有相同任务优先级的CPU的中断分配使用循环方式进行。 动态分配的策略就是负载均衡的一种手段。关于负载均衡的算法以后再研究。

除了CPU与外设之间的中断,多APIC系统还允许CPU产生CPU之间的中断。当一个CPU想给另一个CPU发送中断时,它就会把目标CPU的私有APIC的标识符和中断号存储到自己APIC的中断命令寄存器(ICR)中。然后通过APIC总线发送给目标APIC,该APIC就会给自己的CPU发送一个相应的中断。

CPU间的中断(简称IPI)是多核系统一个重要组成部分。Linux有效地利用它们,在CPU之间传递消息。

目前,大部分的单核系统也都包含一个I/O-APIC芯片,可以使用两种不同的方式配置它:

  1. 当一个标准的8259A类型的外部PIC使用。私有APIC被禁止,LINT0和LINT1这两个IRQ请求线被分别配置为INTR和NMI管脚。
  2. 作为标准的I/O-APIC使用,只不过只有一个CPU而已。

2 异常

x86架构大约有20种不同的异常。内核必须为每种异常提供专用的处理函数。对于某些异常,CPU控制单元也会产生硬件错误码,并将其压入内核态栈,然后再启动异常处理函数。

下表是异常列表,列出了异常号,名称,类型等等。更多信息请参考Intel技术手册。

#

异常

类型

异常处理函数

信号

0

除法错误

fault

divide_error()

SIGFPE

1

Debug

trap/fault

debug( )

SIGTRAP

2

NMI

-

nmi( )

-

3

断点

trap

int3( )

SIGTRAP

4

溢出

trap

overflow( )

SIGSEGV

5

边界检查

fault

bounds( )

SIGSEGV

6

非法操作码

fault

invalid_op( )

SIGILL

7

设备不可用

fault

device_not_available( )

-

8

串行处理异常错误

abort

doublefault_fn()

-

9

协处理器错误

abort

coprocessor_segment_overrun( )

SIGFPE

10

非法TSS

fault

invalid_TSS( )

SIGSEGV

11

段引用错误

fault

segment_not_present( )

SIGBUS

12

栈段错误

fault

stack_segment( )

SIGBUS

13

通用保护

fault

general_protection( )

SIGSEGV

14

页错误

fault

page_fault( )

SIGSEGV

15

Intel保留

-

-

-

16

浮点错误

fault

coprocessor_error( )

SIGFPE

17

对齐检查

fault

alignment_check( )

SIGBUS

18

机器检查

abort

machine_check()

-

19

SIMD浮点异常

fault

simd_coprocessor_error()

SIGFPE

Intel保留20-31未来使用。如上表所示,每个异常都有一个专门的处理函数处理,并给造成异常的进程发送一个信号。

3 中断描述符表

现在,我们已经知道了中断信号是如何从设备发出,然后经过高级可编程中断控制器的分配,到达各个指定的CPU中。那么,剩下的工作就是内核的了,内核使用一个中断描述符表(IDT),记录每个中断或者异常编号以及相应的处理函数。那么,收到中断信号后,将相应的处理函数的地址加载到eip寄存器中执行即可。

IDT表中,每一项对应一个中断或者异常,大小8个字节。因而,IDT需要256x8=2048个字节大小的存储空间。

IDT表的物理地址存储在CPU寄存器idtr中:包括IDT的基地址和最大长度。在使能中断之前,必须使用lidt汇编指令初始化IDT表。

IDT表包含三种类型的描述符,使用Type位域表示(40-43位)。下图分别解释了这三种描述符各个位的意义。

三种描述符分别为:

  1. 任务门 包含中断发生时要替换当前进程的新进程的TSS选择器。
  2. 中断门 包含段选择器和在段中的偏移量。设置了正确的段后,处理器清除IF标志,禁止可屏蔽中断。
  3. 陷阱门 同中断门类似,只是不会修改IF标志。

4 中断和异常的硬件处理

现在,我们来探究一下CPU控制单元是如何处理中断和异常的。我们假设内核已经完成初始化,CPU工作在保护模式下。

CPU控制单元,在取指令之前,检查控制单元在执行前一条指令的时候是否有中断或异常发生。如果发生中断,控制单元就会做如下处理:

  1. 确定中断或异常的编号N;
  2. 读取IDT表中的第N项;(在后面的描述中,假设包含的是中断门或陷阱门)
  3. 获取GDT的基地址,遍历GDT找到IDT表第N项中的段选择器标识的段描述符。这个描述符指定了包含中断或异常处理程序的段的基地址。
  4. 确保中断合法性。 首先比较cs寄存器中的CPL(当前特权等级)和包含在GDT中的段描述符的DPL(描述符特权等级),如果CPL小于DPL,产生 通用保护 异常,因为中断处理程序的特权等级不能比造成中断的程序的低。对于可编程异常,还会做进一步的安全检查:比较当前特权等级(CPL)和IDT表中包含的描述符的DPL,如果DPL小于CPL,则产生通用保护的异常。后一项检查,可以阻止用户应用程序访问特定的trap或中断门。
  5. 检查特权等级是否发生变化。如果CPL与描述符中的DPL不同,控制单元应该使用新特权等级下的堆栈。 其实对于Linux来说,只使用了supervisor和user两种特权等级。所以中断应该都是在supervisor特权等级下运行。
    1. 读取tr寄存器,访问运行中的进程的TSS段;
    2. 使用新特权等级对应的堆栈段和堆栈指针加载ss和esp寄存器;(这些值存储在TSS中)
    3. 在新的堆栈中,保存旧任务的ss和esp寄存器值。(处理完中断或异常后,还要恢复到旧任务执行)
  6. 根据造成异常的指令的逻辑地址,加载cs和eip寄存器(异常解决后,程序可以继续从这儿执行);
  7. 保存eflags、cs和eip到堆栈中;
  8. 如果异常携带异常错误码,将其保存在堆栈中;
  9. 根据IDT表中的第N项内容,加载cs和eip寄存器。

至此,CPU控制单元跳转到中断或异常处理程序处开始执行。等到中断或异常处理完成后,把CPU的使用权让给之前被中断的进程,使用iret指令,该指令强迫控制单元执行下面步骤:

  1. 加载被中断进程的cs,eip和eflags寄存器。(如果压栈过异常错误码,应该在执行iret指令之前弹出)
  2. 检查CPL是否等于cs寄存器中的CPL,如果相等,则iret指令结束执行;否则,继续。
  3. 加载旧特权等级的ss和esp寄存器值。
  4. 检查ds、es、fs和gs寄存器中的值。如果它们之中任何一个的描述符中的DPL小于CPL,则清除相应的段寄存器。这么做,可以禁止用户态程序使用先前内核态的段寄存器。如果这些寄存器没有被清除,恶意用户态程序就可以利用它们访问内核地址空间。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 嵌入式ARM和Linux 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Hadoop--HDFS API编程封装
HDFS是一个分布式文件系统,既然是文件系统,就可以对其文件进行操作,比如说新建文件、删除文件、读取文件内容等操作。下面记录一下使用JAVA API对HDFS中的文件进行操作的过程。   对分HDFS中的文件操作主要涉及一下几个类:   Configuration类:该类的对象封转了客户端或者服务器的配置。   FileSystem类:该类的对象是一个文件系统对象,可以用该对象的一些方法来对文件进行操作。FileSystem fs = FileSystem.get(conf);通过FileSystem的静态
汤高
2018/01/11
1.1K0
HDFS系列(6) | HDFS的java API操作
在之前的博客《HDFS系列(5) |进行API操作前的准备》中,博主为大家分享的是在进行API操作前的准备工作。而本篇博客,博主为大家展现HDFS的API操作。 1. HDFS文件上传 1. 源码: package com.buwenbuhuo.hdfs; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; imp
不温卜火
2020/10/28
9030
HDFS系列(6) | HDFS的java API操作
大数据 | Java 操作 HDFS 常用 API
上篇文章介绍了关于 HDFS 的常用命令,其常用的命令都有相应的 API,用命令可以完成的功能,使用 Java API 也可以完成。本文介绍关于 HDFS 常用的 Java API。
码农UP2U
2021/11/17
8190
HDFS-常用API操作
可以在项目的src/main/resources目录下,新建一个文件,命名为“log4j.properties”,在文件中填入
栗筝i
2022/12/01
2560
hadoop: hdfs API示例
利用hdfs的api,可以实现向hdfs的文件、目录读写,利用这一套API可以设计一个简易的山寨版云盘,见下图:
菩提树下的杨过
2018/09/20
1.6K0
hadoop: hdfs API示例
Hadoop之HDFS04【JavaAPI操作】
  前面项目中我们是创建的java项目来演示的,但是hadoop相关的依赖太多了,不方便,本文通过maven项目来演示HDFS的java API操作
用户4919348
2019/04/18
4680
javaAPI操作hadoop hdfs
写在之前 在开始操作之前请确保已经正确安装启动hadoop并且能够连接到 依赖 <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency> <dependency> <groupId>org.apache.hadoop</groupId>
许喜朝
2020/09/29
6450
HDFS JAVAAPI总结
maven仓库 <dependencies> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-common</artifactId> <version>2.7.5</version> </dependency> <dependency> <groupId>org.apache.hadoop</groupId> <ar
Maynor
2022/11/30
4410
HDFS基本操作
一、hadoop搭建     1、修改主机名     2、ip等     3、主机映射     4、关闭防火墙(两种)     5、ssh免密登录(免密脚本)     6、安装jdk 配置环境变量     7、安装hadoop         配置文件的修改         hadoop-env.sh    JAVA_HOME         core-site.xml    客户端参数    namenode在哪里         hdfs-site.xml    namenode和datanode存放的目录         mapred-site.xml    经过重命名之后才得到的文件,提交任务到哪里         yarn-site.xml    配置resourcemanager在哪里,资源的多少         HADOOP环境变量     8、安装程序分发集群         hosts文件         jdk安装文件    /etc.profile         hadoop安装文件     9、namenode进行格式化         hadoop namenode -format     10、启动测试         start-dfs.sh         hadoop-deams.sh    单独启动单台机器的进程         start-yarn        是在resourcemaneger启动的 二、shell命令操作hdfs
曼路
2018/10/18
8150
《快学BigData》--Hadoop总结(F)(39)
Hadoop总结 - - - - - - - - - - - - - - - - - - - - - - - - - - - - 210
小徐
2019/08/05
2950
Hadoop基础教程-第4章 HDFS的Java API(4.7 Java API封装类)(
第4章 HDFS的Java API 4.7 Java API封装类 package cn.hadron.hdfsDemo; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSys
程裕强
2022/05/06
2520
Hadoop基础教程-第4章 HDFS的Java API(4.7 Java API封装类)(
Hadoop(八)Java程序访问HDFS集群中数据块与查看文件系统
前言   我们知道HDFS集群中,所有的文件都是存放在DN的数据块中的。那我们该怎么去查看数据块的相关属性的呢?这就是我今天分享的内容了 一、HDFS中数据块概述 1.1、HDFS集群中数据块存放位置   我们知道hadoop集群遵循的是主/从的架构,namenode很多时候都不作为文件的读写操作,只负责任务的调度和掌握数据块在哪些datanode的分布,   保存的是一些数据结构,是namespace或者类似索引之类的东西,真正的数据存储和对数据的读写是发生在datanode里的。   找到${HADOO
用户1195962
2018/01/18
1.2K0
Hadoop(八)Java程序访问HDFS集群中数据块与查看文件系统
HDFS Java API
官网 http://hadoop.apache.org/docs/r2.7.3/api/index.html
程裕强
2022/05/06
1.6K0
HDFS Java API
HDFS之使用Java客户端对文件的一些操作
在这里总结了一下使用java对HDFS上文件的操作,比如创建目录、上传下载文件、文件改名、删除…… 首先对一些类名、变量名做出解释说明: FileSystem: 文件系统的抽象基类 FileSystem的实现取决于fs.defaultFS的配置! 有两种实现! LocalFileSystem: 本地文件系统 fs.defaultFS=file:/// DistributedFileSystem: 分布式文件系统 fs.defaultFS=hdfs://xxx:9000 声明用户身份:
孙晨c
2020/07/08
1.2K0
一脸懵逼学习hadoop之HDFS的java客户端编写
该文章是一篇关于Java编程的文章,主要介绍了Java中的各种基础概念和常见用法,包括Java中的基本数据类型、流程控制、函数和方法、面向对象编程等内容。文章还介绍了Java中的异常处理、文件操作、网络编程等方面的内容。文章旨在帮助读者掌握Java编程的基础知识,并学会如何编写高质量的Java代码。
别先生
2018/01/02
7140
快速学习-HDFS客户端操作
需要在项目的src/main/resources目录下,新建一个文件,命名为“log4j.properties”,在文件中填入
cwl_java
2020/02/19
6000
HDFS Java API 实践
安装集群:https://michael.blog.csdn.net/article/details/114607857
Michael阿明
2021/09/06
4170
【详解】HadoopHDFS操作实例
Hadoop分布式文件系统(HDFS)是Hadoop项目的核心组件之一,它是一个高容错性的数据存储系统,非常适合处理大规模的数据集。本文将通过几个实际的操作示例来介绍如何在Hadoop的HDFS中进行常见的文件操作。
大盘鸡拌面
2025/01/14
1500
Hadoop 使用Linux操作系统与Java熟悉常用的HDFS操作
1.向HDFS中上传任意文本文件,如果指定的文件在HDFS中已经存在,则由用户来指定是追加到原有文件末尾还是覆盖原有的文件;
小小程序员
2023/03/09
2.3K0
Hadoop 使用Linux操作系统与Java熟悉常用的HDFS操作
Win10 IDEA连接虚拟机中的Hadoop(HDFS)
IDEA自带Maven,如果需要自己安装Maven可以参考Maven安装教程 创建项目,选择Maven,模板选择第一个maven-archetype-archetype
超级小的大杯柠檬水
2024/11/21
1390
Win10 IDEA连接虚拟机中的Hadoop(HDFS)
相关推荐
Hadoop--HDFS API编程封装
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档