林纳斯·托瓦兹与所谓的“垃圾代码”
林纳斯·托瓦兹批评谷歌工程师提交的RISC-V Linux内核代码是“垃圾代码”。争议焦点在于辅助函数make_u32_from_two_u16()与托瓦兹提议的显式代码方案。让我们探讨正确类型转换、位操作以及创建更安全可复用宏/函数的重要性——这些实践能提升代码清晰度并减少缺陷。
近日,林纳斯·托瓦兹公开斥责谷歌工程师提交的RISC-V内核代码为“垃圾代码”:
https://lkml.org/lkml/2025/8/9/76
首先,我认为林纳斯应当更尊重他人。
此外,让我们聚焦于make_u32_from_two_u16()辅助函数。据我理解,这是C语言预处理器宏(因Linux内核主要用C语言编写)。让我们将该辅助函数与Linus提出的显式代码“(a << 16) + b”进行对比。
首先,该显式代码很可能存在错误,事实上Linus补充道“可能需要添加强制类型转换”。
为何需要强制类型转换?用林纳斯的话说:“[…] 确保’b’的高位不会污染最终结果”。那么按他的思路,显式代码应写成“(a << 16) + (uint16_t)b”吗?
但让我们退一步思考:我们应该自问:’a’和’b’的类型是什么?从辅助函数的名称来看,我认为它们是两个“u16”,即两个uint16_t。
如果让我编写一段C代码,要求将两个uint16_t值'和a''b'作为输入并组合成uint32_t,我会这样写:
((uint32_t)a << 16) | (uint32_t)b
我会使用_位运算_或(|)而非加号(+);我认为这更合适,因为我们此刻正在进行位操作层面的处理。不过这或许只是个人偏好和编码风格的问题。
此外,我会像上面示例那样对'a'和'b'都进行类型转换。
我不确定Linus所说的“b的高位可能污染最终结果”具体指什么。难道’b‘本身就是uint32_t类型?若属此情况,我会使用0xFFFF这样的位掩码配合位与运算(&)清除’b‘的高位。
另外,我还会为’a‘和’b‘取更清晰的名称,比如’high‘和’low‘,明确区分高16位字和低16位字。
因此,正确的显式代码绝非简单如“(a << 16) + b”。你可能需要进行类型转换,且必须正确使用括号确保语法无误。同时还需注意:是否需要用位掩码清除’b’的高位?
如果将两个uint16_t组合成uint32_t的操作出现在多个位置,那么采用Linus邮件中提倡的显式代码,确实会增加引入错误的机会!
因此,更优雅、清晰、安全的方式是提升代码的语义层次,编写辅助函数或宏来安全正确地完成组合操作。
一个C语言宏可能如下所示:
#include
#define MAKE_U32_FROM_TWO_U16(high, low)
( ((uint32_t)(high) << 16) | (uint32_t)(low) )
是否需要考虑’low’的高位需要清除的情况?此时宏将变为:
2
#define MAKE_U32_FROM_TWO_U16(high, low)
( ((uint32_t)(high) << 16) | ((uint32_t)(low) & 0xFFFF))
如你所见,类型转换、括号使用以及潜在的位掩码操作确实需要谨慎处理。但一旦代码正确无误,你就能在每次需要时安全便捷地复用它!
因此真正的垃圾代码,其实是反复编写显式易错的代码,比如“(a << 16) + b”!而非像上文那样,将这类代码封装到合理的辅助宏(或函数)中。
在C++中,我们可以定义内联辅助函数替代预处理宏,例如:
#include
inline uint32_t make_u32_from_two_u16(uint16_t high, uint16_t low) {
return (static_cast(high) << 16) |
static_cast(low);
}
我们甚至可以进一步优化这个函数,标记为_noexcept_,因为它保证不会抛出异常。
同时还可以将函数设为_constexpr_,因为当输入参数为常量时,它可以在编译时进行评估。
经过这些额外优化后,我们得到:
inline constexpr uint32_t make_u32_from_two_u16(
uint16_t high,
uint16_t low) noexcept
{
return (static_cast(high) << 16) |
static_cast(low);
}
本文文字及图片出自 Linus Torvalds and the Supposedly “Garbage Code”

