Double Fault & Triple Fault

之前我曾提到过intel x86 cpu的14号中断用于处理缺页异常,这也是被人熟知的一个中断号,事实上intel架构总共可用的中断号有256个(从0到255),其中前32个(从0到31)中断号保留给Intel架构内部使用,也就是说这些中断都有其特定的含义而不可被用户更改,而剩余的224(从32到255)个中断则可由用户自定义和实现其具体功能。本文重点关注前32个中断,更具体点说是其中的一个中断Double Fault,以及隐包含的另外一个中断Triple Fault。

前32个中断,每个中断的含义都可以在intel手册“Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 3A System Programming Guide.pdf”上找到对应的描述(Table 6-1. Protected-Mode Exceptions and Interrupts ),这里不再过多解释其它中断,重点看一下8号Double Fault中断。
Double Fault从字面上来讲,就是前后的动作连续触发两个中断的异常,比如一个缺页异常没有对应的异常中断处理函数(用c代码表示即为NULL,空指针),这就将会引发一个典型的Double Fault,在这种情况下,如果发生缺页异常则将触发一个中断,此时cpu要去调用缺页异常中断处理函数进行处理,但却发现没有这个处理函数,因而这又是一个异常而再次触发中断,此时触发的就是Double Fault异常中断。但是,值得注意的是并不是连续发生两个中断就是Double Fault,因为部分情况下,连续发生的两个中断可以依次被cpu处理。为了辨别什么情况下才会触发Double Fault,Intel cpu把32个中断分为3类:
Table 6-4. Interrupt and Exception Classes

ClassVector NumberDescription
Benign
Exceptions
and
Interrupts








16 
17 
18 
19 
All 
All 
Debug
NMI Interrupt
Breakpoint
Overflow
BOUND Range Exceeded
Invalid Opcode
Device Not Available
Coprocessor Segment Overrun
Floating-Point Error
Alignment Check
Machine Check
SIMD floating-point
INT n
INTR
Contributory
Exceptions

10 
11 
12 
13 
Divide Error
Invalid TSS
Segment Not Present
Stack Fault
General Protection
Page Faults14 Page Fault

而下表显示了会导致发生Double Fault的情况,即如果先发生某类型的异常,接着再发生某类型的异常,此时是否会导致发生Double Fault异常:
Table 6-5. Conditions for Generating a Double Fault

First ExceptionSecond Exception
BenignContributoryPage Fault
Benignxxx
ContributoryxDouble Faultx
Page FaultxDouble FaultDouble Fault

看看前面举的示例,先发生Page Fault,接着发生Contributory Exception(执行NULL空函数为Segment Not Present,11号中断),对应下表的确将引发Double Fault。事实上,这个示例有点特殊,其实不管前一个异常是Benign也好,Contributory也好,Page Fault也好,只要对应的中断函数为空,此时都会触发Double Fault,对应intel手册里的话为:“Any further faults generated while the processor is attempting to transfer control to the appropriate fault handler could still lead to a double-fault sequence.”
Double Fault不可恢复,所以在该中断的处理函数里能做的也只能是收集相关信息(以便进行Double Fault问题原因排查),主动关闭相关程序和服务(如果可以的话),让机器‘死’得优雅一点。
如果Double Fault异常中断的函数也为空,那么此时将发生Triple Fault异常,该异常并不是一个真正的异常,它没有对应中断号,也表示如果发生了Triple Fault异常这种情况,cpu都不管了,除了重启机器别无他法(一般外部硬件,比如主板会向cpu发出重启信号)。
触发Triple Fault的情况大部分是因为LDT或者GDT或IDT错误导致(被无意中踩坏?)或者内核栈溢出(或下溢),比如内核栈溢出,栈顶指针刚好达到一个无效内存页(即访问页不在内存中)而触发Page Fault中断,进而调用Page Fault异常中断函数进行处理,函数调用需要进行异常信息压栈(以传递给中断函数),但栈内存页不存在内存中,这又触发Page Fault异常,如此反复进而触发Double Fault、Triple Fault。
遇到Triple Fault怎么弄?额,我不知道,不过Linus给出了明确的答复,但那是03年的事情:
Triple faults (Linus Torvalds)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

Newsgroups: fa.linux.kernel

From: Linus Torvalds <torvalds@osdl.org>

Subject: Re: 2.6.0-test9-mm3

Original-Message-ID: <Pine.LNX.4.44.0311141344290.5877-100000@home.osdl.org>

Date: Fri, 14 Nov 2003 21:48:59 GMT

Message-ID: <fa.jjta19r.1gl80h1@ifi.uio.no>

On Fri, 14 Nov 2003, Martin J. Bligh wrote:

>

> Linus had some debug thing for triple faults, a few months ago, IIRC ...

> probably in the archives somewhere ...

Triple faults you can't debug, they raise a line outside the CPU, and

normal PC hardware will cause that to just trigger a reboot.

But double faults do get caught, and that debugging stuff actually is in

the standard kernel. It won't give _nearly_ as good a debug report as a

"normal" oops, since I didn't want the double-fault handler to touch

