05 2010存档

看到奇文一篇,说世界上有75%的人都后悔自己年轻时努力不够。
我不理解。

读中学的时候,我不知道花了多少精力来背单词音标(因为我考音标总是丢分)、背历史、背古文,还有准备各种各样的考试,我自己回想起来真是没什么遗憾,真的够卖力了。但我现在用得上英语音标吗?用得上历史年代吗?用得上古文吗?我把我的少年时代拿来背书了。
上大学的时候,我花了很大力气才考过了有机化学、物理化学、分析化学、结构化学,整个大学我都在对挂科的恐惧中渡过的,多少个自习室熄灯的晚上我还在看那该死的无用的化学书啊。后来我当上了程序员,我学那么苦那么多那么卖力的化学知识,拿来干什么呢?我四年的青春就拿来送给化学系了。

少壮不努力,老大徒伤悲;那少壮努力了,就不伤悲了吗?努力错方向的人、努力白费了的人,他们就不后悔了吗?

“努力”只是一种投资,跟其他投资形式一样,都是有风险的,你能担保你的“努力”就一定有回报吗?

说回那篇奇文,如果你年轻时努力透了,你可能现在每天睡前,发现自己浪费了那么多青春时光在死记硬背上,回首往事,居然除了课本,啥也记不起来,你是不是还是后悔呢?
没努力过,后悔;努力过,也后悔,人这种动物,真是太麻烦了,上帝如果天天听人类说后悔事,估计再来一次世界末日的心思都有了。

所以,别再后悔了,别再唠叨了,过去的就一定要过去。现在不是过得挺好吗?好好过吧。
补充了一篇epoll里EPOLLET标记的代码剖析:


我原先的client端代码流程如下:  

创建一个socket
设为异步socket(fcntl)
将socket加入epoll
connect到远端(此时connect调用返回非0,但errno为EINPROGRESS,表示正在建立连接中)
epoll_wait之
捕获到EPOLLOUT事件,此时便认为connect已经成功,client端开始发消息

这个过程通常能够运转,但是线上环境复杂多变,如果发生这种情况:server进程调用listen开始侦听后,被gdb或信号挂住了,此时异步connect会怎样?很遗憾,client端的epoll_wait依然返回EPOLLOUT,甚至往此socket里发消息都返回成功,只有当发的消息多得占完了server端的tcp缓冲以后(窗口收缩到很小),send调用才开始失败。这时候用 losf -i 看网络连接也很有趣,client端的机器显示连接建立了,server端的却显示没有这个连接。
仔细想想,OS这样做是正确的,毕竟connect的语义只是“连接”,当server挂住时,连接还是能成功的,但你能不能往里面发消息那就是另外一回事了。

所以对于应用来说,异步socket想要知道connect后连接是不是可以正常收发数据了,还是要靠应用层的一问一答才能知道。


====== 2010.5.14 ======

昨天同事朱照远给了一个更正确的解决方案,可参考之:
收到EPOLLOUT也不能认为是TCP层次上connect(2)已经成功,要调用getsockopt看SOL_SOCKET的SO_ERROR是否为0。若为0,才表明真正的TCP层次上connect成功。至于应用层次的server是否收/发数据,那是另一回事了。”

最近参与一个大项目,几十个开发人员,代码都放在一个trunk下,也就是说,这几十个人的代码都在一起编译,一起跑单元测试,一起在半夜跑自动化测试用例。反正我以前是从未加入过这么大的trunk(之前我参加的项目最多就四五个人在一起写代码而已),现在发现巨大的trunk造成了一些很郁闷的问题。

首先,编译越来越慢。项目用scons来编译(我暂时还没发现scons比Makefile好在哪里),一编译就耗干系统的内存(我们的系统是8G内存),整个trunk重编译一次是两个多小时,好在我负责的只是其中一部分,但是即使是这一部分,也要花十多分钟编译(我目前还不能说这是scons慢造成的,但是python占内存偏多,嫌疑很大),这在我调试阶段很费时间。

其次,互相干扰很严重。几十个程序员,就算一个人每一个月才出现一次代码错误,那加起来就是:每天都有人至少犯一次代码错误。虽然使用了ReviewBoard,但是review不可能洞察一切,还是会有单元测试频繁的不过。后来加了一个提交代码的工具,为每个提交代码的人单独编译并跑单元测试,通过了才真正交给subversion,但这样还是有问题:每次提交,我都发现有很多人排着队的等前面的人编译完,我有几次好不容易排上了,跟别人的提交一起编译,结果由于别人的提交编译不过,我还得重新排队......问题的根源在于:这个trunk上的提交太过频繁了。毕竟几十个人呢,几乎每十分钟就有新的code需要提交。

可能长期在互联网公司干,我对那种动辄几百万行code,几十兆可执行文件的大软件越来越反感。我觉得好的软件,一定是满足且刚好满足了某一类用户的某一个需要,而不是妄图去满足所有用户的很多需要。说白了:尽量做小巧的东西。如果项目真的是个大项目,那最好是能拆成多个小软件,好歹代码能分到不同的trunk下去,trunk小了,挤在一个trunk下的开发人员就会少很多,就不会有漫长的编译和频繁的提交了,然后,这些小软件一起运行,完成某个大工作。

说得有点理想化了,把大项目拆成小部分,这个道理谁都懂,但难度在于怎么拆?这种问题我回答不了,也许《unix编程艺术》能够回答:不要写一个巨大的进程,应该写多个,然后一起工作。进程都可以拆开,代码有什么不能拆开的?有人可能担心效率,但是相比于混乱的管理,这点效率损失我觉得还算值得。

以上是我的看法。实际中,反例也是有的,比如windows NT就是一堆人挤在一个代码trunk下做出来的,而且产品还很成功。但是从《观止》里可以看出,NT的开发人员也一样被频繁的build break和单元测试fail折磨得人不人鬼不鬼,也许这就是为什么他们要辛苦上6年才能开发完成,以及为什么操作系统的未来是属于微内核的(微内核巧妙的把OS拆开了)。


====== 2010.5.10 ======

补充一下,每次编译,除了内存,CPU也被占满,登录同一台服务器的其他人连vi都用不了,到了后来,我在哪台机上编译,哪台机上的开发人员就埋怨,真成“嫌人”了;编译完后,20G的硬盘空间就没了,想只编译一部分是不行的,因为没拆开,整个trunk必须一起编译。

更可怕的是,这么大的trunk,还要打分支,一个160G的硬盘,才能下载编译几个分支呢?

关于存档

This page is an archive of entries from 05 2010 listed from newest to oldest.

04 2010 is the previous archive.

06 2010 is the next archive.

Find recent content on the main index or look in the 存档 to find all content.