关注了就能看到更多这么棒的文章哦~
Bugs and fixes in the kernel history
By Jonathan Corbet
December 8, 2022
DeepL assisted translation
https://lwn.net/Articles/914632/
内核发布的每个新版本中都修复了很多 bug,但每个版本也会引入新的 bug。这引出了一个基本性的疑问:内核社区修复 bug 的速度是否比引入 bug 的速度快?答案并不是很清晰,但是,如果能够找到明确答案的话,就能给内核代码库的长期未来发展如何给出一个重要的指示。虽然挖掘内核的提交历史不能给这个问题一个明确的答案,但它可以提供一些关于这个答案的线索。
Tagged fixes
在目前的内核实践中,修复 bug 的开发者应该在 patch 描述中加入 Fixes 这个 tag,指明引入该 bug 的 commit。这是一个相对来说存在不是很久的新的做法;虽然各种形式的 Fixes 标签出现在 commit 中已经有一段时间了,但第一个使用目前这种形式的 patch 以及指出错误 commit 的 hash 值的做法,似乎是 2013 年 10 月 Rafael Wysocki 为 3.12 版本所做的一个 revert commit。在那个版本中,只有两个 commit 指明了以前版本中引入 bug 的 commit,但在随后的开发周期中,这个标签的使用迅速增加。6.0 版本的内核中包括 2784 个带有 Fixes 标签的 commit,其中 2112 个 commit 指出的引入 bug 的都是过去某个版本中的 commit(其余的 commit 则是修复了 6.0 版本中引入的 bug,因此从未出现在正式发布的内核中)。
因此,从理论上讲,我们可以在一个开发周期内简单地统计这些 Fixes 标签,从而了解其中 fix 了以前版本中的多少个 bug。然后再看一下后续发布的所有版本中的 Fixes 标签,来了解有多少 bug 是在这个发布周期中引入、并在后来的周期中被 fix 的。如果在一个开发周期中 fix 的 bug 数量经常超过该周期中引入的 bug 数量,那么很有可能内核是在随着时间的推移变得更加完善的。这个想法很简单,但是遇到了一些实际的困难,我们将在后面探讨。这里先从一个显示上述分析结果的图表开始:
[每个版本引入和修复的 bug]
(也可以在 https://lwn.net/Articles/914479/ 这里看到相应的数据表格)。
在上图中,较粗的线条是对 Fixes 标签的数量统计;因此棕色的 "bugs introduced(引入的错误) "线条是在该版本中的 commit 被后续版本标记了 Fixes 标签的次数,而绿色的 "bugs fixed(修复的错误) " 线条显示的是在该版本中的 Fixes 标签指出了以前以前版本中的多少个有 bug 的 commit。较细的线条则是计算 commit 的数量。"buggy commits introduced(引入的有缺陷的提交)" 是指某一特定版本中后来被 fix 的 commit 的数量,而 "commits fixed(被修复的提交) "则是指在某一特定版本中修复了此前那些版本中引入的多少个 commit 中的 bug。
这两组数字之所以不同,原因很简单:有些 commit 引入的 bug 很多,导致需要被 fix 不止一次——我们很快还会回到这个话题。不过这里有一个有趣的差异:在任意一个开发周期中,bugs fixed 数量与 commits fixed 的数量密切相关,但 bugs introduced 数量与 buggy commits introduced 数量就有很大的差异。从这个区别中我们可以得出的结论是,引入大量 bug 的 commit 需要多个开发周期才能将这些 bug 全部都修复掉。在任何一个开发周期中,都很少看到对同一个 commit 进行多次 fix 的情况。
不过,这个数据图能回答本文开头提出的问题吗?如果对它进行一个很天真的解读的话,因为这些线产生了交叉,因此,从 5.1 版本开始,所修复的 bug 数量就已经超过了引入的 bug 数量。但是,这个结果显然是需要进行一些更深入的分析的。正如在最近对 Fixes tag 的检查中看到的那样,很多 bug 在内核中潜伏了很长时间。内核开发者仍然在寻找和修复 2.6 时代那么早期就引入的 bug,甚至更之前的 bug。所以最近的内核的 "bugs introduced" 这个数字显然太低了,也可以从最近的版本中这些数字很接近 0 这一特征就能看出这点。
不过,引入的 bug 数量似乎稳定在平均每个版本 1200 到 1400 个的范围内;这可以在较早的版本中看到,这些数字在近期版本里也不太可能有太大的变化。这一趋势似乎一直持续到 5.8 左右,此后曲线下降,这显然不能反映长期以来的现实规律。如果这种模式可以保持下去(时间会告诉我们),那么曲线的交叉点可能会继续变动,但似乎有可能保持在 5.x 时代的早期。如果真的是这样的话,那么至少在最近一段时间,内核社区 fix 的 bug 可能比它引入的更多。
是什么原因导致了情况发生变化的呢?编者虽然不知道,但完全不影响像其他人一样挥舞双手来夸夸其谈一下。一种可能性是开发工具的改进,特别是越来越多地使用 fuzz testing 来发现古老的 bug 并且防止引入新的 bug。内核的测试基础设施(虽然仍然是不够的)的缓慢但稳定的增长提供了帮助。进一步坚持对 patch 进行 review,在内核增加的代码量不断增长的情况下也可能有助于保持引入的 bug 数量大致不变。也可能上述情况都不是主要原因。
几乎可以肯定的是,开发人员在添加 Fixes 标签方面变得更加严谨,导致更多的 bug fixes 被统计进来,也许其实这个数字的变化并没有反映出 fix 速度的变化。总的来说,Fixes 标签可能是我们对实际 bug 数量的最佳入手点,但仍然是一个不准确的指标;这取决于开发人员是否仔细正确地进行了添加,并正确地指向了那个引入 bug 的 commit。
The buggiest commits
不过,这些 tag 可以很可靠地用来提供出一个信息,那就是识别出内核历史上带有最多 bug 的 commit。刚才提到过,有些 commit 在一段时间内需要不止一次进行 fix;有些则需要很多次 fix。下面是一个 Git 时代需要的 fix 数量最多的 commit 的表格。
Commit | Fixes | Description |
---|---|---|
1da177e4c3f4 | 355 | Linux-2.6.12-rc2 |
e126ba97dba9 | 70 | mlx5: Add driver for Mellanox Connect-IB adapters |
8700e3e7c485 | 65 | Soft RoCE driver |
46a3df9f9718 | 54 | net: hns3: Add HNS3 Acceleration Engine & Compatibility Layer Support |
9d71dd0c7009 | 42 | can: add support of SAE J1939 protocol |
76ad4f0ee747 | 38 | net: hns3: Add support of HNS3 Ethernet Driver for hip08 SoC |
604326b41a6f | 38 | bpf, sockmap: convert to generic sk_msg interface |
1738cd3ed342 | 38 | net: ena: Add a driver for Amazon Elastic Network Adapters (ENA) |
e1eaea46bb40 | 35 | tty: n_gsm line discipline |
e7096c131e51 | 34 | net: WireGuard secure network tunnel |
1c1008c793fa | 33 | net: bcmgenet: add main driver file |
d5c65159f289 | 29 | ath11k: driver for Qualcomm IEEE 802.11ax devices |
c0c050c58d84 | 27 | bnxt_en: New Broadcom ethernet driver. |
c09440f7dcb3 | 27 | macsec: introduce IEEE 802.1AE driver |
7724105686e7 | 26 | IB/hfi1: add driver files |
d2ead1f360e8 | 25 | net/mlx5e: Add kTLS TX HW offload support |
7733f6c32e36 | 25 | usb: cdns3: Add Cadence USB3 DRD Driver |
726b85487067 | 24 | qla2xxx: Add framework for async fabric discovery |
1e51764a3c2a | 24 | UBIFS: add new flash file system |
a49d25364dfb | 24 | staging/atomisp: Add support for the Intel IPU v2 |
96c8395e2166 | 24 | spi: Revert modalias changes |
3c4d7559159b | 23 | tls: kernel TLS support |
d7157ff49a5b | 23 | mtd: rawnand: Use the ECC framework user input parsing bits |
6a98d71daea1 | 22 | RDMA/rtrs: client: main functionality |
3f518509dedc | 22 | ethernet: Add new driver for Marvell Armada 375 network unit |
ca6fb0651883 | 21 | tcp: attach SYNACK messages to request sockets instead of listener |
ad67b74d2469 | 21 | printk: hash addresses printed with %p |
c29f74e0df7a | 20 | netfilter: nf_flow_table: hardware offload support |
d2ddc776a458 | 20 | afs: Overhaul volume and server record caching and fileserver rotation |
1a86b377aa21 | 20 | vdpa/mlx5: Add VDPA driver for supported mlx5 devices |
人们可能会想,Linux-2.6.12-rc2 出了什么问题,它已经被 fix 了(据最后统计)355 次。其实这是开启了 Git 时代的初始 commit,所以对该 commit 的 fix 其实都是针对 2005 年 4 月之前的 bug 的。哪怕是到了 2022 年,仍然在不断发现和修复那个年代的 bug 。
此外,我们能得出的结论并不意外:需要大量 fix 的 commit 往往是那些添加了一个新的重要子系统的大型 commit。大量的新代码就会不可避免地带来相当数量的新 bug,而这些 bug 需要随着时间的推移被发现和 fix。一个有趣的例外可能是 ca6fb0651883("tcp: attach SYNACK messages to request sockets instead of listener"),它在 2015 年新增了 47 行代码,此后被 fix 了 21 次,最近一次是在今年 2 月份的 5.17 版本。同样值得注意的是 96c8395e2166("spi: Revert modalias changes"),它删除了 6 行代码,此后需要进行 24 次 fix。不过,除了这些之外,其他的需要大量 fix 的 commit 都是很大型的 commit。
更有趣的一点是,在上面显示的 30 个得到 fix 最多的 commit 中,有 22 个是与网络(包括 InfiniBand)有关的。网络子系统是内核的一个重要部分,但它仍然是整个内核中的一小部分,而且不是唯一一个合并了大型 patch 的子系统。不清楚为什么网络相关的 patch 比较特别,需要许多 fix 的可能性会更大。
不幸的是,存在 bug 是软件开发中的一个不变的事实,而且我们不太可能在短期内摆脱这个现实。不过,如果对上述数据的乐观解读反映了现实的话,那么内核开发社区有可能已经达到了修复的 bug 多于引入的 bug 的地步。LWN 一定会在未来重新回头审视这个话题,看看情况是如何发展的。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~