我们选择 panda 配置并没有特别的原因——任何其他配置也同样适用。内核代码是用 C 语言编写的,它源自 Linux 内核。因此,我们的方法也适用于通用 Linux 内核的任何配置。
现在我们都知道 C/C++ 是一门复杂的语言,因此我们预料到分析会很困难。但这种困难仅仅是指解析器。有了 Clang 解析器,我们信心十足,并且很高兴没有遇到任何问题。我们的目标是检查 panda 配置中所有的 C/C++ 源文件,并理解它们之间的相互关系。为此,有必要弄清楚哪些文件被包含或排除在 panda 构建之外。然后,还有如何处理所有文件的编译、包含和链接问题。这些都需要花费精力。最终的结果显示了 Linux 内核的耦合程度有多高。
首先,我们必须承认 Linux 内核是编写得很好的。进入内核的内容受到严格控制。考虑到它在全球 IT 基础设施中的重要性,这正是人们所期望的。我们还应该记住,当今使用的许多模块化机制都源于 Unix。设备驱动程序插入操作系统的概念由 Unix 推广,并已在今天司空见惯。应用程序管道也是 Unix 的创举。然而,Linux 内核本身的模块化却很差。
部分问题在于,当 Unix/Linux 内核被开发时,编程语言对模块化的支持很差。例如,C 语言没有接口的概念,因此自然不支持依赖倒置(尽管这是可以实现的)。而且,Linux 没有真正的模块化机制来验证或强制执行模块化。
在应用了分区算法之后,一些事情变得显而易见。这个分区算法根据依赖关系对子系统进行重新排序,揭示了哪些是“底层”,哪些是“高层”。在理想的实现中,高层的开发人员只需要理解底层的 API,而底层的开发人员只有在接口受到影响时才需要担心高层。在一个耦合的系统中,开发人员需要同时理解两个层次,这使得理解变更所带来的影响变得相当困难。事实上,在 Android 内核中,几乎所有的层次都是耦合的,开发人员有时可能需要理解数千个文件才能对自己的更改有信心。
这也意味着分解的初衷已经丧失。例如,'arch.arm' 与 'kernel' 紧密耦合,以至于开发人员很难在不理解另一个的情况下理解其中一个。注意,即便是 'drivers' 也与系统的其余部分耦合。我曾尝试为驱动程序的基础层创建一个独立的层次,甚至移动了一些基础驱动程序,如 'char' 和 'tty',但耦合依然存在。遗憾的是,即使是一些较新的驱动程序也与内核耦合。
所有这些都表明,除非重点关注架构的定义和验证,否则即使是管理得最好的软件系统也会随着时间的推移而经历架构腐蚀。
附件:[分析 Android 内核.png]