2009年8月8日星期六
哄老婆秘籍
错误答案A:'爱。'
错误答案B:'这还用问吗?'
错误答案C:'你烦不烦啊?
标准答案:目光怜爱的望着对方三秒,然后神情的点一下头,同时发出'嗯'的声音,然后一把把她揽在怀里......
解 析:答案A会让她觉得你太不严肃了,纯粹胡弄她;答案B会让她觉得你对她的爱不够坚定,而且她一定会喋喋不休的问到底,让你给她明确的答案,够你烦的;答 案C过 后一定是一顿大架或冷战。其实女人对男人是不是爱她这件事儿心里特有数,她这么问纯粹就是撒娇调情,你抱抱她亲亲她比回答什么都让她高兴。
问题二:你看我这件衣服好看吗?
错误答案A:'挺好看的。
错误答案B:'还行。'
错误答案C:'真好看,我老婆穿什么都好看。'
标准答案:来来来,转一圈让我看看......'待她害羞的转完一圈后,把她拉过来,拉 着她的手微笑的看着他说:'真挺好看的。
解析:答案A会让她觉得你应付她;答案B会让她觉得没自信,在她心里她穿什么你都 应该觉得好;可是如果真说好看,像答案C一样她又会觉得你花言巧语不真诚。所以啊,你要表示出对她提的这个问题的认真程度,必须要仔细的看看再说。
问题三:在她把她的闺蜜介绍给你认识之后,回到家她问:'你觉得我这朋友怎么样啊?'
错误答案A:'挺漂亮的。'
错误答案B:'不怎么样,比你差远了。'
错误答案C:'我都没怎么注意她。'
标准答案:我觉得她对你挺真诚的,应该好好珍惜这样的朋友。
解 析:答案A纯粹是没事儿找抽型的,女人是绝不能允许你在她面前说别的女人好的(当然,母亲除外。);答案B听着就太假了;答案C你一定觉得回答得挺高明的 吧,她压根儿就不会信,而且她会展开想像分析你为什么不愿意对她的朋友进行评价。所以,要避开问题,转个弯儿回答,让她觉得你一切都是为她着想,她会有被 呵护照顾的感觉。
问题四:你觉得我胖了吗?
错误答案A:'没胖。'
错误答案B:'好像是胖了。'
错误答案C:'呦,是不是最近太累啊,怎么都瘦成这样了?'
标准答案:过来让我抱抱,等抱过之后你再说:我就喜欢你这样的,有点肉。
解 析:答案A回答得太干脆了吧,她听着不过瘾。答案B纯属找骂,女孩子怎么能喜欢听别人说她胖呢?即使你再三强调你喜欢胖的,她还会不高兴;答案C听着都那 么虚伪,现在的女人哪那么好蒙啊。其实,在女人的心里,她还是希望男人不在乎她的胖瘦,虽然嘴上说为了你减肥,其实是为了自己更漂亮,能穿更多好看的衣 服。所以你用肢体语言表示出你喜欢她有点儿肉就可以了。
问题五:我和你前女友比起来,你更喜欢哪一个?
错误答案A:'废话,当然是你了,要不然干吗跟你在一起。'
错误答案B:'嗯,怎么说呢,如果让我说实话,各有千秋吧。'
错误答案C:'她比你差远了。'
标准答案:狠狠的咬她一口或亲她一下或捏她的小脸蛋,等到她撒娇的嗷嗷叫的时候 你就说:'下次再问这种无聊的问题,还得这么惩罚你。'
解 析:答案A也太生硬直接了吧,她接下来就会问:'那你以后碰见比我好的是不是也得把我踹了啊?'答案B您就等着分手吧,忒实在了也。答案C听上去还不错, 但你这不是抽自己嘴巴嘛,你说自己前女友不好会让女人很得意而降低了你自己的身价。其实在女人心里,她特别希望你的前女友是个特别优秀的女人,而偏偏是你 不愿意跟她好了,这样女人心里会有成就感的。可是你又不能直接说前女友有多好,所以就用她喜欢的方式来回避问题吧(至于她是喜欢你捏她的脸蛋儿还是咬她一 口我们就不知道了)。
问题六:如果我和你妈妈同时掉进水里,你会救哪一个?
错误答案A:'你有聊无聊啊?'
错误答案B:'一块儿救。'
错误答案C:'宝贝儿,别难为我,再说这种情况也不会发生的。'
标准答案:哦,我没告诉你吧,我妈是游泳健将。
解析:虽说问这个问题确实很无聊但你也不能像答案A那样,这会引来喋喋不休的争吵;答案B她一定会不甘心的追问下去,你不想清静清静吗?答案C听上去还不错,但还是很牵强。不如就幽默一回,别让她一天到晚的老杞人忧天没事儿找事儿了。
问题七:如果我老了难看了你还会爱我吗?
错误答案A:'不可能,你老了也好看。'
错误答案B:'女人不同的年龄段有不同年龄段的魅力......'
错误答案C:'没事儿,你老了我也好不到哪儿去,咱俩谁也别嫌弃谁。'
标准答案:我巴不得你难看点儿老点儿呢,这样放在家里多放心啊。
解析:答案A她听着也高兴,但心里知道这是绝对不可能的;答案B就赶紧打住吧,唐僧来了;答案C不就说她老了肯定不好看了嘛,她怎么能接受呢?所以啊,最聪明的答案就是既让她打消顾虑又让她知道你特别在意她特别怕失去她。
2009年8月7日星期五
我喜欢“大道至简”的方法
本杰明・格雷厄姆认为,股票内在价值的概念非常灵活,这种灵活性在不同情况下会表现出不同的明确性。举一个例,比如对于招商银行,如果我知道近三年(2008、2007和2006)来它的营业收入分别为553亿、409亿和250亿;净利润分别为209亿、152亿和71亿;净资产收益率分别为28.58 %、24.76%和16.74%;不良贷款率由2006年2.12%降到2008年的1.11%;资本充足率则一直维持11.%以上。 2008年10月和2009年1月招商银行又发生了两个重要事项:一是纽约分行正式开业,这是继1991年美国颁布《外资银行监管加强法》以来第一个获得美联储准入的中资银行;一是收购永隆银行,使得永隆银行成为全资附属公司。毫无疑问,这两个重大的举措将提升公司的内在价值,对于进一步加快国际化进程,深入推进经营战略调整,具有深远的战略意义。因此,我估计招商银行的内在价值不会少于5100亿元。当然,如果由另一个人去估计,他得出的肯定又是另一个数据。实际上招商银行在去年市场极端下跌后,其股票价格曾经一度跌破了11元,市盈率不足8倍,市净率在2倍以下,这种情形并不常见。要知道2倍以下的市净率往往是银行并购的价格,招商银行自己全资收购永隆银行,出价就相当于市净率3.01倍。因此,在我看来,此时对"内在价值不会少于5100亿元",并且准备以多少折扣买入这个判断不如"市盈率不足8倍,市净率在2倍以下"来得明确。但即便如此,也并不意味着我就不重视估值,因为毕竟"这种定性分析本身就非常有助于股票买卖决策"(格雷厄姆)。菲利普・费雪在他另一本名著《股市投资致富之道》中说,"归根结底,即使最成功的股票投资在本质上都包括也反包括三个部分:
1、选择一只或多只未来增长潜势超越市场大盘的股票;
2、知道应该买入的适当时间;
3、知道应该卖出的时间。"
在我看来,"知道应该买入的适当时间",要么就是大萧条或大崩溃的时候,要么就是在优秀企业遭遇严重困难的时候。特别在大萧条或大崩溃的情形下,几乎所有的股票都会出奇得便宜,所以此时不必过分关注估值――因为事实太明朗了。反之亦反是。确切地说,我喜欢这种"大道至简"的方法,因为这本身也符合费雪在他的《怎样选择成长股》第一部分《普通股和不普通的利润》第一章的开头所说的,"在萧条时买入,而在繁荣时卖出股票的做法包含了很浓厚的价值原理。"而且,我更深信,投资最根本的问题就是心态,并非精确地估值。无论多么重视估值、精确估值,假如不能抑制内心的冲动,控制好情绪,一样地难以取得成功。
过去我对"价值评估,既是艺术,又是科学"深感不解。现在我才知道,大致准确地预测是一种艺术,但满意地进行预测以满足投资决策却是一门科学。关于这一点,我认为肯尼斯在他的父亲菲利普・费雪《怎样选择成长股》的序言"从父亲的著作中学到的"中一段话可以作为这句话的参考:"在本书出版的时候,父亲已经51岁了,他具有不拘一格的才华,已经获得了非常大的成功。这些年来,他慢慢地从直觉上将对技巧的理解转变为对艺术的理解,但是我认为,他不明白这一转变是新手需要花很长的时间才能学会的。......我写这些文字的时候已经52岁了,和他当时写本书的年龄差不多;我很清楚地了解这一点,因为我也不得不花时间去学习,而不是创造这一过程。"看起来,掌握一门"技巧"易,但是上升至"艺术"则难得多。相对于肯尼斯,我绝对不会比他更聪明。他尚且还要"花时间去学习",以求得"从直觉上将对技巧的理解转变为对艺术的理解",而况于我,因此我肯定"也不得不花时间去学习,而不是创造这一过程"。
约翰・邓普顿指出:"正确的买入时间是悲观情绪最严重的时刻,那样的话,大部分的问题都可能被解决。"我想,这里面"大部分的问题"中应该也包含了估值的问题。
传说中的挖宝图
1999年各月上证所月度A股平均市盈率
月份 月末市价总值 月末平均市盈率
(亿元)
1 10518.86 34.03
2 10171.11 0
3 10923.02 34.3
4 10650.42 8.39
5 12180.22 35.3
6 16263.85 45.37
7 15524.26 43.93
8 16115.3 43.89
9 15694.41 42.52
10 15127.68 40.84
11 15203.35 40.09
12 14440.72 38.14
2000年各月上证所月度A股平均市盈率
月份 月末市价总值 月末平均市盈率
(亿元)
1 16326.65 42.82
2 18279.09 47.99
3 19397.18 49.92
4 20067.84 51.13
5 20796.51 54.02
6 21458.29 55.22
7 23202.11 58.21
8 23247.66 58.13
9 22228.77 54.83
10 22957.48 56.31
11 24599.07 59.89
12 26596.32 59.14
2001年各月上证所月度A股平均市盈率
月份 月末市价总值 月末平均市盈率
(亿元)
1 27193.01 59.39
2 26216.84 56.82
3 28736.26 60.88
4 28938.1 60.99
5 30273.98 55.92
6 30839.22 56.55
7 26913.68 49.26
8 29127.09 42.14
9 28109.11 40.61
10 26865.47 38.84
11 27829.13 40.08
12 26934.51 37.59
2002年各月上证所月度A股平均市盈率
月份 月末市价总值 月末平均市盈率
(亿元)
1 24588.31 34.31
2 25265.63 35.11
3 26915.17 37.16
4 28784.68 39.08
5 26367.73 38.75
6 30390.79 44.47
7 29134.53 42.4
8 29691.54 43.02
9 28359.39 40.4
10 27561.3 38.23
11 26292.8 36.46
12 24921.42 34.5
2003年各月上证所月度A股平均市盈率
月份 月末市价总值 月末平均市盈率
(亿元)
1 27789.98 37.92
2 28067.34 38.2
3 28122.77 38.2
4 28433.65 38.53
5 29421 38.28
6 27832.56 36.11
7 27902.85 35.78
8 27083.36 34.37
9 26282.55 32.97
10 26012.76 32.51
11 27333.16 34
12 29400.65 36.64
2004年各月上证所月度A股平均市盈率
月份 月末市价总值 月末平均市盈率
(亿元)
1 31311.1 38.91
2 32995.4 40.89
3 34442.98 42.49
4 31661.63 38.95
5 30878.95 28.73
6 28011.9 26.65
7 27903.98 26.49
8 27127.72 25.68
9 28200.99 26.75
10 26712.03 25.34
11 27185.07 25.69
12 25714.07 24.29
2005年各月上证所月度A股平均市盈率
月份 月末市价总值 月末平均市盈率
(亿元)
1 24197.84 22.87
2 26646.51 24.99
3 24086.62 22.63
4 23617.35 22.28
5 21863.1 15.66
6 22233.41 15.98
7 22310.52 16.05
8 23556.64 16.92
9 23372.06 16.78
10 21950.15 15.72
11 21832.6 15.63
12 22856.07 16.38
2006年各月上证所月度A股平均市盈率
月份 月末市价总值 月末平均市盈率
(亿元)
1 24564.4 17.61
2 25171.97 18
3 24791.28 17.72
4 26820.64 19.42
5 30352.15 19.69
6 30741.26 19.91
7 34943.16 20.03
8 36528.15 20.38
9 38545.08 21.41
10 47843.36 22.86
11 54777.81 26.13
12 71117.95 33.38
2007年各月上证所月度A股平均市盈率
月份 月末市价总值 月末平均市盈率
(亿元)
1 83484.61 38.36
2 87865.2 39.62
3 99621.45 44.36
4 123592.59 53.33
5 135934.29 43.42
6 127945 42.74
7 152554.73 50.59
8 178549.93 59.24
9 195147.66 63.74
10 223604.16 69.64
11 241413.06 53.79
12 268497.27 59.24
2008年各月上证所月度A股平均市盈率
月份 月末市价总值 月末平均市盈率
(亿元)
1 224268.6 49.4
2 224740.28 49.21
3 180455.44 39.45
4 193829.85 42.06
5 180409.12 25.89
6 143795.11 20.64
7 145955.48 20.93
8 126514.5 18.13
9 121164.86 18.68
10 91419.32 14.09
11 98792.46 15.23
12 96875.31 14.86
2009年各月上证所月度A股平均市盈率
月份 月末市价总值 月末平均市盈率
(亿元)
1 106054.88 16.26
2 111178.39 17.01
3 126720.61 19.37
4 132407.92 20.21
2009年8月5日星期三
每句话可以品半辈子
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
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超时时间的一点探讨
看了一下代码并作了一点实验,大致得出了一点结论。没有时间写的太细了,把结果贴出来,感兴趣的人自己去看吧。
背景知识:
各种系统对此都没有一个总时间的限制,而是设置了重连的次数(即如果收不到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);
}