> 我觉得林纳斯应该更尊重他人
你们都该练就更厚实的皮肤。若我们真是工程师,就该直面批评。林纳斯的评论针对的是代码本身,从未侮辱开发者。若你将自我价值与编写的代码挂钩,那是你自己的问题。
你们想让他怎么做?用糖衣炮弹吗?“呃亲爱的请重写这个函数,它还不够完善”?
这是PR(合并请求),对代码的诚实公正反馈(及改进建议)才是唯一要务。若代码是垃圾,审阅者就该直言不讳。
这是个高度公开的邮件列表,每当Linus发表此类评论,都会被广泛传播。
去谷歌搜索那位工程师的名字吧。尽管他参与了RISC-V的创建,但几乎所有搜索结果都包含“垃圾”这个词。若我的职业声誉遭遇此等羞辱,我会羞愧难当,精神上彻底崩溃。我没有厚脸皮承受成千上万人的嘲笑。
仅凭这一点,我永远不会考虑参与Linux内核开发,也会劝阻他人参与。
批评他人作品本不必沦为看客运动,许多人始终能做到理性评价。
至于你评论中的“亲爱的”:这世界远不止黑白两色。你该尝试看看。
哇,这可糟了。我没想到会对SEO造成这么大的影响。
没错。现在想象一下,对于那些在其他领域并不出名的工程师来说,影响会有多大。
以下是林纳斯评价提交代码时用的词:疯狂、愚蠢、垃圾、需要重写、难以理解。
你愿意在用这种词反馈的地方工作吗?我肯定不愿。不过也许我就是个玻璃心。
背景是:如此规模的合并请求本应在合并窗口期前提交,而此请求却在窗口期后提交,且修改了其他子树使用的通用头文件。此类情况此前也曾发生。
因此评论不仅针对代码本身,更指向整个处理流程。最终结论是允许修改但不得触及通用头文件,并建议在下个版本周期初期重新提交PR。
这种措辞并非煽动性言论,而是严正态度,且为未来提交留有充分余地。
若在我的职场遇到这种情况会介意吗?大概不会。尤其当我的上司是位经验丰富的知名程序员,且能与我分享行业内幕时。
关键在于,若他能在合适时机收到这个补丁,打赌他会如何评价?大概率会指出代码质量问题,给出相同反馈但措辞会温和许多吧?
我感觉他更在意的是时机问题,虽然对代码本身不满意,但他的反应_正是因为_时机不对。不过邮件的措辞方式让人难以判断。
如果发件人是像林纳斯这样受人尊敬的工程师,那另当别论。但如果是能力平平却自视甚高的人?绝无可能。
关键在于上下文。
所以只要你是受人尊敬的同行,就能肆意辱骂他人而逍遥法外?
这种逻辑只会滋生暴君。
若能让我成为更优秀的工程师且反馈合理,当然可以。但若攻击我的个人信仰或家人,那便是另一回事。
我并非主张所有人都适用此法。若有人无法从中获益,完全可以另寻良师。
你可以给出诚实公正的反馈,无需侮辱他人或其作品。
软件工程是协作过程,而非对抗过程。
> 软件工程是协作过程,而非对抗过程。
协作过程本身就存在对抗性。当他人贡献违背自身目标时妥协,实则背离了目标。有时为达成次要目标而妥协,总比完全无法达成目标要好。但当 stakes 极高(而 Linux 的 stakes 极其巨大)时,妥协目标便不再合适。Linux 和 Linus 处于无需妥协目标的地位。
用不同措辞拒绝代码究竟算哪门子“投降”?
在软件工程领域待得够久,职业生涯里总会多次遭遇这类社交失调的小暴君。不过看到这么多暴君在本帖里自曝其短,倒也挺有意思。
确实,但不确定
> 这种行为会让世界变得更糟
是否真有必要用这种方式表达观点。
这难道不是双向的吗?如果我们是“真正的工程师”,就不该用夸张手法表达“这既没按时完成,质量也不达标,我厌倦了反复强调”这种简单道理。
实际上,沟通方式至关重要——即便在工程领域亦然。这并非要求时刻保持绝对客观(现实交流本就如此),而是需要花时间理解对方如何接收我们的言辞。
试想:若我想让你去商店,却始终用某个无意义的词代替“商店”,我们都会认为这是糟糕的沟通。因为我知道你听不懂那个词,自然无法理解我的意图。
语气亦是如此。语气是沟通的一部分,因此当我使用过于激进或过于客气谨慎的语调时,你就无法理解我真正想表达的意思。找到恰当的语气对良好沟通至关重要。具体语气取决于对话对象——与朋友交谈时我会用一种语气,与陌生人则另当别论;对英国人表达时采用一种方式,与德国人交流时则另有一套。
顺带一提,我注意到Linux邮件列表的交流风格普遍偏好针锋相对,若这是林纳斯营造的文化氛围,他选择这种沟通方式也无可厚非。当然潜在风险在于:若始终维持这种交流模式,当不熟悉该文化的新人加入时,沟通障碍可能令其望而却步。
他描述的是代码,而非编码者。
这并非对编码者的攻击,仅是对其产出的评价。
若我们总将对作品的批评视为对自身的攻击,那么整个社会都将陷入困境。
咦,我对最后一段的看法几乎完全相反。认识到我是作品的创作者,因此必须对作品质量(以及这次的时效性)负责,这才是自我反思的核心。若缺乏这种认知,我如何提升能力?当对作品的批评与自我批判完全割裂时,我就无法成长——因为我从未真正被批评过。
我觉得这里存在一个中间地带:我理解工作中的失败未必定义我的价值,但我仍需为之负责。
其实你完全可以不带贬损地表达反馈。无需刻意粉饰。让我们重新解读托瓦兹那段话,去除那些抨击和辱骂:
> 不行。这段代码质量糟糕透顶,提交时间又太晚。我要求提前提交拉取请求是因为正在出差,若你无法遵守规则,至少确保提交的代码质量合格。
稍作调整:
> 这段代码将大量非RISC-V特有的低质量代码添加到通用头文件中。
垃圾代码
> 合并窗口期临近时收到如此糟糕的代码,我深感愤怒。
其实他并非真正愤怒于代码本身,而是对提交时机不满。要我说,若这段代码在合并窗口期前很久就提交,他态度怕是温和得多。
例如这个 make_u32_from_two_u16() 辅助函数。它让代码变得难以理解,甚至比根本不使用它还要糟糕。
“那玩意儿让世界变得更糟”——但事实真是如此吗?许多事物都在让世界变得更糟:疾病、犯罪、狗狗币…但辅助函数真能主动削弱人类生存能力吗?
> 若将代码写成“(a << 16) + b”,你就能明确理解其作用及高位字符的处理方式。或许你需要添加强制类型转换来确保'b'的高位不会污染最终结果,所以代码可能不够优雅,但至少不会出错或令人费解。
看,这难道很难吗?我根本没改动任何内容。
> 反观 make_u32_from_two_u16(a,b) 的写法,你根本无法确定字节序。换言之,你不仅让情况变得更糟,还把这个辅助函数塞进了通用非RISC-V文件。若他人使用此函数,必将拖累其他代码质量。
我几乎没改动什么,但没粉饰太平。
> 所以不行。请别把这类函数写进代码。它们不该出现在通用头文件里,更别说在合并窗口后期才提交。
注意我保留了“该死”这个词。
> 警告你:不准再提交迟到的拉取请求,也不准在RISC-V代码库外添加无关代码。
这很合理。托瓦兹生气是有道理的。
> 现在,我_希望_RISC-V部分没有类似代码,但这取决于你的选择。不过通用头文件里的内容不会被这类东西污染。在合并窗口关闭前一天提交大型拉取请求,指望我忙得顾不上处理——这可不是什么好策略。
微调而已。
> 因此你可以在6.18版本重试——在合并窗口期伊始提交,且无需夹带垃圾代码。
毫无改动,甚至还借机用了“垃圾”这个词。
或许我该换种你可能欣赏的表达方式:托瓦兹在沟通中是个混蛋,也是个心智不成熟的人。他沟通能力欠缺,让付出大量时间精力改善现状的人倍感委屈;而当激怒他时,这个情绪失控的卑鄙小人便会暴露本性。
> 或许我该换种你可能欣赏的表达方式:托瓦兹在沟通中是个混蛋,也是个发育不良的人。他无法与他人沟通,导致对方明明付出大量时间精力改善状况却仍感到沮丧;而当激怒他时,这个情绪失控的卑鄙小人便会显露本性。
你看,你在谈论这个人本身,而林纳斯讨论的是代码及其迟交行为。林纳斯的观点要温和得多
我理解你的观点,但代码并非与作者完全割裂。当然,你必须能接受批评且不要过度在意,但我认为不能用各种过于苛刻且毫无建设性的言辞指责代码,然后声称这仅关乎代码本身,与作者毫无关联,因此无伤大雅。毕竟,那些“晦涩难懂”的“垃圾代码”,不正是出自某人之手吗?
作者确实有道理;整个BcacheFS风波部分源于批评其他文件系统设计的评论(当然,压垮骆驼的最后一根稻草是那些暗示开发者需要“检查头脑”的言论,以及在变更冻结期强行添加功能等失当行为)。
我印象中核心问题在于反复无视开发流程,将实验性bcachefs模块的快速推进置于内核稳定性之上
总的来说,四处审查他人讨论并非社会中值得推崇的角色
更具体地说,保持礼貌固然可贵——而林纳斯通常如此。
对代码持有强烈观点本就是他的职责所在。要求他每次都措辞完美且永不显露挫败感——尤其当主要通过公开、基于文本且永久可见的沟通方式工作时——这根本是不可能的标准。
四处评头论足地审视他人讨论,在社会中并非真正有价值的角色。
你描述的本质上是养育子女的行为,而这在社会中具有极其重要的价值。
更广泛地说,最近有人对我引用那句经典箴言:“伟人讨论思想,平庸者讨论事件,愚者讨论他人”,我不得不予以反驳。讨论他人行为本就是人类协商文化价值观、道德规范与礼仪的天然方式,这绝非可轻视之事。
你怎么能把审查他人讨论等同于养育子女?
你是不是用错了词?
养育子女是父母对亲生子女的行为,不是对网络陌生人的…
我从未见过自己或同事称其他开发者的代码为“垃圾”。混蛋行为永远不可接受。无论你是林纳斯、乔布斯,还是那些偶然有点才华的无穷无尽的霸凌者。
这太不可思议了,在我看来这可是家常便饭。我写过被当面指责的垃圾代码,有时对方措辞相当不礼貌;我也指责过别人的垃圾代码,有时同样不留情面。
当然保持礼貌更好,但我不会因为有人偶尔失态就称其为混蛋或霸凌者。
恕难苟同。在所有可能的批评中,“垃圾”一词已算相当温和
哈哈,林纳斯·托瓦兹、史蒂夫·乔布斯之流…不过是些“有点才华”的霸凌者罢了
这…这说法连当玩笑都难以成立…
> 更具体地说,保持礼貌是件好事,而林纳斯通常都做得到。
哈?呃…啥?
我没见每天有成千上万条评论在此谴责他,可这可是他几十年的工作方式。每当他说出刻薄话,人们就跳出来指责“看吧这就是他的本性”。说实话这倒成了我们这个时代的症结。
去看看LKML邮件列表就知道了。他确实通常如此。
托瓦兹的暴言总能引发如此关注,这让我感到怪异。就像人们看选秀节目时,听主持人贬低歌手反而觉得乐在其中。
我认为是因为多数软件开发者都曾与混蛋共事过。评论某个出名的混蛋,是宣泄情绪的自然途径。
构建内核很难,对不参与的事发表意见很容易
官方默认检查器的最新Rust稳定版提示:
>>首先,这段显式代码很可能有误,事实上Linus补充道“可能需要添加强制类型转换”。
当你编写显式代码时,你清楚a和b在上下文中的类型。
你不会编写泛型代码,而是为该上下文编写显式的特定代码。某些上下文需要强制类型转换,某些则不需要(因此使用“可能”一词)。
文章其余部分读起来像是一篇讽刺作品。作者本可采用清晰明确的一行代码,却最终写出多行函数,却仍未能解决林纳斯指出的核心问题(即阅读代码时遇到该函数,你无法分辨哪个参数是高位哪个是低位)。
> 阅读代码遇到该函数时,你无法分辨哪个参数是高位哪个是低位
参数定义里写得清清楚楚。你的IDE难道不会提示正确顺序?否则你遇到每个函数都要吐槽?
这根本不是函数,是宏。只是个没有定义参数类型的文本替换规则。
阅读代码差异、搜索结果或PR请求时,并非总能通过IDE查看函数定义。即便能查看,仍需耗费时间点击跳转并阅读定义。参数名称虽有帮助,但并非文档说明,理解仍需时间。
替代方案是使用语言内置的通用运算符。
>>否则你得在遇到每个函数时都添加类似注释。
这是一种权衡。要打包的逻辑越多,其价值就越大。这里我们有一行代码包含两个内置运算符。
原始消息(因lkml链接无法加载):https://lore.kernel.org/lkml/CAHk-=wjLCqUUWd8DzG+xsOn-yVL0Q=…
你不需要在上面添加noexcept,编译器会自动识别,避免潜在的noexcept开销…除此之外——我同意。
我认为这个反驳偏离了重点,前两段已阐明核心问题:
前半段说得有道理,但最后那段C++代码简直是垃圾。向C语言致敬。
你完全忽略了Linus评论的前半部分,还写了篇“垃圾文章”来抨击它。