仓库源文

.. Kenneth Lee 版权所有 2019-2020

:Authors: Kenneth Lee :Version: 1.0

接口定义的工作模型


接口定义是架构的最终结果。实际上,架构的外在表现常常表现为接口。我们用SDSA方法 来看这个问题,可以这样看:作为计算系统,你的核心需求必然是数据加工。无论这个功 能看起来多不像一个计算,计算机系统能干的也就是数据加工。我们拿一个计算辅助功能 来举例,比如说,MMU。用DFD来讨论MMU功能,它是这样的:

    .. figure:: _static/mmu_dfd.jpg

MMU处理的DFD图,上图是Context Diagram,下图是DFD。如果你觉得这个图缺了缺页流程 ,请再去看一下DFD图是什么,否则应该看不懂下文。

TLB填充,TLB查询是你必做的动作,因为它就是这个功能本身的定义。无论你把这个功能 放在软件里面做,还是硬件里面做,你总是得做的。拿这个TLB填充为例,你要软件做,接 口可以定义软件必须在发出VA前填充TLB,否则硬件停机或者异常。又或者让硬件查表的时 候给软件发中断,要求它填。你要硬件做,可以让软件一开始就把整个页表放在内存中, TLB查询不到的时候,由硬件的某个部件来做这个填充。硬件还可以进行局部预估,每次加 载一个TLB项的时候,根据机器学习得到某种Pattern,自动加载另外一些项进来,提前做 好准备。你还可以软硬结合,把填充历史反馈给软件,让软件给出一组Hint,告诉你下次 预装载哪些项……

所以其实接口这个东西,本身会产生数据流。我们通常不在DFD中放接口本身产生的数据流 ,因为DFD并非一个设计逻辑分析模型,而是一个需求分析模型。我们是通过DFD描述核心 的“数据加工”需求是什么,然后决定如何分配里面的每个“加工”步骤,决定是哪个模块来 负责。对于架构师来说,他其实在某种程度上不怎么在乎这个加工放在哪一边,因为放哪 一边都是工作量。

    | 插一句:其实,这还是个自由度的问题。如果你只有软件团队,
    | 而且平台都是买的,你能控制的就一两个模块,你的加工都得
    | 放在你可控的模块里,你可想的东西就会少很多,第三方的能
    | 做的,你只能让它做,不能做的,你只能自己做。所以其实限
    | 制越多,设计难度越低。就好像有人说的:最容易解决的问题
    | 就是钱的问题——要不有钱,要不没钱,不用多想。

但那个模块的负责人,就不是这样看了。

所以,复杂系统的架构师,经常在这种问题上碰到困难。模块之间,都希望拿好做,容易 出成绩的加工放自己一边;把坑多,不好出成绩的加工放到别人的模块里。不过这个算是 小问题了,大部分时候就是个利益的问题,分配好利益就好了。

其实更难的问题是:这个接口怎么定?因为每个模块都在问:你对我是什么要求?你严格 定义好了,我一定给你实现出来。

但两个模块,到底应该A模块定义接口呢?还是B模块定义接口呢?我遇到的情况,大部分 时候是都不想定义这个接口,一方面这个工作量巨大,出了任何错(接口部分信息传递不 过去),都是妥妥的锅;另一方面,这通常是个吃力不讨好的工作,因为“加工”本身不是 接口实现的,到时吹牛说某某特性是你做的,接口是最难吹的。

但接口定义决定了整个系统的性能,它才是整个架构工作的核心。一种方法当然是架构团 队做,但架构团队根据什么做呢?这是个递归问题:你没有深入设计两边的模块,你就不 知道这个接口怎么才是高效的。但你不定义这个接口呢,两边的模块无法深入设计。

更大的问题是,架构团队又不是神仙,我工作二十多年了,和各种团队和大拿共事过,交 流过,从来没有见过真正的通才。不说别的,同时懂软件和芯片细节的专家我就没有见过 。你凭什么设计这个接口?

你说你用中断通知软件破坏流水线,转头芯片设计团队就来告诉你这不会,因为人家分支 预测能预判这种情况。你说你芯片可以根据虚拟机ID转发中断,转头Hypervisor团队告诉 你,这用不上,因为按人家的流程,都是先在安全区收集所有的中断,判断过权限以后才 重新分发出去的。你找谁说理去?

关键是,就算你是多领域的通才,这还是个细节问题,如果你把细节问题都解决了,你还 要其他设计团队吗?你自己就都把设计都做了。我们定义接口,把某个加工放到接口的这 一边还是那一边,判断条件是主要是:

  1. 这个加工需要的数据主要在哪一边

  2. 接口上是否有足够的带宽和实时性提供加工需要的数据

  3. 流程需要的驱动力是否可以插入某侧的驱动框架中(比如某一侧本来就有一个线程定期 监控数据,那么就不需要另一侧主动创建中断来通知另一边)

  4. 某个加工是否可以和某侧已有的加工简单结合

这些都需要知道两边的细节设计才能做出合理判断。我举个最简单的例子,就事论事讨论 这里的接口,你能想到在TLB表中加上ASID?你不做微内核这类频繁IPC的系统,你根本不 可能想到要加ASID吧?

所以,接口定义这件事情,我们必须建立一个基本的认识:我们不可能一次到位的。它就 是一个先做一部分关键功能,尽量不建立太多约束(留下余地),然后开始设计两边模块 的内部,然后再升级调整,然后再进一步设计,然后在细化两边的模块设计。每次升级, 都有可能对原来的定义产生一定的破坏(这考验定义时留余地的功夫)。这样小心翼翼做 下去,我们最终可能可以拿到一个挺好的接口。而指望把责任推给任何一个模块或者团队 ,然后觉得自己没有错,花大量的时间希望接口一次成型,最终它就成不了型。

这又是某种意义上的“无名”。你问“有什么方法完成一个接口定义”?答案是没有。但你认 为“那么接口就没法定义了?”,回答是“不是”。这个事情可以做,但它“无名”,你不要指 望“精确定义它”,然后把它变成生产线行为。这个世界的主体就是“无名”(没有Pattern的 )。