linux的中断管理机制
这篇文章将从中断相关概念开始,并介绍Linux中的中断机制,最后以实现一个简单的按键中断驱动程序结束。
异常与中断
中断
中断是一种事件,它改变程序的正常执行流,可以由硬件设备甚至CPU本身生成。当中断发生时,当前的执行流将被暂停,中断处理程序运行。中断处理程序运行完毕后,先前的执行流将恢复。例如,每当你按下键盘上的一个键时,CPU会被中断,从而使计算机可以从键盘读取用户输入。这发生得非常快,所以你不会注意到任何变化或损害用户体验。
这里有一个类比加深对中断的理解:
假设你正在家里专心地写作(相当于CPU在执行当前的程序)。这时,电话铃声响起(相当于硬件中断信号),你需要暂停写作去接电话。
-
你在写作(CPU正在执行当前的指令流)。
-
电话铃声响起(硬件设备触发中断信号)。
-
你暂时停下写作(CPU停止当前指令的执行)。
-
你记住你在写作的内容和位置(CPU保存当前执行状态和上下文)。
-
你去接电话(CPU调用中断服务程序ISR处理中断)。
-
你处理完电话上的事情(ISR完成中断处理)。
-
你挂断电话(中断处理结束)。
-
你回到书桌继续写作(CPU恢复之前保存的执行状态和上下文,继续执行被中断的程序)。
在了解异常之前,我想先讲下广义和狭义的中断概念,因为这个概念混淆了我很久,只有区分清楚,才能知道如何正确看待中断与异常的关系。
-
广义的中断:在广义上,中断指的是任何打断正常程序执行流程的事件。这包括由外部设备触发的硬件中断,也包括由CPU内部检测到的错误或特殊情况触发的异常。因此,从这个角度来看,异常可以被视为中断的一个子类。
-
狭义的中断:在狭义上,中断专指由外部硬件设备触发的事件,与异常区分开来。在这种定义下,中断和异常是并列的两类事件,分别处理不同的情况。
接下来将以广义的角度讲解中断,但是我们要知道一点,真实的异常与中断的处理程序是不一样的,中断使用中断处理程序(Interrupt Service Routine, ISR),ISR通常由设备驱动程序定义,每种中断类型(例如键盘中断、网络中断)有专门的ISR。异常使用异常处理程序(Exception Handler),通常由操作系统内核定义,每种异常类型(例如除零异常、页面错误)有专门的处理程序。
异常
异常通常与中断一起讨论。与中断不同,异常是相对于处理器时钟同步发生的;它们通常被称为同步中断。异常是处理器在执行指令时产生的,可能是由于编程错误(如除零)或必须由内核处理的异常情况(如页面错误)。由于许多处理器架构以类似于中断的方式处理异常,因此内核处理两者的基础设施相似。
中断类型
中断类型有三种,分别是:
-
硬件中断(Hardware Interrupts)
-
软件中断(Software Interrupts)
-
异常(Exceptions)
硬件中断
硬件中断由外部硬件设备触发,用于通知CPU处理外部事件或设备请求。
-
外部设备中断:由外围设备(如键盘、鼠标、硬盘、网络接口卡等)触发。例如,当键盘按键被按下时,会产生键盘中断。
-
定时器中断:由系统定时器触发,用于实现定时任务或系统时钟的更新。
-
电源管理中断:由电源管理设备触发,用于处理电源状态变化,如电池电量低、电源插入或拔出等。
软件中断
软件中断由程序指令触发,用于实现系统调用或用户态与内核态的切换。
-
系统调用中断:程序通过特定指令(如x86架构中的INT指令)触发,进入内核态执行系统调用。
-
用户定义中断:由用户程序显式触发,用于实现特定的功能或异常处理。
异常
异常是由CPU在执行指令过程中检测到的错误或特殊情况触发的,用于处理程序错误或特殊事件。
异常分为三种:陷阱、故障和终止。
陷阱(Traps):
-
来源:由指令执行过程中预期的事件触发,如系统调用。
-
处理结果:通常处理后继续执行下一条指令。
-
示例:系统调用陷阱。
故障(Faults):
-
来源:由指令执行过程中检测到的可恢复错误触发。
-
处理结果:处理后通常可以恢复并重新执行导致故障的指令。
-
示例:页面错误(Page Fault)、除零错误(Divide-by-zero Fault)。
终止(Aborts):
-
来源:由指令执行过程中检测到的不可恢复错误触发。
-
处理结果:通常无法恢复,进程终止。
-
示例:硬件故障、内存校验错误(Machine Check Abort)。
中断控制器
中断控制器(Interrupt Controller)是一个硬件模块,它负责接收多个外部和内部中断源的中断请求,并按照优先级和配置将这些中断请求发送给处理器。对于复杂系统,尤其是多处理器系统,中断控制器是不可或缺的组件,因为它能够高效地处理和调度大量的中断请求。
CPU每执行完一条指令后,就会检查中断控制器(如Arm架构的GIC)是否有中断请求。此时,CPU可以决定是否中断当前程序的执行(如果设置了屏蔽中断),跳转到中断处理程序。
这里还是用一个类比来加深对中断控制器的理解:想象一个繁忙的办公室,前台接待员负责接收并处理来自不同访客和电话的请求。办公室里的员工(处理器)正在处理各种各样的工作(指令执行)。
-
接收请求:接待员(中断控制器)接收并记录所有访客和电话的请求(中断信号)。
-
判断优先级:接待员根据请求的紧急程度决定处理顺序(中断控制器判断中断优先级)。
-
分配请求:接待员将高优先级请求分配给相应的员工(中断控制器将中断请求发送到目标处理器)。
-
处理请求:员工暂停当前工作,处理访客或电话(处理器暂停当前指令,执行中断服务程序)。
-
完成请求:员工处理完访客或电话后,返回继续工作(处理器处理完中断后,恢复执行被中断的指令)。
Linux 系统对中断的处理
进程上下文和中断上下文
内核使用进程上下文和中断上下文的组合方式来完成各种任务。
什么是上下文
上下文是指在某个特定时刻,CPU的状态和执行环境所需的信息集合。它包括了执行程序所需的所有寄存器、程序计数器、堆栈指针,以及与执行状态相关的数据。
因此我们可以得知,进程上下文和中断上下文拥有不同的执行环境和状态信息。
进程上下文
进程上下文是指内核代码在代表用户进程执行时的状态。服务于用户应用程序发出的系统调用的内核代码是在进程上下文中运行的。
它拥有如下特性:
-
代表用户进程执行:在进程上下文中,内核代码代表某个具体的用户进程执行,因此拥有该进程的上下文(如进程控制块、内存地址空间)。
-
可以睡眠和阻塞:在进程上下文中,内核代码可以调用可能导致睡眠或阻塞的函数,因为进程可以被调度器调度并在适当的时候重新唤醒。
-
特权级别:内核代码在进程上下文中执行时具有内核态特权,可以访问内核数据结构和资源。
继续通过一个类比来加深理解:员工(代表一个进程)在办公室里工作,处理日常任务和项目。
1.日常工作:员工按照日常安排和优先级处理手头的任务(系统调用、用户进程的请求)。
2.可以打断和等待:
-
员工可以被打断去开会(阻塞),会后继续工作。
-
如果需要某个同事的帮助,员工可以等待同事的回复(睡眠),在等待期间做其他事情或休息(调度其他进程)。
3.上下文信息:员工的办公桌上有他们的工作文件和工具(进程控制块、内存地址空间等),确保他们可以有效地完成工作。
4.权限和资源:员工可以访问公司资源(内核数据结构和资源),但需要遵循公司的规章制度(内核态特权)。
中断上下文
中断上下文是指内核代码在处理中断请求时的状态。中断处理程序(ISR)在中断上下文中异步运行。
它拥有如下特性:
-
无特定进程关联:中断上下文不代表任何具体的用户进程,因此没有与特定进程相关的上下文。
-
不能睡眠或阻塞:在中断上下文中,内核代码不能调用可能导致睡眠或阻塞的函数,因为中断处理程序需要尽快完成以恢复正常执行。
-
高优先级:中断处理程序通常具有高优先级,以便迅速响应硬件事件。
中断上下文一旦运行就不能终止,它必须尽可能快的完成,这个过程中不能被其他中断信号中断。中断上下文中的代码不能执行以下操作:
-
进入睡眠状态或阻塞
-
获取互斥锁
-
执行耗时任务
-
访问用户空间虚拟内存
接下来通过一个类比加深理解:办公室里有一个专门的紧急任务响应人员(中断服务程序ISR),它负责处