2009年8月5日星期三
每句话可以品半辈子
1,绅士无非就是耐心的狼。
2,老鼠嘲笑猫的时候,身旁必有一个洞。
3,女人吻男人是一种幸福,男人吻女人是一种口福。
4,男人:二十岁的时候,是半成品;三十岁的时候,是成品;四十岁的时候,是精品;五十岁的时候,是极品;六十岁的时候,是样品;七十岁的时候,是纪念品。
5,站在山顶和站在山脚下的两人,虽然地位不同,但在对方眼里,同样的渺小。
6,结论就是你懒得再想下去的地方。路的尽头,仍然是路,只要你愿意走。
7,使我们不快乐的,都是一些芝麻小事,我们可以躲闪一头大象,却躲不开一只苍蝇。
8,最好的与最坏的创造了历史,平庸之辈则繁衍了种族。
9,在这世上惟一件事比别人议论更糟,那就是无人议论你。
10,一颗将爆的炸弹比一颗已爆的炸弹恐怖得多。
11,成功便是站起比倒下多一次。
12,失败并不意味你浪费了时间和生命。失败表明你有理由重新开始。
13,人生中有时不去冒险比冒险更危险。
14,所有的人都站在一边并不一定是好事,譬如他们都站在船的一边
15,许多人爬到了梯子的顶端,却发现梯子架错了墙。
16,使学生丧失信心是教师最大的犯罪。
17,十之八九,争论的结果是双方比以前更相信自己绝对正确。
18,偶然的成功比失败更可怕。
19,才华其实就是把与人相同的聪明用到与众不同的地方。
20,状态是干出来的,而不是等出来的。
21,道歉是为将来再次冒犯打下伏笔。
22,所谓百依百顺,就是为了某种不可告人的目的在未完成前,所表现的不同寻常的耐心。
23,所谓儿童不宜,其实就是大人们令人感动地把犯错误的危险留给了自己。
24,你想以40km/h的速度开车到80岁,还是以80km/h的速度开车到40岁?
25,在敌人面前,谁先镇定下来,谁就离胜利不远了。
26,所有的人都是平凡的,有些人因知道这一点而真正成了平凡的人。
27,乞丐就是一种向你的良心征税的人。
28,所谓大难不死,就是有了灾祸之后,得到的不是同情,而是莫名其妙的祝贺。
29,处在社交圈中是一种烦恼,而超脱出来简直是一场悲剧。
30,厌倦,就是一个人吃完盘子里的食物后对盘子的感情。
31,参加追悼会的程序常常是,先受一次深刻的人生教育,然后大家一起抽烟喝酒打牌。
32,知识是一种使求知者吃得越多越觉得饿的粮食。
33,羞耻心就像人的内衣,必要时脱掉了没什么,关键是为谁脱掉。
34,爱情是会沉底的,在平淡的日子里,最重要的是经常轻轻晃一晃盛装爱情的"水杯"。
35,战争:用舌头解不开就用牙齿咬吧!
36,憎恨别人就像为了逮住一只耗子而不惜烧毁你自己的房子。但耗子不一定逮到了。
37,每个人的一生都有许多梦想,但如果其中一个不断搅扰着你,剩下的就仅仅是行动了。
38,世上只有想不通的人,没有走不通的路。
39,幸运之神的降临,往往只是因为你多看了一眼,多想了一下,多走了一步。
40,失败发生在彻底的放弃之后。
41,所谓敌人,不过是那些迫使我们自己变得强大的人。
42,胆怯的人在危险前被吓住了;懦弱的人在危险中被吓住了;勇敢的人在危险过后被吓住了。
43,反腐败的风险常常大于搞腐败的风险----人大代表的不平。
44,使人疲惫的不是远方的高山,而是鞋里的一粒沙子。
45,家庭的幸福需夫妻共同努力,而破坏它,一人就够了。
46,最了解你的人有时不是你的朋友,而是你的敌人。
47,你若不想做,会找一个或无数个借口;你若想做,会想一个或无数个办法。
48,看一个人的心术,看他的眼神;看一个人的身价,看他的对手;看一个人的底牌,看他的朋友。
49,魅力女人,就是有充分的意志力去抵挡男人的进攻,也有足够多的魅力阻挡男人的撤退。
50,山盟海誓是一种经常让高山和海洋领受尴尬的重量级承诺。
51,好女人是一所学校,好男人毕业了可留校任教。
52,你不理财,财不理你。
53,不要同一个傻瓜争辩,否则别人会搞不清到底谁是傻瓜。
54,加班加点,废寝忘食有时很可能是不具备效率和工作能力的表现。
55,做父母是个专业性很强的职业,可大部分的父母未经任何培训就上岗了。
56,凡向鳄鱼池内投掷物品者,必须下池自己捡回。--――肯尼亚天然动物园告示
57,当你想丢点什么的时候请想想,千万别丢脸。----――成都垃圾箱,果皮箱上语
58,凡是上了年纪的人,大多是反对的太多,商议的太久,行动的太迟,后悔的太早。
59,做生意的过程就是一个不断怂恿别人放松警惕,而自己保持高度警惕的过程。
60,打工者做工,小老板做事,中老板做市,大老板做势----你是哪一种?
61,成功是一种观念,致富是一种义务,快乐是一种权力。
62,聪明人能洞察事物未来的发展趋势。他们在发洪水之前养鸭,而不是养鸡。
63,两种人无药可救:一是不服从命令的人,二是惟命是从的人。
64,打算开创新事业时,十人中有一两人赞成时就可开始,以免太迟。
65,买保险----用不上时痛苦,用上时更痛苦。
66,给猴一棵树,给虎一座山----用人之道
67,世上有三种人:一是良心被狗吃了的人,二是良心没被狗吃的人,三是良心连狗都不吃的人。
68,你可以先知先觉地领导产业,后知后觉地苦苦追赶,或不知不觉地被淘汰。
2,老鼠嘲笑猫的时候,身旁必有一个洞。
3,女人吻男人是一种幸福,男人吻女人是一种口福。
4,男人:二十岁的时候,是半成品;三十岁的时候,是成品;四十岁的时候,是精品;五十岁的时候,是极品;六十岁的时候,是样品;七十岁的时候,是纪念品。
5,站在山顶和站在山脚下的两人,虽然地位不同,但在对方眼里,同样的渺小。
6,结论就是你懒得再想下去的地方。路的尽头,仍然是路,只要你愿意走。
7,使我们不快乐的,都是一些芝麻小事,我们可以躲闪一头大象,却躲不开一只苍蝇。
8,最好的与最坏的创造了历史,平庸之辈则繁衍了种族。
9,在这世上惟一件事比别人议论更糟,那就是无人议论你。
10,一颗将爆的炸弹比一颗已爆的炸弹恐怖得多。
11,成功便是站起比倒下多一次。
12,失败并不意味你浪费了时间和生命。失败表明你有理由重新开始。
13,人生中有时不去冒险比冒险更危险。
14,所有的人都站在一边并不一定是好事,譬如他们都站在船的一边
15,许多人爬到了梯子的顶端,却发现梯子架错了墙。
16,使学生丧失信心是教师最大的犯罪。
17,十之八九,争论的结果是双方比以前更相信自己绝对正确。
18,偶然的成功比失败更可怕。
19,才华其实就是把与人相同的聪明用到与众不同的地方。
20,状态是干出来的,而不是等出来的。
21,道歉是为将来再次冒犯打下伏笔。
22,所谓百依百顺,就是为了某种不可告人的目的在未完成前,所表现的不同寻常的耐心。
23,所谓儿童不宜,其实就是大人们令人感动地把犯错误的危险留给了自己。
24,你想以40km/h的速度开车到80岁,还是以80km/h的速度开车到40岁?
25,在敌人面前,谁先镇定下来,谁就离胜利不远了。
26,所有的人都是平凡的,有些人因知道这一点而真正成了平凡的人。
27,乞丐就是一种向你的良心征税的人。
28,所谓大难不死,就是有了灾祸之后,得到的不是同情,而是莫名其妙的祝贺。
29,处在社交圈中是一种烦恼,而超脱出来简直是一场悲剧。
30,厌倦,就是一个人吃完盘子里的食物后对盘子的感情。
31,参加追悼会的程序常常是,先受一次深刻的人生教育,然后大家一起抽烟喝酒打牌。
32,知识是一种使求知者吃得越多越觉得饿的粮食。
33,羞耻心就像人的内衣,必要时脱掉了没什么,关键是为谁脱掉。
34,爱情是会沉底的,在平淡的日子里,最重要的是经常轻轻晃一晃盛装爱情的"水杯"。
35,战争:用舌头解不开就用牙齿咬吧!
36,憎恨别人就像为了逮住一只耗子而不惜烧毁你自己的房子。但耗子不一定逮到了。
37,每个人的一生都有许多梦想,但如果其中一个不断搅扰着你,剩下的就仅仅是行动了。
38,世上只有想不通的人,没有走不通的路。
39,幸运之神的降临,往往只是因为你多看了一眼,多想了一下,多走了一步。
40,失败发生在彻底的放弃之后。
41,所谓敌人,不过是那些迫使我们自己变得强大的人。
42,胆怯的人在危险前被吓住了;懦弱的人在危险中被吓住了;勇敢的人在危险过后被吓住了。
43,反腐败的风险常常大于搞腐败的风险----人大代表的不平。
44,使人疲惫的不是远方的高山,而是鞋里的一粒沙子。
45,家庭的幸福需夫妻共同努力,而破坏它,一人就够了。
46,最了解你的人有时不是你的朋友,而是你的敌人。
47,你若不想做,会找一个或无数个借口;你若想做,会想一个或无数个办法。
48,看一个人的心术,看他的眼神;看一个人的身价,看他的对手;看一个人的底牌,看他的朋友。
49,魅力女人,就是有充分的意志力去抵挡男人的进攻,也有足够多的魅力阻挡男人的撤退。
50,山盟海誓是一种经常让高山和海洋领受尴尬的重量级承诺。
51,好女人是一所学校,好男人毕业了可留校任教。
52,你不理财,财不理你。
53,不要同一个傻瓜争辩,否则别人会搞不清到底谁是傻瓜。
54,加班加点,废寝忘食有时很可能是不具备效率和工作能力的表现。
55,做父母是个专业性很强的职业,可大部分的父母未经任何培训就上岗了。
56,凡向鳄鱼池内投掷物品者,必须下池自己捡回。--――肯尼亚天然动物园告示
57,当你想丢点什么的时候请想想,千万别丢脸。----――成都垃圾箱,果皮箱上语
58,凡是上了年纪的人,大多是反对的太多,商议的太久,行动的太迟,后悔的太早。
59,做生意的过程就是一个不断怂恿别人放松警惕,而自己保持高度警惕的过程。
60,打工者做工,小老板做事,中老板做市,大老板做势----你是哪一种?
61,成功是一种观念,致富是一种义务,快乐是一种权力。
62,聪明人能洞察事物未来的发展趋势。他们在发洪水之前养鸭,而不是养鸡。
63,两种人无药可救:一是不服从命令的人,二是惟命是从的人。
64,打算开创新事业时,十人中有一两人赞成时就可开始,以免太迟。
65,买保险----用不上时痛苦,用上时更痛苦。
66,给猴一棵树,给虎一座山----用人之道
67,世上有三种人:一是良心被狗吃了的人,二是良心没被狗吃的人,三是良心连狗都不吃的人。
68,你可以先知先觉地领导产业,后知后觉地苦苦追赶,或不知不觉地被淘汰。
Daily Build and Smoke Test
Steve McConnell,代码完成(code complete)的作者曾于1996年在IEEE
Software杂志上发表了下面这篇关于每日编译和冒烟测试的文章,已经是别人当时的最佳实践(Best
Practices)了。
我们现在好几个项目组都在做Daily
Build,就这一点上,其实已经落后别人十年了,但是我们希望能够坚持把Daily
Build做好,为研发的正常进行提供保证。
原文链接是http://www.stevemcconnell.com/bp04.htm
下面贴出原文,以及我自己的翻译,不当之处,请斧正。
Daily Build and Smoke Test
If you want to create a simple computer program consisting of only one
file, you merely need to compile and link that one file. On a typical team
project involving dozens, hundreds, or even thousands of files, however,
the process of creating an executable program becomes more complicated and
time consuming. You must "build" the program from its various components.
A common practice at Microsoft and some other shrink-wrap software
companies is the "daily build and smoke test" process. Every file is
compiled, linked, and combined into an executable program every day, and
the program is then put through a "smoke test," a relatively simple check
to see whether the product "smokes" when it runs.
BENEFITS. This simple process produces several significant benefits.
It minimizes integration risk. One of the greatest risks that a team
project faces is that, when the different team members combine or
"integrate" the code they have been working on separately, the resulting
composite code does not work well. Depending on how late in the project
the incompatibility is discovered, debugging might take longer than it
would have if integration had occurred earlier, program interfaces might
have to be changed, or major parts of the system might have to be
redesigned and reimplemented. In extreme cases, integration errors have
caused projects to be cancelled. The daily build and smoke test process
keeps integration errors small and manageable, and it prevents runaway
integration problems.
It reduces the risk of low quality. Related to the risk of unsuccessful or
problematic integration is the risk of low quality. By minimally
smoke-testing all the code daily, quality problems are prevented from
taking control of the project. You bring the system to a known, good
state, and then you keep it there. You simply don't allow it to
deteriorate to the point where time-consuming quality problems can occur.
It supports easier defect diagnosis. When the product is built and tested
every day, it's easy to pinpoint why the product is broken on any given
day. If the product worked on Day 17 and is broken on Day 18, something
that happened between the two builds broke the product.
It improves morale. Seeing a product work provides an incredible boost to
morale. It almost doesn't matter what the product does. Developers can be
excited just to see it display a rectangle! With daily builds, a bit more
of the product works every day, and that keeps morale high.
USING THE DAILY BUILD AND SMOKE TEST. The idea behind this process is
simply to build the product and test it every day. Here are some of the
ins and outs of this simple idea.
Build daily. The most fundamental part of the daily build is the "daily"
part. As Jim McCarthy says (Dynamics of Software Development, Microsoft
Press, 1995), treat the daily build as the heartbeat of the project. If
there's no heartbeat, the project is dead. A little less metaphorically,
Michael Cusumano and Richard W. Selby describe the daily build as the sync
pulse of a project (Microsoft Secrets, The Free Press, 1995). Different
developers' code is allowed to get a little out of sync between these
pulses, but every time there's a sync pulse, the code has to come back
into alignment. When you insist on keeping the pulses close together, you
prevent developers from getting out of sync entirely.
Some organizations build every week, rather than every day. The problem
with this is that if the build is broken one week, you might go for
several weeks before the next good build. When that happens, you lose
virtually all of the benefit of frequent builds.
Check for broken builds. For the daily-build process to work, the software
that's built has to work. If the software isn't usable, the build is
considered to be broken and fixing it becomes top priority.
Each project sets its own standard for what constitutes "breaking the
build." The standard needs to set a quality level that's strict enough to
keep showstopper defects out but lenient enough to dis-regard trivial
defects, an undue attention to which could paralyze progress.
At a minimum, a "good" build should
compile all files, libraries, and other components successfully;
link all files, libraries, and other components successfully;
not contain any showstopper bugs that prevent the program from being
launched or that make it hazardous to operate; and
pass the smoke test.
Smoke test daily. The smoke test should exercise the entire system from
end to end. It does not have to be exhaustive, but it should be capable of
exposing major problems. The smoke test should be thorough enough that if
the build passes, you can assume that it is stable enough to be tested
more thoroughly.
The daily build has little value without the smoke test. The smoke test is
the sentry that guards against deteriorating product quality and creeping
integration problems. Without it, the daily build becomes just a
time-wasting exercise in ensuring that you have a clean compile every day.
The smoke test must evolve as the system evolves. At first, the smoke test
will probably test something simple, such as whether the system can say,
"Hello, World." As the system develops, the smoke test will become more
thorough. The first test might take a matter of seconds to run; as the
system grows, the smoke test can grow to 30 minutes, an hour, or more.
Establish a build group. On most projects, tending the daily build and
keeping the smoke test up to date becomes a big enough task to be an
explicit part of someone's job. On large projects, it can become a
full-time job for more than one person. On Windows NT 3.0, for example,
there were four full-time people in the build group (Pascal Zachary,
Showstopper!, The Free Press, 1994).
Add revisions to the build only when it makes sense to do so. Individual
developers usually don't write code quickly enough to add meaningful
increments to the system on a daily basis. They should work on a chunk of
code and then integrate it when they have a collection of code in a
consistent state-usually once every few days.
Create a penalty for breaking the build. Most groups that use daily builds
create a penalty for breaking the build. Make it clear from the beginning
that keeping the build healthy is the project's top priority. A broken
build should be the exception, not the rule. Insist that developers who
have broken the build stop all other work until they've fixed it. If the
build is broken too often, it's hard to take seriously the job of not
breaking the build.
A light-hearted penalty can help to emphasize this priority. Some groups
give out lollipops to each "sucker" who breaks the build. This developer
then has to tape the sucker to his office door until he fixes the problem.
Other groups have guilty developers wear goat horns or contribute $5 to a
morale fund.
Some projects establish a penalty with more bite. Microsoft developers on
high-profile projects such as Windows NT, Windows 95, and Excel have taken
to wearing beepers in the late stages of their projects. If they break the
build, they get called in to fix it even if their defect is discovered at
3 a.m.
Build and smoke even under pressure. When schedule pressure becomes
intense, the work required to maintain the daily build can seem like
extravagant overhead. The opposite is true. Under stress, developers lose
some of their discipline. They feel pressure to take design and
implementation shortcuts that they would not take under less stressful
circumstances. They review and unit-test their own code less carefully
than usual. The code tends toward a state of entropy more quickly than it
does during less stressful times.
Against this backdrop, daily builds enforce discipline and keep
pressure-cooker projects on track. The code still tends toward a state of
entropy, but the build process brings that tendency to heel every day.
Who can benefit from this process? Some developers protest that it is
impractical to build every day because their projects are too large. But
what was perhaps the most complex software project in recent history used
daily builds successfully. By the time it was released, Microsoft Windows
NT 3.0 consisted of 5.6 million lines of code spread across 40,000 source
files. A complete build took as many as 19 hours on several machines, but
the NT development team still managed to build every day (Zachary, 1994).
Far from being a nuisance, the NT team attributed much of its success on
that huge project to their daily builds. Those of us who work on projects
of less staggering proportions will have a hard time explaining why we
aren't also reaping the benefits of this practice.
Editor: Steve McConnell, Construx Software, 11820 Northup Way #E200,
Bellevue, WA 98005.
E-mail: steve.mcconnell@construx.com - WWW:
http://www.construx.com/stevemcc/
每日构造与冒烟测试
如果你想创建一个只包含一个源程序文件的简单程序,那么你只需要编译、连接那一个文件就可以了。如果是一个团队项目组,有着许多甚至上千个源程序文件,那么要创建一个可执行程序的过程就变得更复杂、更耗时。你必须用各种各样的组件将程序逐步建立起来。
在微软或其它一些软件公司中惯例是:每日构造并做"冒烟测试"。每天都对已完成的源程序进行编译,然后连接组合成可执行的程序,并做"冒烟测试",以简单的检查该执行程序在运行时是否会"冒烟"。
带来的好处
虽然这是一个非常简单的过程,但却有非常重要的意义:
1、能最小化集成风险
项目组可能遇到的一个很大的风险是,项目组成员根据不同的系统功能各自开发不同的代码,但是当这些代码集成为一个系统的时候,也许系统完成不了预期的功能。这种风险的发生取决于项目中的这种不兼容性多久才被发现,由于程序界面已经发生了变化,或者系统的主要部分已经被重新设计和重新实现了,相应的排错工作将非常困难和耗时。极端情况下,集成的错误可能回导致项目被取消掉。每日构造和冒烟测试可以使这种集成错误变得非常小,而且便于解决,防止了很多集成问题的产生。
2、能减小产品低质量的风险
这种风险是和集成不成功、集成出错相关联的。每天对集成的代码做一些少量的冒烟测试,即可杜绝项目中那些基本的质量问题。通过这种方式,使系统达到一种周知的良好状态,维护这样的系统可以防止系统逐步恶化到耗费大量时间排查质量问题的地步。
3、能简单化错误诊断
当系统每天都进行build和测试时,系统任何一天发生的错误都能够变得十分精细,便于排查。比如在17日系统还运行正常,18日就出错了,那么只需要检查这两次build之间的代码变化就可以了。
4、能极大鼓舞项目组的士气
看到产品的不断成长,能够极大的鼓舞项目组的士气,有时甚至不管这个产品到底用来做什么。开发人员可能会为系统显示了一个矩形而感到激动。通过每日构造,产品每天进步一点点,保证项目士气的持续高涨。
进行每日构造和冒烟测试
虽然说这是一个简单枯燥的活,每天进行build,每天进行测试,但也有着一些值得注意的细节:
1、每天坚持
每日构造,最重要的就是"每日"。如Jim
McCarthy所说,把每日构造看作是项目的"心跳",没有"心跳"的话,项目也就死了(Dynamics
of Software Development, Microsoft Press, 1995)。Michael Cusumano and
Richard W.
Selby描述了另外一种隐含的比喻,把每日构造比作项目的"同步脉冲"(Microsoft
Secrets, The Free Press, 1995)。
不同开发人员写的代码在他们的"脉冲"之间肯定都会存在"同步"的差异,但是必须有这样一个"同步脉冲",使得这些代码能够组合为一个整体。当项目组坚持每天把这些不同的"脉冲"组合到一起的时候,开发人员脱离整体的情况就会得到极大程度的杜绝。
有些项目组把这一过程简化为"每周build一次"。这样带来的问题是,某一次build失败后,可能要回溯好几周才能找到原因。如果这种情况发生的话,已经得不到经常build带来的好处了。
2、严格检查每一次build
要保证每一次build的成功,就必须保证build后的结果(也可称为build)是可以正常运行的,如果build不可运行,那么本次build被认为是不成功的,同时应该将修复此次build的工作提高到项目组最高级别来处理。
对于如何衡量一个build,每一个项目组都会定义一些自己的标准,这些标准需要设定一个严格的质量级别来处理那些特别严重的缺陷,同时也需要具有一定的伸缩性来忽略掉那些微不足道的缺陷,一些不适当的关心也许会使整个过程举步为艰。
一个好的build起码应该具备以下条件:
●能够成功编译所有的文件、库,以及其它相关组件;
●能够成功链接所有的文件、库,以及其它相关组件;
●不能存在任何使得系统无法运行或者运行出错的高级别故障;
●当然,必须通过冒烟测试
3、每天进行冒烟测试
冒烟测试应该是对整个系统流程从输入到输出的完整测试。测试不必是面面俱到的,但是应该能够发现系统中较大的问题。冒烟测试应该是足够充分的,通过了冒烟测试的build就可以认为是经过充分测试、足够稳定的。
不进行冒烟测试的build是没有太大价值的。冒烟测试就像一个哨兵,在阻止着产品质量恶化和集成问题的产生,不进行冒烟测试,每日构造可能会变成浪费时间的练习。
冒烟测试必须随着系统的扩充而扩充。最初,冒烟测试可能是非常简单的,比如验证系统是否会打印"Hello
World",随着系统功能的扩充,冒烟测试需要越来越充分。最初的冒烟测试也许只需要几秒钟来执行,逐渐地,测试可能会花费30分钟,1小时,甚至更长。
4、建立一个专门的build小组
在很多项目组,维护每日构造,并更新冒烟测试用例,会耗费一个人工作的大部分时间。因此在一些大的项目中,这项工作独立成不止一个人来完成的全职工作。比如在
Windows NT 3.0的研发中,就有一个由四个全职人员组成的专门的build小组(Pascal
Zachary, Showstopper!, The Free Press, 1994)。
5、为build增加修订,如果这样做有意义的话
一般开发人员不会每天都经常向系统中快速的增加实际的代码,通常是每隔几天,他们在开发好完成某个功能的一套代码以后,然后集成到整个系统中。
6、规定一些导致build失败的惩罚措施
很多执行每日构造的项目组都会规定一些惩罚措施,来惩罚那些导致build失败的行为。从最开始,项目组成员就清楚的知道,build的正常执行是项目组的头等大事。一个失败的build是项目组的意外,无法成为项目组工作的准则。必须坚持:导致build失败的同事,必须停下手中的工作,首先来解决build失败的问题。如果一个项目组的build经常失败的话,久而久之的,再来谈build的正确性就没有意义了。
有种轻松的惩罚措施,能够突出解决问题的优先性。Some groups give out
lollipops to each "sucker" who breaks the build. This developer then has
to tape the sucker to his office door until he fixes the problem.
有些项目组会惩罚犯错的同事戴上山羊角,或者向一个项目基金捐献5块钱。
有些项目组对此的惩罚就有点残酷了。微软的开发人员,在一些知名度很高、很重要的产品如Windows
NT,Windows
95,Excel等产品后期研发中,被要求随时带着寻呼机,如果你的代码导致build失败的话,即使是凌晨3点钟,也会要求你立即来处理这个问题。
7、即使在压力下也需坚持每日构造和冒烟测试
当项目进度的压力越来越大时,维护每日构造的工作看起来有些浪费时间,但是恰恰相反。在压力之下,开发人员丢掉一些平时的规定,会采用一些设计和实现的捷径,这在平时压力较小的环境下一般时不会用的。代码的review和单元测试也可能会比平时粗心一些,这些代码的状态变化也会比平时快很多。
为防止这种情况的出现,每日构造会坚持相关的规定,让压力下的项目保持在正轨上。代码仍然每天在不断变化,但是构造过程使得这种变化每天都可控。
谁能够从每日构造这种过程中得到好处呢?一些开发人员会抗议说,由于他们的项目太大,每天进行build是没有实际意义的。但是为什么现在最复杂的软件项目组却能够成功的执行每日构造的制度呢?本文首发时,Windows
NT包括了560万行代码、分布在4万个源程序文件中,项目组仍然可以坚持每日构造。
Software杂志上发表了下面这篇关于每日编译和冒烟测试的文章,已经是别人当时的最佳实践(Best
Practices)了。
我们现在好几个项目组都在做Daily
Build,就这一点上,其实已经落后别人十年了,但是我们希望能够坚持把Daily
Build做好,为研发的正常进行提供保证。
原文链接是http://www.stevemcconnell.com/bp04.htm
下面贴出原文,以及我自己的翻译,不当之处,请斧正。
Daily Build and Smoke Test
If you want to create a simple computer program consisting of only one
file, you merely need to compile and link that one file. On a typical team
project involving dozens, hundreds, or even thousands of files, however,
the process of creating an executable program becomes more complicated and
time consuming. You must "build" the program from its various components.
A common practice at Microsoft and some other shrink-wrap software
companies is the "daily build and smoke test" process. Every file is
compiled, linked, and combined into an executable program every day, and
the program is then put through a "smoke test," a relatively simple check
to see whether the product "smokes" when it runs.
BENEFITS. This simple process produces several significant benefits.
It minimizes integration risk. One of the greatest risks that a team
project faces is that, when the different team members combine or
"integrate" the code they have been working on separately, the resulting
composite code does not work well. Depending on how late in the project
the incompatibility is discovered, debugging might take longer than it
would have if integration had occurred earlier, program interfaces might
have to be changed, or major parts of the system might have to be
redesigned and reimplemented. In extreme cases, integration errors have
caused projects to be cancelled. The daily build and smoke test process
keeps integration errors small and manageable, and it prevents runaway
integration problems.
It reduces the risk of low quality. Related to the risk of unsuccessful or
problematic integration is the risk of low quality. By minimally
smoke-testing all the code daily, quality problems are prevented from
taking control of the project. You bring the system to a known, good
state, and then you keep it there. You simply don't allow it to
deteriorate to the point where time-consuming quality problems can occur.
It supports easier defect diagnosis. When the product is built and tested
every day, it's easy to pinpoint why the product is broken on any given
day. If the product worked on Day 17 and is broken on Day 18, something
that happened between the two builds broke the product.
It improves morale. Seeing a product work provides an incredible boost to
morale. It almost doesn't matter what the product does. Developers can be
excited just to see it display a rectangle! With daily builds, a bit more
of the product works every day, and that keeps morale high.
USING THE DAILY BUILD AND SMOKE TEST. The idea behind this process is
simply to build the product and test it every day. Here are some of the
ins and outs of this simple idea.
Build daily. The most fundamental part of the daily build is the "daily"
part. As Jim McCarthy says (Dynamics of Software Development, Microsoft
Press, 1995), treat the daily build as the heartbeat of the project. If
there's no heartbeat, the project is dead. A little less metaphorically,
Michael Cusumano and Richard W. Selby describe the daily build as the sync
pulse of a project (Microsoft Secrets, The Free Press, 1995). Different
developers' code is allowed to get a little out of sync between these
pulses, but every time there's a sync pulse, the code has to come back
into alignment. When you insist on keeping the pulses close together, you
prevent developers from getting out of sync entirely.
Some organizations build every week, rather than every day. The problem
with this is that if the build is broken one week, you might go for
several weeks before the next good build. When that happens, you lose
virtually all of the benefit of frequent builds.
Check for broken builds. For the daily-build process to work, the software
that's built has to work. If the software isn't usable, the build is
considered to be broken and fixing it becomes top priority.
Each project sets its own standard for what constitutes "breaking the
build." The standard needs to set a quality level that's strict enough to
keep showstopper defects out but lenient enough to dis-regard trivial
defects, an undue attention to which could paralyze progress.
At a minimum, a "good" build should
compile all files, libraries, and other components successfully;
link all files, libraries, and other components successfully;
not contain any showstopper bugs that prevent the program from being
launched or that make it hazardous to operate; and
pass the smoke test.
Smoke test daily. The smoke test should exercise the entire system from
end to end. It does not have to be exhaustive, but it should be capable of
exposing major problems. The smoke test should be thorough enough that if
the build passes, you can assume that it is stable enough to be tested
more thoroughly.
The daily build has little value without the smoke test. The smoke test is
the sentry that guards against deteriorating product quality and creeping
integration problems. Without it, the daily build becomes just a
time-wasting exercise in ensuring that you have a clean compile every day.
The smoke test must evolve as the system evolves. At first, the smoke test
will probably test something simple, such as whether the system can say,
"Hello, World." As the system develops, the smoke test will become more
thorough. The first test might take a matter of seconds to run; as the
system grows, the smoke test can grow to 30 minutes, an hour, or more.
Establish a build group. On most projects, tending the daily build and
keeping the smoke test up to date becomes a big enough task to be an
explicit part of someone's job. On large projects, it can become a
full-time job for more than one person. On Windows NT 3.0, for example,
there were four full-time people in the build group (Pascal Zachary,
Showstopper!, The Free Press, 1994).
Add revisions to the build only when it makes sense to do so. Individual
developers usually don't write code quickly enough to add meaningful
increments to the system on a daily basis. They should work on a chunk of
code and then integrate it when they have a collection of code in a
consistent state-usually once every few days.
Create a penalty for breaking the build. Most groups that use daily builds
create a penalty for breaking the build. Make it clear from the beginning
that keeping the build healthy is the project's top priority. A broken
build should be the exception, not the rule. Insist that developers who
have broken the build stop all other work until they've fixed it. If the
build is broken too often, it's hard to take seriously the job of not
breaking the build.
A light-hearted penalty can help to emphasize this priority. Some groups
give out lollipops to each "sucker" who breaks the build. This developer
then has to tape the sucker to his office door until he fixes the problem.
Other groups have guilty developers wear goat horns or contribute $5 to a
morale fund.
Some projects establish a penalty with more bite. Microsoft developers on
high-profile projects such as Windows NT, Windows 95, and Excel have taken
to wearing beepers in the late stages of their projects. If they break the
build, they get called in to fix it even if their defect is discovered at
3 a.m.
Build and smoke even under pressure. When schedule pressure becomes
intense, the work required to maintain the daily build can seem like
extravagant overhead. The opposite is true. Under stress, developers lose
some of their discipline. They feel pressure to take design and
implementation shortcuts that they would not take under less stressful
circumstances. They review and unit-test their own code less carefully
than usual. The code tends toward a state of entropy more quickly than it
does during less stressful times.
Against this backdrop, daily builds enforce discipline and keep
pressure-cooker projects on track. The code still tends toward a state of
entropy, but the build process brings that tendency to heel every day.
Who can benefit from this process? Some developers protest that it is
impractical to build every day because their projects are too large. But
what was perhaps the most complex software project in recent history used
daily builds successfully. By the time it was released, Microsoft Windows
NT 3.0 consisted of 5.6 million lines of code spread across 40,000 source
files. A complete build took as many as 19 hours on several machines, but
the NT development team still managed to build every day (Zachary, 1994).
Far from being a nuisance, the NT team attributed much of its success on
that huge project to their daily builds. Those of us who work on projects
of less staggering proportions will have a hard time explaining why we
aren't also reaping the benefits of this practice.
Editor: Steve McConnell, Construx Software, 11820 Northup Way #E200,
Bellevue, WA 98005.
E-mail: steve.mcconnell@construx.com - WWW:
http://www.construx.com/stevemcc/
每日构造与冒烟测试
如果你想创建一个只包含一个源程序文件的简单程序,那么你只需要编译、连接那一个文件就可以了。如果是一个团队项目组,有着许多甚至上千个源程序文件,那么要创建一个可执行程序的过程就变得更复杂、更耗时。你必须用各种各样的组件将程序逐步建立起来。
在微软或其它一些软件公司中惯例是:每日构造并做"冒烟测试"。每天都对已完成的源程序进行编译,然后连接组合成可执行的程序,并做"冒烟测试",以简单的检查该执行程序在运行时是否会"冒烟"。
带来的好处
虽然这是一个非常简单的过程,但却有非常重要的意义:
1、能最小化集成风险
项目组可能遇到的一个很大的风险是,项目组成员根据不同的系统功能各自开发不同的代码,但是当这些代码集成为一个系统的时候,也许系统完成不了预期的功能。这种风险的发生取决于项目中的这种不兼容性多久才被发现,由于程序界面已经发生了变化,或者系统的主要部分已经被重新设计和重新实现了,相应的排错工作将非常困难和耗时。极端情况下,集成的错误可能回导致项目被取消掉。每日构造和冒烟测试可以使这种集成错误变得非常小,而且便于解决,防止了很多集成问题的产生。
2、能减小产品低质量的风险
这种风险是和集成不成功、集成出错相关联的。每天对集成的代码做一些少量的冒烟测试,即可杜绝项目中那些基本的质量问题。通过这种方式,使系统达到一种周知的良好状态,维护这样的系统可以防止系统逐步恶化到耗费大量时间排查质量问题的地步。
3、能简单化错误诊断
当系统每天都进行build和测试时,系统任何一天发生的错误都能够变得十分精细,便于排查。比如在17日系统还运行正常,18日就出错了,那么只需要检查这两次build之间的代码变化就可以了。
4、能极大鼓舞项目组的士气
看到产品的不断成长,能够极大的鼓舞项目组的士气,有时甚至不管这个产品到底用来做什么。开发人员可能会为系统显示了一个矩形而感到激动。通过每日构造,产品每天进步一点点,保证项目士气的持续高涨。
进行每日构造和冒烟测试
虽然说这是一个简单枯燥的活,每天进行build,每天进行测试,但也有着一些值得注意的细节:
1、每天坚持
每日构造,最重要的就是"每日"。如Jim
McCarthy所说,把每日构造看作是项目的"心跳",没有"心跳"的话,项目也就死了(Dynamics
of Software Development, Microsoft Press, 1995)。Michael Cusumano and
Richard W.
Selby描述了另外一种隐含的比喻,把每日构造比作项目的"同步脉冲"(Microsoft
Secrets, The Free Press, 1995)。
不同开发人员写的代码在他们的"脉冲"之间肯定都会存在"同步"的差异,但是必须有这样一个"同步脉冲",使得这些代码能够组合为一个整体。当项目组坚持每天把这些不同的"脉冲"组合到一起的时候,开发人员脱离整体的情况就会得到极大程度的杜绝。
有些项目组把这一过程简化为"每周build一次"。这样带来的问题是,某一次build失败后,可能要回溯好几周才能找到原因。如果这种情况发生的话,已经得不到经常build带来的好处了。
2、严格检查每一次build
要保证每一次build的成功,就必须保证build后的结果(也可称为build)是可以正常运行的,如果build不可运行,那么本次build被认为是不成功的,同时应该将修复此次build的工作提高到项目组最高级别来处理。
对于如何衡量一个build,每一个项目组都会定义一些自己的标准,这些标准需要设定一个严格的质量级别来处理那些特别严重的缺陷,同时也需要具有一定的伸缩性来忽略掉那些微不足道的缺陷,一些不适当的关心也许会使整个过程举步为艰。
一个好的build起码应该具备以下条件:
●能够成功编译所有的文件、库,以及其它相关组件;
●能够成功链接所有的文件、库,以及其它相关组件;
●不能存在任何使得系统无法运行或者运行出错的高级别故障;
●当然,必须通过冒烟测试
3、每天进行冒烟测试
冒烟测试应该是对整个系统流程从输入到输出的完整测试。测试不必是面面俱到的,但是应该能够发现系统中较大的问题。冒烟测试应该是足够充分的,通过了冒烟测试的build就可以认为是经过充分测试、足够稳定的。
不进行冒烟测试的build是没有太大价值的。冒烟测试就像一个哨兵,在阻止着产品质量恶化和集成问题的产生,不进行冒烟测试,每日构造可能会变成浪费时间的练习。
冒烟测试必须随着系统的扩充而扩充。最初,冒烟测试可能是非常简单的,比如验证系统是否会打印"Hello
World",随着系统功能的扩充,冒烟测试需要越来越充分。最初的冒烟测试也许只需要几秒钟来执行,逐渐地,测试可能会花费30分钟,1小时,甚至更长。
4、建立一个专门的build小组
在很多项目组,维护每日构造,并更新冒烟测试用例,会耗费一个人工作的大部分时间。因此在一些大的项目中,这项工作独立成不止一个人来完成的全职工作。比如在
Windows NT 3.0的研发中,就有一个由四个全职人员组成的专门的build小组(Pascal
Zachary, Showstopper!, The Free Press, 1994)。
5、为build增加修订,如果这样做有意义的话
一般开发人员不会每天都经常向系统中快速的增加实际的代码,通常是每隔几天,他们在开发好完成某个功能的一套代码以后,然后集成到整个系统中。
6、规定一些导致build失败的惩罚措施
很多执行每日构造的项目组都会规定一些惩罚措施,来惩罚那些导致build失败的行为。从最开始,项目组成员就清楚的知道,build的正常执行是项目组的头等大事。一个失败的build是项目组的意外,无法成为项目组工作的准则。必须坚持:导致build失败的同事,必须停下手中的工作,首先来解决build失败的问题。如果一个项目组的build经常失败的话,久而久之的,再来谈build的正确性就没有意义了。
有种轻松的惩罚措施,能够突出解决问题的优先性。Some groups give out
lollipops to each "sucker" who breaks the build. This developer then has
to tape the sucker to his office door until he fixes the problem.
有些项目组会惩罚犯错的同事戴上山羊角,或者向一个项目基金捐献5块钱。
有些项目组对此的惩罚就有点残酷了。微软的开发人员,在一些知名度很高、很重要的产品如Windows
NT,Windows
95,Excel等产品后期研发中,被要求随时带着寻呼机,如果你的代码导致build失败的话,即使是凌晨3点钟,也会要求你立即来处理这个问题。
7、即使在压力下也需坚持每日构造和冒烟测试
当项目进度的压力越来越大时,维护每日构造的工作看起来有些浪费时间,但是恰恰相反。在压力之下,开发人员丢掉一些平时的规定,会采用一些设计和实现的捷径,这在平时压力较小的环境下一般时不会用的。代码的review和单元测试也可能会比平时粗心一些,这些代码的状态变化也会比平时快很多。
为防止这种情况的出现,每日构造会坚持相关的规定,让压力下的项目保持在正轨上。代码仍然每天在不断变化,但是构造过程使得这种变化每天都可控。
谁能够从每日构造这种过程中得到好处呢?一些开发人员会抗议说,由于他们的项目太大,每天进行build是没有实际意义的。但是为什么现在最复杂的软件项目组却能够成功的执行每日构造的制度呢?本文首发时,Windows
NT包括了560万行代码、分布在4万个源程序文件中,项目组仍然可以坚持每日构造。
connect超时时间的一点探讨
对阻塞的connect到底会多久超时(返回-1,并且errno被设为ETIMEDOUT)一直也没有搞清楚,今天花时间
看了一下代码并作了一点实验,大致得出了一点结论。没有时间写的太细了,把结果贴出来,感兴趣的人自己去看吧。
背景知识:
各种系统对此都没有一个总时间的限制,而是设置了重连的次数(即如果收不到synack,会重试多少遍),这个缺
省值个个系统不大一样(linux不同版本这个值也有过变化,见后)。每次重连之间的间隔时间会通过算法来调整,
这个算法个个系统的实现也有差别,因此造成了系统之间connect超时时间的千差万别(早期的BSd系统会共耗时75秒
,这点《UNP》 section4.3中提到了,但现代的系统一般更久)。
大多数系统这个重连数可调节(linux可以通过/proc/sys/net/ipv4/tcp_syn_retries或sysctl来修改此值。
TCP/IP V1》的附录E提到了其他系统的)
底下简单分析了linux下的相关代码,并作了个小实验。为了让timeout能够超时,我设了防火墙(通过ipchains)
,将规则设为DENY(REJECT会返回icmp端口不可达,会造成connect函数马上返回),并通过tcpdump检查发包重试
的情况。
自己写程序时应该考虑这种情况,自行设置更短超时(《UNP》中说的很清楚了)。
nmap再扫描这类没有任何返回的端口时,会将其状态标为"filter",这时大多情况就是有防火墙了(如果带宽足够,
对方系统又没有"非常忙"的情况下)。
更细致的探讨请见rfc793,rfc1122,《TCP/IP V1》和论文《Congestion Avoidance
and Control》。
还有一个疑问,为什么alan
cox在两次邮件中一次说总超时大约是15分钟,一次又说是30分钟?是不是以前这个重试
次数设的更大?没时间考古了,知道的请回我的mail:yawl@docshow.net.相关连接如下:
http://www.wcug.wwu.edu/lists/netdev/199808/msg00032.html
http://www.uwsg.indiana.edu/hypermail/linux/kernel/9802.0/0384.html
linux相关的代码(结合后面的结论看):
tcp_v4_init_sock():
tp->rto = TCP_TIMEOUT_INIT; //初值,即3秒
tcp_retransmit_timer():
tp->retransmits++;
tp->rto = min(tp->rto <<1, TCP_RTO_MAX);
//如果小于两分钟(TCP_RTO_MAX),每次乘2
tcp_reset_xmit_timer(sk, TCP_TIME_RETRANS, tp->rto);
tcp_connect():
tp->rto = TCP_TIMEOUT_INIT; //重复语句???
tp->retransmits = 0;
tcp_clear_retrans(tp);
运行结果:
[yawl@redhat-6 tmp]$ gcc -o connect connect.c
[yawl@redhat-6 tmp]$ ./connect 192.168.10.1 50001
Fri Apr 20 12:37:36 2001
connect fail: Connection timed out
Fri Apr 20 12:50:45 2001
运行环境如下:
[yawl@redhat-6 tmp]$ uname -a
Linux redhat-6 2.2.12-20 #1 Mon Sep 27 10:40:35 EDT 1999 i686
unknown
[yawl@redhat-6 tmp]$ cat /proc/sys/net/ipv4/tcp_syn_retries
10
[root@redhat-6 yawl]# ipchains -A input --proto tcp
--destination-port 50001 -j DENY
[root@redhat-6 yawl]# ipchains -L
Chain input (policy ACCEPT):
target prot opt source destination ports
DENY tcp ------ anywhere anywhere any -> 50001
Chain forward (policy ACCEPT):
Chain output (policy ACCEPT):
[root@redhat-6 yawl]# tcpdump port 50001
Kernel filter, protocol ALL, datagram packet
socket
tcpdump: listening on all devices
12:37:36.926670 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:37:36.926670 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:37:39.924937 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:37:39.924937 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:37:45.924934 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:37:45.924934 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:37:57.924934 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:37:57.924934 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:38:21.924940 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:38:21.924940 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:39:09.924941 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:39:09.924941 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:40:45.924939 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:40:45.924939 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:42:45.924938 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:42:45.924938 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:44:45.924940 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:44:45.924940 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:46:45.924941 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:46:45.924941 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:48:45.924939 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:48:45.924939 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:50:45.924942 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:50:45.924942 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
结论(请对照前面的代码):
0s 12:37:36.926670 //第一次发起连接
+3s 12:37:39.924937 //未收到回应,开始重试
+6s 12:37:45.924934 //在未超过TCP_RTO_MAX之前,每次重试时间翻倍
+12s 12:37:57.924934
+24s 12:38:21.924940
+48s 12:39:09.924941
+96s 12:40:45.924939
+120S 12:42:45.924938
//超过TCP_RTO_MAX之后,不再翻倍,而是固定用TCP_RTO_MAX,即2分钟
+120s 12:44:45.924940
+120S 12:46:45.924941
+120s 12:48:45.924939
+120s 12:50:45.924942
共用了大约13分钟零9秒,重试了11次(比设置的值多了一次,在maillist中发现有人提到了这个bug,并在新版本
中作了修正,见链接http://www.uwsg.indiana.edu/hypermail/linux/kernel/0001.0/0237.html)
重连次数linux不同版本也有变化:
在2.2.16的内核代码中(include/net/tcp.h):
#define TCP_SYN_RETRIES 10 /* number of times to retry opening a
* connection (TCP_RETR2-....) */
而在2.4.1中(include/net/tcp.h):
#define TCP_SYN_RETRIES 5 /* number of times to retry active opening a
* connection: ~180sec is RFC minumum */
我改了一下这个次数:
[root@redhat-6 yawl]# echo "5" >
/proc/sys/net/ipv4/tcp_syn_retries
[root@redhat-6 yawl]# cat /proc/sys/net/ipv4/tcp_syn_retries
5
然后又测了一遍,结论类似,用时3分9秒。
其他系统的测试我没有时间做了,如果有有感兴趣的测过了请把结果给我发一份yawl@docshow.net.
测试程序:
[yawl@redhat-6 tmp]$ cat connect.c
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <time.h>
int main(int argc, char* argv[])
{
int fd;
struct sockaddr_in sa;
time_t cur;
if(argc!=3){
printf("%s IP PORTn",argv[0]);
exit(-1);
}
fd=socket(AF_INET, SOCK_STREAM, 0);
if(fd<0){
perror("socket fail");
exit(-1);
}
sa.sin_family=AF_INET;
sa.sin_addr.s_addr=inet_addr(argv[1]);
sa.sin_port=htons(atoi(argv[2]));
cur=time(NULL);
printf("%s", ctime(&cur));
if(connect(fd, (struct sockaddr*)&sa, sizeof(sa)) <0){
perror("connect fail");
}
cur=time(NULL);
printf("%s", ctime(&cur));
close(fd);
exit(0);
}
看了一下代码并作了一点实验,大致得出了一点结论。没有时间写的太细了,把结果贴出来,感兴趣的人自己去看吧。
背景知识:
各种系统对此都没有一个总时间的限制,而是设置了重连的次数(即如果收不到synack,会重试多少遍),这个缺
省值个个系统不大一样(linux不同版本这个值也有过变化,见后)。每次重连之间的间隔时间会通过算法来调整,
这个算法个个系统的实现也有差别,因此造成了系统之间connect超时时间的千差万别(早期的BSd系统会共耗时75秒
,这点《UNP》 section4.3中提到了,但现代的系统一般更久)。
大多数系统这个重连数可调节(linux可以通过/proc/sys/net/ipv4/tcp_syn_retries或sysctl来修改此值。
TCP/IP V1》的附录E提到了其他系统的)
底下简单分析了linux下的相关代码,并作了个小实验。为了让timeout能够超时,我设了防火墙(通过ipchains)
,将规则设为DENY(REJECT会返回icmp端口不可达,会造成connect函数马上返回),并通过tcpdump检查发包重试
的情况。
自己写程序时应该考虑这种情况,自行设置更短超时(《UNP》中说的很清楚了)。
nmap再扫描这类没有任何返回的端口时,会将其状态标为"filter",这时大多情况就是有防火墙了(如果带宽足够,
对方系统又没有"非常忙"的情况下)。
更细致的探讨请见rfc793,rfc1122,《TCP/IP V1》和论文《Congestion Avoidance
and Control》。
还有一个疑问,为什么alan
cox在两次邮件中一次说总超时大约是15分钟,一次又说是30分钟?是不是以前这个重试
次数设的更大?没时间考古了,知道的请回我的mail:yawl@docshow.net.相关连接如下:
http://www.wcug.wwu.edu/lists/netdev/199808/msg00032.html
http://www.uwsg.indiana.edu/hypermail/linux/kernel/9802.0/0384.html
linux相关的代码(结合后面的结论看):
tcp_v4_init_sock():
tp->rto = TCP_TIMEOUT_INIT; //初值,即3秒
tcp_retransmit_timer():
tp->retransmits++;
tp->rto = min(tp->rto <<1, TCP_RTO_MAX);
//如果小于两分钟(TCP_RTO_MAX),每次乘2
tcp_reset_xmit_timer(sk, TCP_TIME_RETRANS, tp->rto);
tcp_connect():
tp->rto = TCP_TIMEOUT_INIT; //重复语句???
tp->retransmits = 0;
tcp_clear_retrans(tp);
运行结果:
[yawl@redhat-6 tmp]$ gcc -o connect connect.c
[yawl@redhat-6 tmp]$ ./connect 192.168.10.1 50001
Fri Apr 20 12:37:36 2001
connect fail: Connection timed out
Fri Apr 20 12:50:45 2001
运行环境如下:
[yawl@redhat-6 tmp]$ uname -a
Linux redhat-6 2.2.12-20 #1 Mon Sep 27 10:40:35 EDT 1999 i686
unknown
[yawl@redhat-6 tmp]$ cat /proc/sys/net/ipv4/tcp_syn_retries
10
[root@redhat-6 yawl]# ipchains -A input --proto tcp
--destination-port 50001 -j DENY
[root@redhat-6 yawl]# ipchains -L
Chain input (policy ACCEPT):
target prot opt source destination ports
DENY tcp ------ anywhere anywhere any -> 50001
Chain forward (policy ACCEPT):
Chain output (policy ACCEPT):
[root@redhat-6 yawl]# tcpdump port 50001
Kernel filter, protocol ALL, datagram packet
socket
tcpdump: listening on all devices
12:37:36.926670 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:37:36.926670 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:37:39.924937 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:37:39.924937 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:37:45.924934 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:37:45.924934 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:37:57.924934 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:37:57.924934 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:38:21.924940 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:38:21.924940 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:39:09.924941 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:39:09.924941 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:40:45.924939 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:40:45.924939 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:42:45.924938 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:42:45.924938 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:44:45.924940 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:44:45.924940 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:46:45.924941 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:46:45.924941 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:48:45.924939 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:48:45.924939 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:50:45.924942 lo > redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
12:50:45.924942 lo <redhat-6.1.nsfocus.com.3389 >
redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss
3884> (DF)
结论(请对照前面的代码):
0s 12:37:36.926670 //第一次发起连接
+3s 12:37:39.924937 //未收到回应,开始重试
+6s 12:37:45.924934 //在未超过TCP_RTO_MAX之前,每次重试时间翻倍
+12s 12:37:57.924934
+24s 12:38:21.924940
+48s 12:39:09.924941
+96s 12:40:45.924939
+120S 12:42:45.924938
//超过TCP_RTO_MAX之后,不再翻倍,而是固定用TCP_RTO_MAX,即2分钟
+120s 12:44:45.924940
+120S 12:46:45.924941
+120s 12:48:45.924939
+120s 12:50:45.924942
共用了大约13分钟零9秒,重试了11次(比设置的值多了一次,在maillist中发现有人提到了这个bug,并在新版本
中作了修正,见链接http://www.uwsg.indiana.edu/hypermail/linux/kernel/0001.0/0237.html)
重连次数linux不同版本也有变化:
在2.2.16的内核代码中(include/net/tcp.h):
#define TCP_SYN_RETRIES 10 /* number of times to retry opening a
* connection (TCP_RETR2-....) */
而在2.4.1中(include/net/tcp.h):
#define TCP_SYN_RETRIES 5 /* number of times to retry active opening a
* connection: ~180sec is RFC minumum */
我改了一下这个次数:
[root@redhat-6 yawl]# echo "5" >
/proc/sys/net/ipv4/tcp_syn_retries
[root@redhat-6 yawl]# cat /proc/sys/net/ipv4/tcp_syn_retries
5
然后又测了一遍,结论类似,用时3分9秒。
其他系统的测试我没有时间做了,如果有有感兴趣的测过了请把结果给我发一份yawl@docshow.net.
测试程序:
[yawl@redhat-6 tmp]$ cat connect.c
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <time.h>
int main(int argc, char* argv[])
{
int fd;
struct sockaddr_in sa;
time_t cur;
if(argc!=3){
printf("%s IP PORTn",argv[0]);
exit(-1);
}
fd=socket(AF_INET, SOCK_STREAM, 0);
if(fd<0){
perror("socket fail");
exit(-1);
}
sa.sin_family=AF_INET;
sa.sin_addr.s_addr=inet_addr(argv[1]);
sa.sin_port=htons(atoi(argv[2]));
cur=time(NULL);
printf("%s", ctime(&cur));
if(connect(fd, (struct sockaddr*)&sa, sizeof(sa)) <0){
perror("connect fail");
}
cur=time(NULL);
printf("%s", ctime(&cur));
close(fd);
exit(0);
}
订阅:
博文 (Atom)