Linux 2.6.39-rc3的一个插曲
2011年4月12日,Linux 2.6.39-rc3发布了,Linus Torvalds写了一个发布邮件,其中包含了一个长长的为这个版本做过贡献的人员名单,这个名单中有很多看上去应该是中国人的名字,我挺为他们感到骄傲的(不知道你是否还记得以前本站的”Linux是由谁写的“)。
不过,没过一会,发现了一个bug,经过大家的调查(2.6.38版没有发现这个问题),很快,找到了原因,是因为一个内存地址的问题,一个叫Yinghai Lu的人(看其名字应该是中国人,其邮件是@kernel.org)找到了原因—— radeon card使用了一个不正确的内存地址[0xa0000000 – 0xc000000]。Joerg Roedel跟贴说,这个地址超出了4GB的内存,然后他和Alex Deucher聊了一会,觉得不应该是这个问题,因为这个地址应该是GPU的,而不是系统内存的。
好像,Yinghai Lu没有理会他们说的不应该是这个问题,给出了个fix:
diff --git a/arch/x86/kernel/aperture_64.c b/arch/x86/kernel/aperture_64.c index 86d1ad4..3b6a9d5 100644 --- a/arch/x86/kernel/aperture_64.c +++ b/arch/x86/kernel/aperture_64.c @@ -83,7 +83,7 @@ static u32 __init allocate_aperture(void) * so don't use 512M below as gart iommu, leave the space for kernel * code for safe */ - addr = memblock_find_in_range(0, 1ULL<<32, aper_size, 512ULL<<20); + addr = memblock_find_in_range(0, 1ULL<<32, aper_size, 512ULL<<21); if (addr == MEMBLOCK_ERROR || addr + aper_size > 0xffffffff) { printk(KERN_ERR "Cannot allocate aperture memory hole (%lx,%uK)\n",
看到这个fix,Linus Torvalds不高兴了,他回贴问道:
- 为什么全都是Magic Numbers?
- 为什么0x80000000就那么特殊?
- 为什么我们这样改就行?
还说了这样一句话——
This kind of “I broke things, so now I will jiggle things randomly until they unbreak” is not acceptable. 这种“我把事搞砸了,就随意地调整直到事情又工作”的方式是不可接受的。
还说,这里即没有说明为什么我们fix在了正确的地方(也没有解释那些Magic Number是什么),也没有回滚那个有问题的patch。还说——
Don’t just make random changes. There really are only two acceptable models of development: “think and analyze” or “years and years of testing on thousands of machines”. Those two really do work.
不要乱改。那里只有两个可行的开发模式:“思考和分析” 或是 “数年数年地不断地在几千台机器上测试”。这两个方式才是真正可行的。
当然,Yinghai Lu对其做了解释,说我们的确调查过了,老的代码用的内存地址是0x80000000,新的则是用0xa0000000,而0xa0000000不工作。这又引发了 Linus Torvalds 的不满的回贴。Linus说——
Yinghai, we have had this discussion before, and dammit, you need to understand the difference between “understanding the problem” and “put in random values until it works on one machine”.
Yinghai,我们以前谈过这个事,该死的,你真的需要明白“理解一个错误”和“设一个随意的值直到其正常工作”的区别。
There was absolutely _zero_ analysis done. You do not actually understand WHY the numbers matter. You just look at two random numbers, and one works, the other does not. That’s not “analyzing”. That’s just “random number games”.
这里就根本没有分析。你没有直正的明白为什么这些数字能行。你只看了两个随机的数,一个能行,另一个不行。这不是“分析”,这叫“随机数游戏”。
If you cannot see and understand the difference between an actual analytical solution where you _understand_ what the code is doing and why, and “random numbers that happen to work on one machine”, I don’t know what to tell you.
一个解决方案真正经过分析了那段代码干什么的为什么的,另一个是“随机数字可以让其在一台机器上运转”,如果你不能看到和理解他们之间的不同,那我不知道要和你说什么了。
然后,Linus Torvalds进行了谆谆教导——(相当的受用啊)
Let me repeat my point one more time.
让我再一次重复一下我的观点
You have TWO choices. Not more, not less:
你有两个选择,不多也不少:
– choice #1: go back to the old allocation model. It’s tested. It doesn’t regress. Admittedly we may not know exactly _why_ it works, and it might not work on all machines, but it doesn’t cause regressions (ie the machines it doesn’t work on it _never_ worked on).
– 选择一:回滚到老的分配模式。那是测试过的。它过了回归测试。诚然,我们也许不知道为什么那样能行,并且,即使是那样也不一定能在所有的机器上工作,但是其没有让回归测试有问题(这个代码永不可能在不能运行的系统上运行)
And this doesn’t mean “old value for that _one_ machine”. It means “old value for _every_ machine”. So it means we revert the whole bottom-down thing entirely. Not just “change one random number so that the totally different allocation pattern happens to give the same result on one particular machine”.
这并不代表“老的值只能在一台机器上工作”。这代表“老的值可以工作在每一台机器上”。所以,我们需要回滚整个代码改动。而不只是“为了一个特别的机器去修改一个和以前完全不一样的随机数”。
– Choice #2: understand exactly _what_ goes wrong, and fix it analytically (ie by _understanding_ the problem, and being able to solve it exactly, and in a way you can argue about without having to resort to “magic happens”).
– 选择二:真正搞清楚为什么会错,并且有分析地修改他(理解问题才能真正解决之,并且,只有没有“魔法发生”的时候你才可以来争论)
Now, the whole analytic approach (aka “computer sciency” approach), where you can actually think about the problem without having any pesky “reality” impact the solution is obviously the one we tend to prefer. Sadly, it’s seldom the one we can use in reality when it comes to things like resource allocation, since we end up starting off with often buggy approximations of what the actual hardware is all about (ie broken firmware tables).
现在,整个分析方法(亦称作“计算机科学”的方法)应该是你可以在没有在外界干扰下真正思考这个问题而得到的解决方案,这很明显是我们推崇的。只有在极罕见地情况下我们可以在有外界干扰下分析这种资源分配的事,因为我们只有了解倒底是什么样的硬件,我们才能最终远离bug(如:错误的固件表)
So I’d love to know exactly why one random number works, and why another one doesn’t. But as long as we do _not_ know the “Why” of it, we will have to revert.
所以,我希望你能知道为什么一个随机数能行,而另一个不行。只要我们不知道,那么我们就不得和回滚整个改动。
It really is that simple. It’s _always_ that simple.
这真的是很简单,而且这一直是那么简单。
So the numbers shouldn’t be “magic”, they should have real explanations. And in the absense of real explanation, the model that works is “this is what we’ve always done”. Including, very much, the whole allocation order. Not just one random number on one random machine.
所以,那些数不应该是“magic”的,他们应该有真正的说明。在有真正的说明的情况下,我们的开发模式才会工作。其包括了整个分配顺序。不只是那个在任意机器上的随机数。
Linus
后面的事不用说了。我没有想到Linux 内核组会有像Yinghai这样工作的方式,毕竟这是一个黑客级的开发团队。我个人对这个乱写代码的人执零容忍的态度,不管你干过什么,不管你哪里毕业的,不管你简历怎么样,不求甚解随意写代码的人我无法接受。我不知道Yinghai Lu会怎么样想,他/她会像我在“程序员那些悲催的事儿”中谈我经历那样知耻而后勇吗?能得到Linus的教导真是一件很不错的事。虽然,Linus教导的这些东西,都应该是程序员最最最基本的技能。fix bug一定要fix在root cause上啊,了解一个问题,不但要知其然,还要知其所以然啊,这都是老生长谈了。本站有很多提高程序员能力的文章,比如,这篇,这篇,还有这篇。
各位朋友,我真心希望你能从这个小插曲中明白点什么。
—– 更新2011/04/27—–
从本贴的回复中可以看到有朋友说如果时间紧,没有办法只能在不求甚解的地去fix bug,因为老板催。我认为这是老板的“急功近利”的问题。我想和大家说一下,你得想清楚你属于下面那种人:
-
你的老板给你压力,让你不得不乱fix,
-
你认同只要时间紧bug是可以乱fix的。
如果你属于1),那我觉得还情由可原,这是管理问题。但这不能成为你对乱fix bug的理由。一般这种问题怎么解决:首先,给一个hot fix去救火,然后,有时间去调查root cause,最后经过分析和测试,给出一个final 的 offical fix。这就是应急的做法,根本不存在什么可以乱fix bug的做法。
如果你属于2),那么我只能“过激”地说你没有成为程序员的资质!
另外,快速地fix bug,并不等于,不求甚解的fix bug。大家不要把这两件事等同。
(请勿用于商业用途,转载时请注明作者和出处)
(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)
《Linux 2.6.39-rc3的一个插曲》的相关评论
原文中的regress和regression应该翻译为“退化”(A return to a former or less developed state)
我很想明白的一个问题是,为什么感觉freebsd的内核代码的实现明显比linux实现要简洁很多。linux应该也有能力实现简洁的代码吧。比较菜。所以,很疑惑这个问题。不指驱动方面的代码,而指核心的代码。比如 freebsd 的调度的部分,sched_ule.c 里有2747行,而linux的调度部分,sched.c 中有 9482 行。不知道差异为什么有这么大。不明白linux为什么多出了这么多的代码。另外,感觉freebsd的源码的组织十分的整齐,调度的这部分的实现就很能体现,为什么linux不做类似的改进呢。
着实不明白这个问题,可能是自己太菜了吧。。。
记得在哪看看过,早年oracle就是bug多多,牛X哄哄。
为的是强占市场好像。现在就不说了。
还有linus大牛都发脾气了,是吧,又不是机器,谁没点想法。
大家多讨论,没什么歪不歪..
本人支持root cause 派的··嘿嘿
@胖岁
回归
计算机专业术语 不是学医的..
wo是不是过激了··
ssssshit~~我还想知道这个bug到底为什么这么解决的呢。扯淡么不是
@monk
bug问题码农自己关心就好了,程序员都”更”关心是否代码写得符合“规范”
@不正直的人
X86 启动的时候,牵涉到的东西很多,各个主板厂家在写 BIOS 的时候,基本都是只测 XP 不测 Linux, 显卡公司 ATI/NV 处于保密考虑很多硬件规范不统一。
没有源代码的混乱 BIOS + 不同厂商的 GPU firmware + 隐藏的信息 在这种时候,问题原因的查找是相当困难的。
Linux 的 ACPI 曾经不断的出问题,一个内核好了,下个内核又坏了,主要也是这个原因。
@陈皓
“而我说的是则是做事业的层次” 不是吧?
“全面质量管理” –我以前听说过, 不过好像许多人没听说过, 建议博主那天给我们翻译一段,
零容忍的态度======那就要求做的鲁棒些
如果为了work,先随便fix一下,然后再fix review回顾下原因呢?
陈皓认为“我没有想到Linux 内核组会有像Yinghai这样工作的方式,毕竟这是一个黑客级的开发团队。” 我认为言之过甚。
Yinghai 的 email 中说:” can you try following change ? it will push gart to 0×80000000″ (你能不能试试这个?)
而陈皓说“给出了个fix”,他认为Yinghai提出的是会加入到kernel code中的patch,我想这不一定是Yinghai的本意。
@zhanxw,其一、Linus在对Yinghai说了这不是第一次发生了,其二、这里谈的是工作方式,不是表达,更不是咬文嚼字。其三、再客气,再礼貌,那怕你加上免责的话,你都无法掩盖你的工作方式上的不是。
@陈皓 我赞同你和Linus提倡的工作方式。但在这个问题上,我个人觉得找bug的第一步总应该发现具体哪一行code产生的错误吧?(就像email中说的bisect方法)如果连第一行有问题的code都没找到,又怎么能Think and analyze更大的问题呢? 我的理解是Yinghai Lu希望confirm这行代码是错误的原因,而不是说用另一个magic number来解决问题。这样来说吧,假如你负责的系统发现一个bug,你发现有一行代码可能是错误原因,你试了一下,不确定是不是管用,所以你问另一个报告这个bug的同事,让他帮忙确认一下你的猜测对不对。你觉得这个工作方式是否恰当?或者怎么做更好?
我同意你要找到哪一行代码出了问题,但是找到哪一行的代码出的问题,并不一定就是真正的原因。这样的例子太多了。另外,之前别人就已经告诉Yinghai那个内存地址不应该是问题。所以,你要做的是——证明自己是对的,并不是说老的代码可以行,新的代码不行,这个证据几乎没有意义。Linus的回复中说的很清楚,一个magic的内存地址以前可行并不代表那是对的,只代表它没有出过问题。要证明一个事有两个方法——1)分析它了解后面的东西,2)数年数年的大量的测试。试一下当然可以,但最害怕的是你在一台机器上试过了,就以为这对所有的机器都是正确的了。我们很多时候没有明白“分析问题”和“试一试猜一猜”的区别。这就是Linus一直都在说的。
我删了两个用我名字发出来的贴,我想对那位朋友说,这个blog旨在分享平时看到想到的东西,可能质量差一点,观点强一点,我也会和大家争论,其中有一些是我需要去思考和改进的地方,欢迎大家给我批评,就事论事,可能不对你的口味,也可能你很反感,但不要人身攻击和一概而论。
如果你喜欢,我们可以切磋讨论,如果不喜欢也不必骂人。这只是一些博文和观点罢了,不同意也犯不着为之生气。你说是不是?
@胖岁
翻译成回退如何?
公司开发中(如我们)故障解决速度是一个很重要的考核标准,遇到故障用magicnumber做一下特殊处理是常有的事
,代码可能和是前任写的,架构你也改不动,只要把bug fix了,过掉测试那一关就OK了。
后果就是对代码没有信心,感觉在靠巧合编程,羡慕Linus那样的工作态度和做事方式。
“我个人对这个乱写代码的人执零容忍的态度,不管你干过什么,不管你哪里毕业的,不管你简历怎么样,不求甚解随意写代码的人我无法接受。”
非常同意这个观点,但是实际操作起来太难:)
虽然我工作还不到一年,不得不说,这种莫名其妙的问题我遇到过好几次……其中一个我还是清清楚楚的记得,将代码的DEBUG输出打开,就解决了一个很奇怪很难以解释的问题,它确实能解决问题,但是我花了很长时间都没有找到根本原因,这个问题,我到现在还清楚的记得,而且还留在BUG跟踪系统上,我也不得不承认我目前没有打算去解决这个问题。
不过这不代表我认同Yinghai的代码,我自认为我对自己代码还是有一定要求的,不会随意写出如此不负责任的代码。偶尔也会用这种临时解决方案,不过会加上详细的注释说明。但是有些时候,真的很难很难做到零容忍。
我记得我N年前fix一个bug,也是没有求甚解,结果提交fix的diff的时候,被reviewer问了一堆问题,而且是不停地问,等我回答完了,我发现我明白了整个问题,也得到了一个完全不同的fix。我对这个记忆犹新。
今天,在我现在公司里,有一个美国的程序员提交了一个fix,被多个reviewer问了近两个月的问题,不断地提交新的改动,不断地被问问题,不断地解释怎么测试的,怎么想的等等,这个问题今天终于被打上了“Ship It!”的标记,还要详细描述应该怎么测试。
这就是我们和别人的差距!
@砖家
补充一点,没有找到问题根本原因之前,我是绝对不会随意写代码。实在无法解决,我会找同事、经理帮助,不过这种找不到原因的问题,的确是很少很少。
@陈皓
我明白了,永远要找到问题的根本原因而不是回避或者碰巧、瞎猫撞死耗子。我的工作中经常去fix各种疑难问题,深有体会,花了很久很长的时间发现一个小问题的根本原因,这个过程让我学到很多东西。我知道该怎么做了,谢谢皓哥。
坚持,不言放弃,专研,这才是真正的程序员的能力。畏难,退缩,应付,是我们的天敌。
有时候时间紧,或者偷懒会不求甚解的地去fix bug(主要是懒)
以后得提高自我约束的能力了
@陈皓
我觉得Linus的做法是正常的. 别说内核,就算是一般系统的核心业务逻辑代码,修Bug的时候,也必须是慎重又慎重的.
不求甚解地提交一个补丁,往往会引来潜在的其他Bug
这种做法, 会给以后的维护带来更大的麻烦.代码腐烂啊
[quote]
Now, the whole analytic approach (aka “computer sciency” approach), where you can actually think about the problem without having any pesky “reality” impact the solution is obviously the one we tend to prefer. Sadly, it’s seldom the one we can use in reality when it comes to things like resource allocation, since we end up starting off with often buggy approximations of what the actual hardware is all about (ie broken firmware tables).
现在,整个分析方法(亦称作“计算机科学”的方法)应该是你可以在没有在外界干扰下真正思考这个问题而得到的解决方案,这很明显是我们推崇的。只有在极罕见地情况下我们可以在有外界干扰下分析这种资源分配的事,因为我们只有了解倒底是什么样的硬件,我们才能最终远离bug(如:错误的固件表)
[/quote]
这一段里后面一句话翻译不是很准确。我觉得这样会更好些:
遗憾的是,在现实中碰到资源分配这样的问题我们很少能够使用这种分析方法。因为我们不能够完全了解实际硬件的情况,经常会做一些有问题的假设,这就导致了程序会出问题(比如错误的固件表)。
又看了一遍,有些词还是不准确,可能这样更好:
遗憾的是,在现实中碰到资源分配这样的问题我们很少能够使用这种分析方法。因为我们不能够完全了解实际硬件的情况,经常从一些有问题的近似(比如错误的固件表)开始分析,这就导致了最终程序会出问题。
这么内核的东西, 还这么乱改个魔法数字, 确实该骂.
这样的程序员, 要我是linus, 就可以让他out了, 少他不少…
幸运的是, linus把关严格…
恩,学习了。。。
@胖岁 胡扯,标准翻译就是回归
我认为一个很大的程序(那怕比Linux还小很多)没有任何人有能力搞清楚里面的所有一切. 我们能解决复杂问题的方法是把这个问题由大到小的分解成许多许多小的问题. 然后解决这个小问题. 如果Yinghai Lu这个fix可以正确的通过测试, 那它就应该是正确的. 即使人无法理解它为什么正确.
自己的fix不能完全理解不代表就是乱fix ,lunis自己说, 宁可回滚到旧代码中,哪怕我们依然不能理解为什么,但是旧代码是测试过的. 那么为什么不能测试新代码, 看他是否能正确运行呢.旧代码里这个参数也一样是个魔数, 它为何就比fix里面的魔数更好?
“I broke things, so now I will jiggle things randomly until they unbreak”,这种不可接受的方法似乎是从古至今,让科学发展的唯一方法. 难道爱迪生在发明电灯之前失败的1万多次不是在随意的调整直到它可以工作吗?
当相对论刚刚提出的时候,全世界除了爱因斯坦没有任何人理解它, 但是科学界很快接受了,因为这个理论比经典理论更符合现实中观察到的情况. 事实永远比理论更正确, 这才是真正的科学精神.
我发现很多人都只是针对linus的话来评论,没有注意场景。
linus的话是正理,不但计算机科学是这样的,很多事情都应该是这样的。
但现在情况是,之前的代码也是magic number的,现在yinghai发现现在代码有问题,改成一个magic number就解决了。yinghai也是一个linux内核bugfix的常客了。那是不是该测试他提供的fix?当前有bug,难道为了严格的知其所以然而后改,你们就能放下现在的bug不顾?那你让使用这个版本内核的人情何以堪?这样岂不是更恶劣?到底是先改还是先留着,等到知其所以然后再改?这也是一种权衡。linux早期因为没有硬件厂商提供硬件参数,应该使用了不少hack的办法的,早期可能烧硬件的,yinghai这样的方案估计也没少使用。
@William.S
说的太对了,随机的修复引起更多的错误
@的份额
你得证明看似解决了的问题所在的子问题域和其它任何部分的交集为空,确保不会引入任何新的bug;或者退一步——证明对其它部分的影响可以被限制在某个可预期的范围之内。
数学或者物理学可以部分地做到这一点,但是程序设计在一般情况下几乎不可能。
(何况还有用C语言和自然语言表达问题本身的复杂性来添乱。)
前面两段我觉得您有点误会了。
Linus是站在自己的角度来说“宁可回滚到旧代码中,哪怕我们依然不能理解为什么”, 这个we 是指像linus本人这样对这段代码不熟悉,但是同样需要来面对这个问题的we, 存在一个me或者一些we是理解原始代码的why的,因为上一个写代码或者fix代码的人肯定是有相关详细说明的。
Linus的话是针对“提交fix的人只提交了fix而没有任何关于这个fix的相关说明”,而不是“提交fix的行为”。
这个fix肯定是要测试的,但是面对这种没有说明,没有数据,没有理由的fix,要是你是这个测试人,你要怎么去测试?
我觉得您的回复最后两段也犯了被Linux耳提面命的同样错误。这些理论可以用来“说明”问题但是不能用来来“论证”问题。
“ 事实永远比理论更正确” Linus就是在要求这个!!!
我发现您没有注意场景。
“之前的代码也是magic number的” 之前的代码并不是magic number!! 它已经在所有测试平台上通过了。 而改的这个并没有说明是否在所有测试平台上通过了。 而且 问题不在这儿—— 见我上面去的回复。
@bermuda
呵呵,赤裸裸的羡慕嫉妒恨
一个系统到底应该追求什么?
另外,菜鸟有点不明白的是:内存的设定不应该是先自检硬件内存的大小,再虚拟分配给操作系统和用户吗?这样为什么还会出现文中的随机数呢?
一个系统到底应该追求什么?
另外,菜鸟有点不明白的是:内存的设定不应该是先自检硬件内存的大小,再虚拟分配给操作系统和用户吗?这样为什么还会出现文中的随机数呢?
一个系统到底应该追求什么?
另外,菜鸟有点不明白的是:内存的设定不应该是先自检硬件内存的大小,再虚拟分配给操作系统和用户吗?这样为什么还会出现文中的随机数呢?
东西方文化差异吗。
提出解决方案,那么就应该测试为什么这个值能行。
然而。现在谁都不知道为什么这个值能性。
但是批评的观点是,你只是提供了值,而不是解决了为什么。
诚然,没有解决为什么。但是他却是一个方向。
牛顿未发现万有引力之前,人类还是在利用万有引力。
随意的批评是自己的工作方式问题。
应该解决的问题很简单。提出有效值,解决为什么。
但是我只看到批评。未看到后续有没有围绕为什么展开。
很遗憾。
翻译作者说对知其然,不知其所以然提出零容忍。
那么我想知道你在生活中到底有多少是你自己知道了的呢。
人类科学是一门经验科学。
我们应该做的不是批评。而是研究。
也许是人的思维问题吧。
Yinghai Lu 做的没有错. 为什么要指责 Yinghai Lu 呢, 大家有没有认真阅读.
在这个事件上 , 只是 发现新版本出现了一个 BUG, 对与这个BUG的查找过程中,
Yinghai Lu 发现了问题的根源, 问题出在一个内存地址上.
其他维护者认为, 这个地址不是产生问题的根源, 理由是这个地址应该是GPU的.
Yinghai Lu 没有理会其他维护者的言论, 发了一份”FIX” ,
来证明 这个地址的确是产生问题的根源, FIX 中的内容或许并不能真正的修复问题.
但是这个地址的确让原本产生的BUG消失了.
Yinghai Lu 发的patch 目的并非是为了真正的FIX, 只是找到问题.
然而, Linus Torvalds却认为 Yinghai Lu 发的是一份正式的补丁.
并且 Yinghai Lu 并没有注释这份patch 的目的, 所以在使用目的上产生了歧义.
Linus Torvalds 或许并没有了解 patch 产生的始末. 产生很大的误会.
Yinghai Lu 提供这份patch,只是说明他找到问题的根源,而其他维护者并不认同,认为地址没有问题.
Linus Torvalds 在这方面 做的不好. 很明显. Yinghai Lu 的patch 是用来测试bug的,或许不是用来修复bug的.
而Linus Torvalds的态度明显像个教父. 缺乏耐性与沟通. 他讲的大道理 太俗气.
这看上去的确就是一个old value。
当然我也理解,作为一个长期维护的产品不应该这样改,而是要看看问题到底出在哪。
看上去是别处的什么代码做了一些假设而没有被注意到。