Linux 中断机制(一)之中断和异常


一、什么是中断

1、概述

中断(interrupt)是指在 CPU 正常运行期间, 由外部或内部事件引起的一种机制。 当中断发生时,CPU 会停止当前正在执行的程序,并转而执行触发该中断的中断处理程序。处理完中断处理程序后,CPU 会返回到中断发生的地方, 继续执行被中断的程序。中断机制允许 CPU 在实时响应外部或内部事件的同时,保持对其他任务的处理能力。

中断的流程图如下:

2、中断的分类

中断通常被定义为一个事件,该事件改变处理器执行的指令顺序。这样的事件与 CPU 芯片内外部硬件电路产生的电信号相对应。

中断通常分为同步(synchronous)中断和异步(asynchronous)中断:

  • 同步中断是当指令执行时由 CPU 控制单元产生的,之所以称为同步,是因为只有在一条指令终止执行后CPU才会发出中断;
  • 异步中断是由其他硬件设备依照 CPU 时钟信号随机产生的。

在 Intel 微处理器手册中,把同步和异步中断分别称为异常(exception)和中断(interrupt)我们也采用这种分类,当然有时我们也用术语“中断信号”指这两种类型(同步及异步)。

二、中断和异常

1、中断和异常

Intel 文档把中断和异常分为以下几类:

  • 中断
    • 可屏蔽中断(maskabie interrupt
      I/O 设备发出的所有中断请求(IRQ)都产生可屏蔽中断。可屏蔽中断可以处于两种状态:屏蔽的(masked)或非屏蔽的(unmasked),一个屏蔽的中断只要还是屏蔽的,控制单元就忽略它。要根据中断允许标志的设置来判断 CPU 是否能响应中断请求
    • 非屏蔽中断(nonmakable interrupt
      只有几个危急事件(如硬件故障)才引起非屏蔽中断。非屏蔽中断总是由 CPU 辨认。不受中断允许标志的影响,不能用软件进行屏蔽
      可屏蔽的中断可以被阻塞,使用 x86_64 的指令 sticli。这两个指令修改了在中断寄存器中的 IF 标识位。 sti 指令设置 IF 标识,cli 指令清除这个标识。不可屏蔽的中断总是被报告。通常,任何硬件上的失败都映射为不可屏蔽中断。我们可以在 Linux 内核代码中找到这两个指令的使用:
static inline void native_irq_disable(void)
{
   
   
        asm volatile("cli": : :"memory");
}

static inline void native_irq_enable(void)
{
   
   
        asm volatile("sti": : :"memory");
}
  • 异常
    1. 处理器探测异常(processor-detected exception
      当 CPU 执行指令时探测到的一个反常条件所产生的异常。可以进一步分为三组,这取决于 CPU 控制单元产生异常时保存在内核态堆栈 eip 寄存器中的值。
      • 故障(fault
        通常可以纠正:一旦纠正,程序就可以在不失连贯性的情况下重新开始。保存在 eip 中的值是引起故障的指令地址。因此,当异常处理程序终止时,那条指令会被重新执行。
      • 陷阱(trap
        在陷阱指令执行后立即报告;内核把控制权返回给程序后就可以继续它的执行而不失连贯性。保存在 eip 中的值是一个随后要执行的指令地址。只有当没有必要重新执行已终止的指令时,才触发陷阱。陷阱的主要用途是为了调试程序。在这种情况下,中断信号的作用是通知调试程序一条特殊指令已被执行(例如到了一个程序内的断点)。一旦用户检查到调试程序所提供的数据,它就可能要求被调试程序从下一条指令重新开始执行。
      • 异常中止(abort
        发生一个严重的错误:控制单元出了问题,不能在 eip 寄存器中保存引起异常的指令所在的确切位置。异常中止用于报告严重的错误,如硬件故障或系统表中无效的值或不一致的值。由控制单元发送的这个中断信号是紧急信号,用来把控制权切换到相应的异常中止处理程序,这个异常中止处理程序除了强制受影响的进程终止外,没有别的选择。
    2. 编程异常(programmed exception
      在编程者发出请求时发生。是由 intint3 指令触发的,当 into(检查溢出)和 bound(检查地址出界)指令检查的条件不为真时,也引起编程异常。控制单元把编程异常作为陷阱来处理。编程异常通常也叫做软中断sofware interrupt)这样的异常有两种常用的用途:执行系统调用及给调试程序通报一个特定的事件。

每个中断和异常是由 0~255 之间的一个数来标识。因为一些未知的原因,Intel 把这个 8 位的无符号整数叫做一个向量(vector)。非屏蔽中断的向量和异常的向量是固定的,而可屏蔽中断的向量可以通过对中断控制器的编程来改变。

中断和异常的区别:中断是由硬件引起的;异常则发生在编程失误而导致错误指令,或者在执行期间出现特殊情况必须要靠内核来处理的时候(比如缺页)。

2、中断的上下部

中断的执行需要快速响应, 但并不是所有中断都能迅速完成。 此外, Linux 中的中断不支持嵌套, 意味着在正式处理中断之前会屏蔽其他中断, 直到中断处理完成后再重新允许接收中断,如果中断处理时间过长, 将会引发问题。

这里以炒菜的过程中接电话进行举例:当你正在炒菜的时候,菜正在锅里翻炒着。 突然, 你的手机响起,打破了你正常的炒菜流程,接电话的时间很短并不会对炒菜产生很大的影响, 而接电话的时候可能就有问题了,因为菜可能会因为没来得及翻面而炒糊了。

为了让系统可以更好地处理中断事件, 提高实时性和响应能力, 将中断服务程序划分为上下文两部分:

  • 上半部:上半部是中断处理函数的一部分,它主要处理一些紧急且需要快速响应的任务。 中断上文的特点是执行时间较短,旨在尽快完成对中断的处理。这些任务可能包括保存寄存器状态、更新计数器等, 以便在中断处理完成后能够正确地返回到中断前的执行位置。
    上半部的执行是在中断上下文中进行的,它运行在中断服务例程(ISR)所在的内核线程上下文中,而不是用户进程的上下文中。因此,上半部的执行是在中断被触发时立即执行的,不会被其他中断打断。

  • 下半部是中断处理函数的另一部分,它相对于上半部来说是延迟执行的。下半部的目的是在中断被触发后,尽快将一些不紧急或者耗时的处理工作延后执行,以减轻上半部的负担,从而使中断处理更加高效。
    下半部的执行是在非中断上下文中进行的,它不会被其他中断打断,并且可以访问用户空间的内存。下半部的执行可以在任意时刻进行,但是需要注意的是,下半部执行的时间越长,会导致中断延迟更长,从而影响系统的响应性能。下半部一般包括以下几种形式:

    • 内核线程:创建一个新的内核线程来执行一些独立于中断的任务。
    • 任务队列:将需要执行的任务放入任务队列中,由内核调度器来选择适当的时机执行。
    • 工作队列:类似于任务队列,但是工作队列可以绑定到某个 CPU,以提高处理效率。

3、异常

80x86 微处理器发布了大约 20 种不同的异常(依赖于体系结构)。内核必须为每种常提供一个专门的异常处理程序。对于某些异常,CPU 控制单元在开始执行异常处理程序前会产生一个硬件出错码(hardware error code),并且压入内核态堆栈。

下面的列表给出了在 80x86 处理器中可以找到的异常的向量、名字、类型及其简单描述。更多的信息可以在 Intel 的技术文挡中找到。

  • 0:“Divide error”(故障)
    当一个程序试图执行整数被 0 除操作时产生。
  • 1:“Debug”(陷阱或故障)
    产生于:
    • 设置 eflags 的 TF 标志时(对于实现调试程序的单步执行是相当有用的);
    • 一条指令或操作数的地址落在一个活动 debug 寄存器的范围之内。
  • 2:未用
    为非屏蔽中断保留(利用 NMI 引脚的那些中断)。
  • 3:“Breakpoint”(陷阱)
    int3(断点)指令(通常由 debugger 插入)引起。
  • 4:“Overflow”(陷阱)
    当 eflags 的 OF(overflow)标志被设置时,into(检查溢出)指令被执行。
  • 5
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值