本文大部分内容由 https://lwn.net/Articles/734016/ 翻译改编而来。
内核测试的现状
新的内核总是会定期发布出来,但是其实大家并不是十分了解内核是如何被深入测试的。那么这里可以提前告诉大家,内核主干有可能并没有做过充分的测试,而稳定内核可能会更少。。。 So what is going on there? Is stable kernel really stable? 刚好今年9月在洛杉矶举办的《Linux Plumbers Conference》有一个BOF(birds of a feather)会议,Dhaval Ginal和Sasha Levin组织了一个关于内核测试的相关讨论,让我们一起去看看。
由于大部分BoF的参会人员来自各个主要的Linux发行商,所以Giani开场的时候提了一个问题:“大家对于稳定内核(stable kernels)都做过了多少测试呢?大家是不是都是仅仅测试自己发行的内核然后再从稳定内核中backport一些安全以及其他相关的修复呢?对于稳定内核的测试从来都是置之不理呢?“ 对于这个问题的答案么,他自己半开玩笑的建议:可以将测试留给了用户。除了上面这个半开玩笑的建议以外,大多数人都一致认为:目前对于稳定内核,除了简单的构建-启动测试以外,几乎很少有其他方面的测试。那么这个现象是如何造成的呢?
内核测试的困境
第一点:开发者很难知道该去选择什么样的上游内核(upstream kernel)来测试。linux-next tree和稳定内核以及内核主线(mainline)都是在不断地变化着,要想做到稳定测试是一件很难的事情。但是大家又都一致同意,能在代码发布出来之前,发现其中的BUG还是很有价值的,所以一些发行版的研发人员就尝试测试-rc1的内核。这样做的话,如果bug被发现,它们将在发布前就被修正,这样看起来是不错的,但是这样需要非常多的时间和机器去做好这件事。另外,测试时使用的哪个版本的内核配置,也会使这个问题的复杂度翻倍。
第二点:内核测试消耗大量的人力物力。有人举了一个生动的例子,红帽(RedHat)需要花费一年的时间去稳定RHEL选定的内核版本。而粗略估计有300个工程师去做这个事情,这也就是意味着一个公司需要花费了300个人年去测试和稳固一个内核(译者在此处还是要感谢一下REDHAT)。此外,在驱动程序中,通常驱动的自检程序是应该由内核开发人员编写,但将个人的测试变成可以更广泛使用的东西需要很多时间和精力,而驱动的作者根本没有时间去添加一个自检程序,所以驱动大多数是没有自检程序的。在许多情况下,正是由于这个原因,导致代码质量很差。因此,现有的大部分自检程序可能都是在子系统中并已经做了很好测试,这些测试用例还能发现的大部分的错误主要发生在与开发者自己运行的硬件架构不同有关,比如ARM相关的错误就是这样被发现的。
第三点:启动测试(boot testing)占了上游内核测试的大头,占据了开发者更多的精力,所以最新发布的内核肯定可以启动起来的。拥有特殊硬件的人会测试他们的驱动,所以通过这一测试也可以发现大部分驱动的bug。像Ftrace、调度器和内存管理由于使用的很广泛,所以他们的测试也比较充分。除了以上这些组件以外,内核中其他的一些非核心或者不是被普遍使用的功能就可能没有那么多的功能测试了。
第四点:内核测试门槛较高,如环境设备和知识储备。对于一些想要测试驱动程序的人来说,他可能没法获得相应的硬件,即使有硬件,他还需要深入了解设备是如何工作的。理想情况下,驱动程序的作者应该测试这些设备,而大部分情况下也确实是这样。不过这个解决方案仍然不是完美的。
第五点:测试覆盖不全面,测试方面单一。因为虽然驱动作者试图确保在他的测试用例下驱动程序是正确工作的,但是他们有着不同的动机,如果他被雇佣做这个事情那么可能测试会比较全面,而如果他仅仅是为了让他自己的硬件可以正常工作,那么可能测试不会太全面,同时相应的文档也基本没有。
第六点:需要制定测试指南和帮助文档。例如,在内核测试中我们还需要发现一些可能引入的性能退化(performance regressions),但是如果仅仅是做一些”随机性能测试“(random performance testing),这是很难发现性能退化的,因此我们需要制定一些性能测试指南,以便可以进行一些苹果对苹果(apples-to-apples,译为同类型的比较)的对比测试。再如,就像Mel Gorman的MMTests作为“kbench”这一性能测试套件的基础,貌似有些与会者对这个测试套件不熟悉,所以MMTests需要有更好的文档来帮助使用者去了解它。
第七点:进行内核测试的另一个困难是要内核配置的数量非常庞大。当稳定的内核被发布的时候,它可能有100个补丁,但任何测试套件都不可能执行许多次去测试这些补丁,那么测试稳定版本的意义到底是什么呢?
总结一下,在所有的这些测试工作中,最大问题就是资源,我们需要更多的人和更多的机器,从而可以更早地发现和修复错误。
企业是如何做的
我们再把话题切回到稳定内核。如果我们可以阻止所有可能的bug引入到内核,那么主线内核(mainline)无疑是完美的,我们也就不需要什么稳定分支了(stable trees)。当然现实是残酷的,完美也是不可能的,所以我们仍然需要稳定分支,但是发行版真的会使用稳定分支么?让我们一个一个看过去。
企业例子之一(红帽)
红帽有一个大型测试实验室,有6000多台各种各样的机器分布在世界各地。实验室使用Beaker并测试了大量不同的内核配置,目前内核测试主要集中在三个RHEL内核和两个Fedora内核上,未来他们会计划添加一些主线版本的内核。在红帽内部,不同的团队专注于他们感兴趣的驱动,比如存储团队在测试各种存储设备,而RDMA团队在测试RDMA类型的硬件。所以,对于红帽而言,他们只从稳定的树上去挑选(cherry-pick)一些修复的补丁。 RHEL的每个小版本内核都有8-10万个补丁,但是所有的这些补丁都来自上游而不是稳定分支。RHEL内核团队会查看稳定的分支和最新的主线内核,然后寻找有必要使用的补丁。至于对应的测试则取决于这些补丁是应用于哪一个子系统; 一些子系统在补丁追溯方面做得很好,而其他子系统则较差。对于稳定内核的使用,一般Red Hat只是编译了一下并用它来和RHEL内核做对比测试,以确定这些错误是来自上游还是在RHEL中引入的。
企业例子之二(SUSE和Ubuntu)
再看看SUSE,他们确实在构建稳定内核,但他们仅仅是为了挑选(cherry-pick)补丁。他们有考虑将稳定的内核测试添加到SUSE的测试网格(testing grid)中,但大家还不清楚它会对公司带来什么样的价值。Ubuntu面对的情况也是类似的:他们除了建立稳定的内核以外,并没有对它们进行正式的测试。
企业例子之三(Linaro)
Linaro目前正在为谷歌开发一个使用内核自检(kernel self-tests,缩写kselftest)和Linux测试项目(Linux Test Project,缩写LTP)来测试稳定的内核项目,这些测试会针对每个稳定的发布版本来进行。自检测试的确能发现bug,但编写这些自检测试的人可能并不是引入大多数错误的人。所以自检测试还只是一个开始,显然Linaro还需要增加更多的测试。
所以综合来看,版本发布者一般都只是关心,合并到稳定版本里的修复(fix)是否是正确的,但是他们只是从稳定版本中摘出修复(fix)然后放到自己的内核中做测试,对稳定内核本身的测试是非常有限的。于是有人建议可以由Linux基金会与Canonical,SUSE,Red Hat等公司一起组建一个合作项目,大家一起贡献一部分机器同时形成一套测试套件来进行稳定内核的测试。
测试的不断改善
例一:0天内核测试服务(0-Day kernel test service)。这个服务也不仅仅是构建和引导测试(build-and-boot tests),还包括一些性能测试。 kernelci.org项目也正在对许多不同的硬件进行构建和引导测试(build-and-boot tests),这些都是非常有价值的,但他们没有做任何真正的功能性的测试。当然事情肯定会变得越来越好,大家不都是说“有比没有好么”。
例二:LTP(Linux Test Project)。它可以测试很多东西,但也有很多地方它并不会去测试。它会被一些发行版使用,然后也肯定能在里面发现一些bug,但很明显我们需要更多更好的测试套件。
例三:性能测试(benchmark)和模糊测试(fuzzing)。会上还讨论了稳定内核的模糊测试。模糊测试上游内核是一种最好的选择,因为所有问题的修补都在那里,但它仍然可以在发行版的内核中发现问题。目前syzkaller fuzzer已经可以针对它发现的问题自动生成小的测试用例,大家也觉得这些应该被加入自检测试集。虽然有人提到一些BUG只在Kernel Address Sanitizer(缩写KASAN)下才会出现,但这些测试在那些没有被配置KASAN的内核上运行的时候是可以简单地被跳过的。除了找BUG的测试之外,内核还需要更多的性能测试来发现性能退化(performance regressions),有人提到可以用Mel Gorman的MMTests作为“kbench”这一性能测试套件的基础,只是这个套件对应的文档很少了,所以我们需要在内核文档目录中建立一个测试套件目录作为一个开始,但任何复杂的性能测试显然需要更加深入的文档。
例四:基准数字(single number)。有人提议说,如果我们有一个性能测试然后给出一个基准数字,而我们可以基于这个数字来判断性能是否有退化就完美了(比如BogoMips背后的想法),当然有另外一些人会质疑是否真的存在这样的测试系统。
例五:自我测试(self-tests)。越来越多的自我测试,正在被添加到内核主线中,但是稳定的内核却不会从中受益。有些人正在使用较老的内核进行最新的自我检测,于是有人提议也许自我测试本身应该被移植到稳定的内核树中。
例六:神经网络训练。随着BoF的结束,Levin要求发行版的维护者将他们正在使用的补丁推向稳定的分支中去。一般发行版中的问题在稳定内核中也一定存在。他最近一直在努力训练一个神经网络来识别适用于稳定内核的补丁,这引起了一些笑声,但他说结果是“出人意料的好”。
内核测试的一些其他声音
Greg Kroah-Hartman是稳定内核的维护者,他没有参加BoF但是其实对BoF的讨论内容也有兴趣。于是第二天当Levin在一个小型会议中概括总结BoF的讨论结果的时候,Greg给出了他的一些意见。
首先正如Levin所说,在BoF中大家提出了很多的观点,但是并没有什么相应的解决措施。有人建议应该向kernelci.org项目提供更多的硬件,Kroah-Hartman同时希望能看到更多的功能测试。另外还有一些人提议应该让Linaro和kernelci.org一起努力合作这样更好。
关于移植自我检测(self-tests),Kroah-Hartman并不反对这个想法,只要它们能在出问题的内核上运行就可以。他也同意如果发行版能将它们的修补补丁打在稳定的分支(tree)上,那将是一件很好的事情,而且他提到Fedora和Debian已经在该领域做得很好。另一位与会者表示,发行版经常会尽快为用户快速解决问题,然后才会做好上游内核的工作。 Kroah-Hartman表示,如果有BUG在上游内核中没有被修复的话,他也不会在稳定内核中修复,这样一方面上游内核和稳定内核做到了“bug兼容”,另一方面也给了那些厂商一些压力来修复它。
结语
我们需要进行更多的内核测试,这是毋庸置疑的,但是它究竟应该采用什么样的形式以及由谁来做仍然不清晰。如果幸运的话,在不久的将来这块会有一些进展,同时也意味着我们有可能会更早地发现BUG。当然,完美是不可能的,但是我们大家都希望能够减少内核里的错误,对么?另外一点,大家对于上游内核(upstream)的热情是远远高于稳定内核(stable)的,所以对于稳定内核的测试肯定仍然会比较少,所以稳定内核的“稳定性”是大打折扣的。