仓库源文

.. Kenneth Lee 版权所有 2024

:Authors: Kenneth Lee :Version: 0.1 :Date: 2024-05-08 :Status: Draft

基于状态机的行为定义


最近在做一些指令集定义的工作。我发现很多工程师在做这类架构定义的时候,很不习惯 用状态机的思路去定义接口。我通过本文来总结一下这个思路具体是什么样的。

比如我们要做一个跳转功能,这需要分两步:

  1. 设置跳转目标
  2. 启动跳转

很多工程师描述这个功能的时候总是简单说:“要进行一个跳转,需要先把跳转目标设置 在寄存器X中,然后调用跳转指令,程序就会转到所述的目标上执行了。”

这个定义我看着就不舒服,设计上觉得它没有设计完,定义上没有定义全。为什么呢?因 为这些东西它没有说:

这就是没有定义完,你不能说“你不要这样做啊”,但软件又不是都是我写的,比如我是一 个写操作系统的,用户程序就做成这个样子了,或者攻击程序就做成这样了,我是不是得 知道你硬件的确切反应,我才能正确设计我的保护逻辑?

而一旦你有了这些所有可能性,那么你的设计的“可能性空间”就扩大了。比如你设置跳转 目标,其实是更新了本系统的某个状态,那么你就要定义所有其他行为(比如指令的执 行),对这个状态的影响。那么你所有和这个状态有关的逻辑就需要重新推理一次,这就 是所谓的“设计上没有设计完”。

你可以对着下面这个状态变迁图联想一下:

.. figure:: _static/状态机.png

如果原来的系统状态只有4个,我们推理它的时候,在每个状态上都要推理任意激发事件 (简称激励)下,系统的反应。遇到不同情况会做不同反应的情况,就需要补充状态了。 现在你增加两个独立的激励,并对这些积累的反应有期待,我是不是应该重新考虑在每个 状态上的反应是否都符合期望?

这就是基于状态机思路的思维模式。在这种模式下,我们不考虑你“先如何如何,再如何如 何,又如何如何了……就能如何如何了”。我们对每个独立的“驱动”或者“事件”,考虑系统在 不同状态下如何反应,系统自身的状态如何更新。这个东西不一定能用状态变迁图来表达 的。状态变迁图只能表达有限状态的变迁。如果状态是一组正交变量的组合,靠人脑逻辑 思路去穷举这个可能性几乎是唯一的选择。

比如,“add指令会导致输出寄存器等于输入寄存器的和,并导致PC移动到下一个指令上”。 这句话就是无法通过状态变迁图表达的。但这仍是一个状态变迁的“穷举”,因为没有其他 可能性了。我们用语言“概括”了所有的可能性。但如果add指令是一条CISC指令,它可以 在中间打断,而被打断的时候,部分结果可以被提交,那么上述表述就无法穷举它的所有 可能状态了,我们必须加入中间状态才能正确描述它。

大部分时候,状态的成本都来自“中间断开”,但对很多功能来说,允许“中间断开”又是不 可避免的,因为我们本来要的就是这个自由组合的自由度。所以,只能说,我们必须让断 开的地方状态是比较少的,在描述上能少一点分类树,不会经常要说:“如果遇到事件A, 有两种可能性……其中第二种可能性呢,有分三种情况要处理……”。这种定义,一旦你要加 一个不是正交的状态进来,新的状态空间的基数(Cardinality)就会等于那个新状态的 可能性空间的基数乘以你的事件A响应行为的分类空间的基数了。

我们做架构设计的时候,比如设计一个新的指令集,一般在设计初期要尽量避免这种复杂 的基础定义,比如我们尽量一条指令就是固定输入特定寄存器,固定输出特定寄存器,而 不会需要在这种情况下是这几类寄存器,那种情况下又是另几类寄存器,互相还有交叉, 还有不同的边际效应,一旦这个状态机是这样的,虽然你现在能跑,但这个指令集就已经 没有未来了,因为一旦状态数量指数式增长,我们就无法穷举和预测它的所有可能性了。