anything even remotely unsafe, but it often gives a good hint about what

might be wrong. Certainly better than triple-faulting did (which we still

do for _catastrophic_ corruption, eg totally munged kernel page tables etc

- it's just very hard to avoid once you get corrupted enough).

        Linus

&lt;think&gt;嗯,用户让我解释这段Arduino代码,看起来是关于MAX31865 RTD温度传感器的使用。首先,我需要理解这段代码的结构和每个部分的作用。用户可能正在学习如何使用这个传感器,或者遇到了问题需要调试。 首先,代码在void loop()里循环执行。开头是读取RTD的值,然后进行一系列的计算和打印。然后检查故障标志,如果有故障就输出对应的信息,并清除故障。最后延迟1秒。用户可能需要了解每个步骤的具体含义,以及如何正确配置和使用MAX31865库。 接下来,我要确保解释清楚每个函数和变量的作用。比如,max.readRTD()返回的是什么?是原始数据还是已经转换过的?这里的rtd变量是uint16_t类型,处理时可能需要转换为浮点数来计算比例。ratio的计算为什么除以32768?这可能与MAX31865的分辨率有关,32768是2的15次方,可能是将16位有符号数转换为比例值。 然后,Resistance的计算用了RREF乘以ratio,RREF是参考电阻的值,通常为400或430欧姆,这需要用户根据实际电路设置。温度计算使用max.temperature()函数,传入的100可能是指RTD的类型,比如PT100,而RREF同样是参考电阻。 故障检测部分,用户可能遇到各种错误,需要解释每个故障标志的含义,比如高低阈值、参考电压问题、RTD输入问题以及电压异常。这些故障信息能帮助用户诊断接线是否正确,传感器是否损坏,或者配置是否有误。 用户可能没有提到的深层需求是如何正确连接MAX31865和Arduino,或者如何初始化传感器,比如设置偏置电流、滤波器等。但代码中可能没有这部分,需要提醒用户检查初始化代码,比如begin()方法的参数是否正确。 另外,延迟1秒可能影响实时性,但作为示例可能足够。用户可能需要根据应用调整延迟时间。此外,打印的数值格式,比如ratio保留8位小数,是否有必要,或者是否会影响可读性。 最后,要确保用户理解代码的结构,能够根据输出调试问题,比如检查电阻值是否合理,温度是否正确,或者根据故障代码排查硬件连接。可能还需要提醒用户确保库已正确安装,电路连接正确,参考电阻匹配RTD类型等潜在问题。&lt;/think&gt;以下是对这段Arduino代码的逐层解析: ==== 代码功能概述 ==== 这是一个基于MAX31865芯片读取RTD温度传感器(如PT100)的循环程序,包含数据读取、转换计算、故障检测和串口输出功能。适用于温度监控系统开发。 ==== 核心代码结构 ==== $void loop()$ 函数以1秒间隔循环执行以下流程: 1. 读取原始RTD数值 2. 计算电阻比例和实际电阻值 3. 转换为温度值 4. 检测并处理故障状态 ==== 关键模块解析 ==== 1. 数据读取: - $uint16_t rtd = max.readRTD();$ ▸ 获取16位原始RTD测量值(范围0-32767) ▸ 实际对应公式:$$ RTD = \frac{V_{RTD}}{V_{REF}} \times 2^{15} $$ 2. 比例计算: - $ratio /= 32768;$ ▸ 将原始值转换为电压比例系数 ▸ 数学表达式:$$ \text{ratio} = \frac{\text{rtd}}{2^{15}} $$ 3. 电阻计算: - $R_{RTD} = R_{REF} \times \text{ratio}$ ▸ $R_{REF}$为参考电阻值(通常400&Omega;或430&Omega;) 4. 温度转换: - $max.temperature(100, RREF)$ ▸ 第一个参数100表示PT100型RTD ▸ 内部使用Callendar-Van Dusen方程计算温度 ==== 故障检测系统 ==== 采用位掩码检测故障类型: ```cpp uint8_t fault = max.readFault(); if (fault) { // 位检测示例: if (fault &amp; MAX31865_FAULT_HIGHTHRESH) &rarr; 电压超上限 } ``` 共检测6类故障: 1. 高低阈值越界 2. 参考电压异常(REFIN-/RTDIN-) 3. 电源欠压/过压 ==== 调试优化建议 ===- 1. 关键参数验证: - 确认$R_{REF}$值与实际使用电阻一致 - 检查PT类型参数(PT100/PT1000) 2. 数据精度提升: - 可增加滤波算法:$$ T_{avg} = \frac{1}{N}\sum_{i=1}^{N}T_i $$ - 修改串口输出格式:Serial.println(ratio,8) 中的精度位数 3. 故障处理增强: - 添加自动复位机制 - 记录故障发生时间戳 ==== 典型输出示例 ==== RTD value: 24576 Ratio = 0.75000000 Resistance = 300.00000000 Temperature = 64.82 Fault 0x02 RTD High Threshold 该代码需要配合MAX31865库和正确的外部电路连接使用,实际应用时需注意RTD传感器的三线/四线制接线配置。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值