学习完前面的并发与同步后,我们再一起来简单学习了解下内核中的中断!
一、什么是中断?为什么要有中断?
在Linux内核中,中断是一种硬件机制,用于在处理器与设备之间传递异步事件的信号。
当设备完成某个操作、出现错误或需要处理某个事件时,会发送一个中断信号给处理器,处理器会立即中断当前正在执行的程序,转而执行与中断相关的处理程序。
中断的存在主要是为了提高系统的响应性和效率。
没有中断的话,处理器需要不断地轮询设备的状态,这样会消耗大量的处理器时间,并且会导致系统资源的浪费。而中断机制可以让处理器在有需要处理的事件时立即响应,从而减少了轮询的开销,提高了系统的效率和响应速度。
当然凡事都不是绝对的,轮询机制也不完全比中断机制差。
在网络吞吐量大的情况下,网卡驱动采用轮询机制比中断机制效率要高。
二、在ARM架构下,中断是如何管理的?
在ARM架构下,中断管理主要涉及以下几个方面:
一)中断控制器(Interrupt Controller):
ARM处理器通常集成了中断控制器,用于接收并管理来自外部设备的中断信号。
在ARM体系结构中,常见的中断控制器包括GIC(Generic Interrupt Controller)和VIC(Vectored Interrupt Controller),它们负责中断的优先级处理、中断调度等。
Linux内核支持众多的处理器架构,从系统角度看,Linux内核的中断管理可以分成如下四层:
硬件层:如CPU和中断控制器的连接。
处理器架构管理层:如CPU中断异常处理。
中断控制器管理层:如IRQ号的映射。
Linux内核通用中断处理器层:如中断注册和中断处理。
不同的架构对中断控制器有不同的设计理念,这里只针对讲ARM Vexpress V2P-CAIS-CA7版本,它支持GIC Version2。
二)中断向量表(Interrupt Vector Table):
ARM处理器在中断发生时会根据中断类型查找对应的中断处理程序的入口地址,这些入口地址被保存在中断向量表中。中断向量表中存储了一系列中断向量(interrupt vector),每个中断向量指向对应中断处理程序的入口地址。
这里需要注意的是,中断处理程序的入口地址指的是处理特定中断事件时,系统将跳转执行的代码段的地址,即异常向量表。
当系统发生中断事件时,硬件会将控制权交给操作系统中断处理程序的入口地址,然后操作系统会根据该中断的类型去执行相应的中断处理程序。
这个入口地址一般会存储在异常向量表中,当中断事件发生时,CPU会根据中断向量号找到相应的异常描述符,从而确定中断处理程序的入口地址。
异常向量表:异常向量表是从0x0地址开始,一共32个字节,包含8个表项,其中有1个保留不用,其他7个表项对应7种异常发生后的跳转位置,这7种异常发生后分别对应到5种异常模式。每个表项里面放的一般都是一条跳转指令,用来跳转到真正的异常处理程序入口,通过B指令,或者LDR PC,[PC, #?] 的方式都可以实现此类跳转。
三)中断处理程序(Interrupt Service Routine,ISR):
中断处理程序是在中断响应时执行的代码块,用于处理具体的中断事件。
在ARM架构下,通常使用汇编语言编写中断处理程序,首先保存寄存器状态,然后执行相关的中断处理逻辑,最后恢复寄存器状态并返回。
下面有一个例子可以说明:
当一个中断发生时,CPU内核感知到异常发生,硬件会自动做如下一些事情。
1.处理器的状态保存在对应的异常等级的SPSR_ELx中。
2.返回地址保存在对应的异常等级的ELR_ELx中。
3.寄存器里的DAIF域都设置为1,相当于把调试异常、系统错误(SError)、IRQ以及FIQ都关闭了。PSTATE寄存器是ARMv8里新增的寄存器。
4.如果是同步异常,那么究竟什么原因导致的呢?具体要看ESR_ELx。
5.设置栈指针,指向对应异常等级里的栈。
6.迁移处理器等级到对应的异常等级,然后跳转到异常向量表里执行。
7.若为IRQ中断,CPU会首先根据异常向量表跳转到对应表项中。
8.再跳转到ell_irq标签中。
9.通过ell_irq标签中的kernel_entry来保存中断上下文。
10.再通过irq_handler处理这个IRQ中断。
11.处理完成后通过kernel_exit宏恢复中断上下文。
注意:在整个IRQ处理过程中ARM64处理器会自动处理。
当有中断发生时,ARM64处理器会自动把处理器状态PSTATE保存到SPSR_ELx里。另外ARM64处理器会自动设置PSTATE寄存器里的DAIF域为1,相当于把调试异常、系统错误、IRQ以及FIQ都关闭了。
当中断处理完成后使用ERET指令来恢复中断现场,把之前保存的SPSR_ELx的值恢复到PSTATE寄存器里,相当于打开了IRQ。
三、中断上下部机制是什么?常用的下半部机制有哪些?
中断管理中有一个很重要的设计理念——上下部机制。前面所说的硬件中断处理基本上属于上半部的范畴,中断线程化属于下半部的范畴。在中断线程化机制合并到Linux内核之前,早已有一些其他的下半部机制如:
-
软中断:软中断是Linux内核很早就引入的机制,是预留给系统中对时间要求较严格和重要的下半部使用的,而且目前驱动中只有块设备和网络子系统使用了软中断。
-
Tasklets:Tasklets是一种轻量级的延迟处理机制,它在下半部处理中运行,并且会在运行过程中被调度来处理中断事件。Tasklets是Linux内核中的一种静态中断处理机制,可以提高中断处理的效率。
-
Workqueues:Workqueues是一种更加通用和灵活的下半部机制,它是通过工作者结构体来进行处理。工作者是以工作队列的形式存在的,可以异步执行处理函数,并且可以延迟执行。
四、总结
总之,内核中的中断管理部分还是比较复杂的,不仅需要了解中断机制是如何产生的,而且还要知道工作中常用的上下部机制,以及常用的下半部机制如何去处理中断。
通过阅读本书,详细讲解了中断机制背后的实现原理,但光看还不够,还是需要结合内核中的代码对比学习,才能更深刻地对中断管理有更好的了解,如果不是提前学过这部分,可能还真的看不懂在讲什么,还是推荐有一定工作经验的同学学习此书。如果在工作中也有针对内核中断相关开发的同学,也可以看一看,了解内核中断管理的本质。