0%

CPU核中断设计基本逻辑

CPU根据一条一条指令执行程序,如果没有外界刺激CPU就这样周而复始的运行下去。可以看到
这样的程序CPU总是按照预定的程序同步的执行下去。

CPU在设计的时候会预留一些外部信号的输入接口,并从硬件上定义当这些接口有信号时CPU
的执行行为。一般的,当这些接口上有信号时,CPU把执行流跳到预定义的指令地址。

CPU执行被外部信号中断,进而跳到预定义地址执行指令,这个行为看似受到硬件或者外部
信号的配置,其实实际上所有控制的主体其实还是软件。软件决定当一个外部信号发生时CPU
的所有行为。

当我们仔细观察“软件”这个概念,就会发现CPU上可能分时运行着多个软件,比如CPU上可能
一段时间在运行BISO的代码,一段时间在运行Host内核的代码,一段时间在运行Guest内核的
代码,一段时间在运行用户态的代码。这些代码,有些在逻辑上都是相互隔离的软件。

我们从原始需求上去看CPU需要被中断这个诉求,实际上这些需求并不来自硬件,而是真实的
来自CPU上运行的各种软件。比如,BIOS在处理一些任务的时候需要一个精确的计时器,CPU
用指令轮询的办法没法做精确的计时,这样BIOS就需要设置一个计时器,当计时器时间到了时,
发中断中断当前CPU,需要注意的是,这个中断是发给BIOS这个软件的,CPU架构设计上要通过
各种软硬件设计保证如上的中断可以发给BIOS。

我们再看一个例子,比如CPU上运行很多虚拟机,每个虚拟机上都要收自己虚拟机上的外设的
中断。每个虚拟机运行的时候,都可以配置当外设中断到来时CPU的处理行为。但是,不论是
BIOS的例子还是虚拟机的例子,其中的一个关键问题是中断怎么投递到相应的软件上下文上。

我们再把这个问题打开看下,CPU在一个时间只能运行一个软件,而特定的中断要投递到特定
的软件上,这就要CPU核在收到中断时硬件内部有一套调度算法,把特定的中断调度到特定的
软件上下文:

1
2
3
4
5
6
7
8
         +----------------------+
| CPU |
|+--------------+ |
-------->|| irq schedule | |
|+--------------+ |
| |
| |
+----------------------+

我们可以看下硬件的这个调度算法大概是怎么样的,把外界想要中断的CPU特权级叫做target_irq_level,
把CPU当前运行的特权级叫做current_cpu_run_level。一般的,不同的软件运行在不同的CPU
特权级,运行在相同特权级的相同软件的不同实例(比如,不同虚拟机)需要用新硬件变量标记。
下面我们用中断发给特权级或者发给特权级上的软件实例来描述问题。

当target_irq_level就是current_cpu_run_level时,这个表示外界想要中断的软件山下文
目前正在运行,硬件直接中断当前的上下文就好。

当target_irq_level不是current_cpu_run_level时,表示外界想要中断的上下文不在线,
如果target_irq_level比current_cpu_run_level高,CPU需要直接陷入到高特权级处理对应
的中断,如果target_irq_level比current_cpu_run_level低,那么中断可以被记录在CPU里,
等到CPU切换到中断目标特权级时再处理中断。

在如上的逻辑上,我们还要叠加上多实例的逻辑,这样才能和实际的软件使用场景相吻合。
以target_irq_level就是current_cpu_run_level为例,就是在中断目标特权级的基础上增加
了软件实例这个新变量,也就是说中断的发送目标是某个特权级上运行的一个软件实例,虽然
CPU可能正在对应的特权级上运行,但是,不一定运行的是目标软件实例,这样,硬件就需要
完成两个动作,一是有办法识别当前运行的软件实例是否是中断的目标实例,如果正好就是,
直接中断当前软件就好,二是如果需要中断的实例和当前实例不一样,这个中断怎么处理,
思路和特权级的处理基本一致,就是记录起来,但是软件实例的上下文完全可能是记录在
软件上的,而且软件实例一般是由比当前特权级高的特权级管理的,那么把这个中断报给
高特权级是一种合理的选择。