概念
中断
CPU在执行指令时,收到某个中断信号转而去执行预先设定好的代码,然后再返回到原指令流中继续执行,这就是中断机制。
在Linux系统中,中断是一种机制,用于处理外部设备或软件请求的事件。当一个设备需要与CPU进行通信时,它会发送一个中断信号给CPU,以引起注意并触发相应的中断处理程序。中断可以分为硬件中断和软件中断。硬件中断是由外部设备(如键盘、鼠标、网卡等)发出的信号,用于通知CPU有相关事件需要处理。当收到硬件中断信号后,CPU会暂停当前正在执行的任务,并跳转到对应的中断处理程序来响应和处理这个事件。软件中断是由操作系统或应用程序主动触发的,例如系统调用、异常或软件产生的信号等。通过软件方式触发的中断也会导致CPU暂停当前任务并跳转到相应的中断处理程序来执行特定操作。通过使用中断机制,Linux能够实现高效地响应外部事件,并及时进行必要的处理。
中断的作用
- 外设异步通知CPU : 外设发生了什么事情或者完成了什么任务或者有什么消息要告诉CPU,都可以异步给CPU发通知。例如,网卡收到了网络包,磁盘完成了IO任务,定时器的间隔时间到了,都可以给CPU发中断信号。
- CPU之间发送消息 : 在SMP系统中,一个CPU想要给另一个CPU发送消息,可以给其发送IPI(处理器间中断)。
- 处理CPU异常 : CPU在执行指令的过程中遇到了异常会给自己发送中断信号来处理异常。例如,做整数除法运算的时候发现被除数是0,访问虚拟内存的时候发现虚拟内存没有映射到物理内存上。
- 实现系统调用 : 早期的系统调用就是靠中断指令来实现的,后期虽然开发了专用的系统调用指令,但是其基本原理还是相似的。
中断的产生来源
- 外设,外设产生的中断信号是异步的,一般也叫做硬件中断(注意硬中断是另外一个概念)。硬件中断按照是否可以屏蔽分为可屏蔽中断和不可屏蔽中断。例如,网卡、磁盘、定时器都可以产生硬件中断。
- CPU,这里指的是一个CPU向另一个CPU发送中断,这种中断叫做IPI(处理器间中断)。IPI也可以看出是一种特殊的硬件中断,因为它和硬件中断的模式差不多,都是异步的
- CPU异常,CPU在执行指令的过程中发现异常会向自己发送中断信号,这种中断是同步的,一般也叫做软件中断(注意软中断是另外一个概念)。CPU异常按照是否需要修复以及是否能修复分为3类:1.陷阱(trap),不需要修复,中断处理完成后继续执行下一条指令,2.故障(fault),需要修复也有可能修复,中断处理完成后重新执行之前的指令,3.中止(abort),需要修复但是无法修复,中断处理完成后,进程或者内核将会崩溃。例如,缺页异常是一种故障,所以也叫缺页故障,缺页异常处理完成后会重新执行刚才的指令。
- 中断指令,直接用CPU指令来产生中断信号,这种中断和CPU异常一样是同步的,也可以叫做软件中断。例如,中断指令 int 0x80 可以用来实现系统调用。
中断信号的4个来源正好对应着中断的4个作用。前两种中断都可以叫做硬件中断,都是异步的;后两种中断都可以叫做软件中断,都是同步的。
执行上下文(execute context)
有了中断之后,CPU就分为两个执行上下文了,进程执行上下文(process context)和中断执行上下文(interrupt context)。那么哪些是进程执行上下文哪些是中断执行上下文呢?进程的执行是进程执行上下文,同步中断的处理也是进程执行上下文(因为同步中断处理是和当前指令相关的,可以看做是进程执行的一部分),异步中断的处理是中断执行上下文。 进程执行上下文和中断执行上下文有两个区别:
- 进程执行上下文是可以调度、可以休眠的,而中断执行上下文是不可以调度不可以休眠的
- 二是在进程执行上下文中是可以接受中断信号的,而在中断执行上下文中是屏蔽中断信号的。
如果中断执行上下文的执行时间太长的话,就会影响我们对新的中断信号的响应性,所以我们需要尽量缩短中断执行上下文的时间。为此我们对异步中断的处理有下面两类办法:
- 立即完全处理 : 对于简单好处理的异步中断可以立即进行完全处理。
- 立即预处理(上半部) + 稍后完全处理(下半部) : 对于处理起来比较耗时的中断可以采取立即预处理加稍后完全处理的方式来处理。立即预处理阶段(上半部)主要负责接收中断,传递给处理程序;稍后完全处理阶段(下半部)主要是执行处理程序。
立即完全处理和立即预处理是屏蔽中断的,稍后完全处理阶段可以不屏蔽中断。中断预处理只有一种方法,叫做
hardirq(硬中断)。中断后处理有很多种方法,分为两类,直接中断后处理有softirq(软中断)、tasklet(微任务),线程化中断后处理有workqueue(工作队列)、threaded_irq(中断线程)。
中断处理流程
中断控制器初始化
在内核启动流程中,其中一个步骤是进行中断栈和中断控制器的初始化,中断控制器是CPU与中断源之间的一个硬件,负责传递中断信号。linux操作系统支持多个/多种中断控制器。多个中断控制器可以级联在一起;发生中断时,中断信号由底层向上传递,中断处理时,由顶层向下找到具体对应的中断处理函数。 一个中断控制器对应一个irq_domain,irq_domain数据结构负责完成硬件中断号到虚拟中断号的映射。
外设驱动中断初始化
- 获取中断号:基于设备树上设备的中断信息,找到匹配的irq_domain, 创建中断描述符,确定触发方式(水平、边沿等),注册中断向上执行的函数指针,完成硬件中断号到虚拟中断号的映射(中断向量表)
- 获取到了irq中断号后,采用request_irq/request_threaded_irq向中断向量表来注册普通处理的中断/线程化处理的中断处理函数;
保护现场并关中断
将 CPU 寄存器按照 pt_regs 结构体的定义将第一现场保存到栈上
- 保存 PSTATE 到 SPSR_ELx 寄存器
- 将 PSTATE 中的 DAIF 全部屏蔽
- 保存 PC 寄存器的值到 ELR_ELx 寄存器
执行硬中断
- 进入中断栈
- 进入中断上下文
- 根据 hwirq 去查找 linux 中断号
- 通过中断号找到全局中断描述符数组 irq_desc[NR_IRQS] 中的一项,执行该 irq 号注册的 action
- 退出中断上下文
- 退出中断栈
恢复现场并关中断
- disable 中断
- 检查在退出中断前有没有需要处理事情,如调度、信号处理等
- 将之前压栈的 pt_regs 弹出,恢复现场
执行软中断
软中断是在硬中断执行后异步执行的,也可以直接触发。均是调用raise_softirq函数。 软中断四种机制:
- softirq:
softirq 是 Linux 内核用于处理高优先级任务的一种机制。它是在中断处理程序执行完毕后,由内核调度执行的延迟处理机制。softirq 通常用于处理一些与网络、定时器、调度器等相关的高优先级任务。 - tasklet:
tasklet 是一种比 softirq 更为轻量级的延迟处理机制。它也是在中断处理程序执行完毕后,由内核调度执行的。tasklet 通常用于处理一些与设备驱动程序相关的任务,如处理接收到的数据等。 - threaded_irq:
threaded_irq 是一种将中断处理程序转换为内核线程的机制。它将中断处理程序的执行放到一个独立的内核线程中,以便在中断处理程序中执行复杂的操作,而不会阻塞其他中断。threaded_irq 通常用于处理一些需要较长时间执行的中断处理任务,如磁盘 I/O。 - worker_queue:
worker_queue 是一种用于异步处理中断的机制。它通过将中断处理程序的执行放入一个工作队列中,由内核调度执行,以实现异步处理中断。Worker_queue 通常用于处理一些需要长时间执行的任务,如文件系统同步等
时钟中断
在Linux的0号中断是一个定时器中断。在固定的时间间隔都发生一次中断,也是说每秒发生该中断的频率都是固定的。该频率是常量HZ,该值一般是在100 ~ 1000之间。该中断的作用是为了定时更新系统日期和时间,使系统时间不断地得到跳转。另外该中断的中断处理函数除了更新系统时间外,还需要更新本地CPU统计数。指的是调用scheduler_tick递减进程的时间片,若进程的时间片递减到0,进程则被调度出去而放弃CPU使用权。 inux的OS时钟的物理产生原因是可编程定时/计数器产生的输出脉冲,这个脉冲送入CPU,就可以引发一个中断请求信号,我们就把它叫做时钟中断。
“时钟中断”是特别重要的一个中断,因为整个操作系统的活动都受到它的激励。系统利用时钟中断维持系统时间、促使环境的切换,以保证所有进程共享CPU;利用时钟中断进行记账、监督系统工作以及确定未来的调度优先级等工作。可以说,“时钟中断”是整个操作系统的脉搏。
在 Linux 系统中,时钟中断是指由系统时钟硬件或软件定时器触发的中断。这种中断以固定的时间间隔触发,通常用于进行系统调度、时间管理以及其他与时间相关的操作。时钟中断的主要作用包括以下几个方面:
- 系统调度:时钟中断用于触发操作系统的调度程序,使其能够周期性地进行任务切换,确保各个进程都能得到执行机会。
- 时间管理:时钟中断用于更新系统的时钟,保持系统时间的准确性。此外,它还用于处理定时器和延时操作,以及执行与时间相关的功能。
- 定时器:时钟中断可以用于触发定时器操作,比如执行周期性的任务或者超时处理。
在 Linux 内核中,时钟中断由系统的定时器硬件(如 APIC 定时器)或者软件定时器(如 jiffies)管理。当时钟中断发生时,操作系统会执行相应的中断处理程序,通常会更新系统时钟、进行进程调度、执行定时器操作等。时钟中断的频率通常是固定的,通常以每秒钟产生多个中断来维护系统的时间和进行调度。
时钟中断是 Linux 系统中非常重要的一部分,它确保了系统的时间管理和任务调度的正常运行,同时也为系统提供了精确的时间基准。