Posts archived in CompSci

我用苹果 Mac OS X 两年了, 越用越觉得, 其他操作系统要从苹果的 Mac OS X 上学习很多东西. 特别是图形界面的统一的脚本化接口, 我认为这对未来的操作系统是一个很好的启示, 也是完全用命令行工作哲学在图形界面时代的一个不可缺少的部分.

起因是最近我在写一系列的中文文章, 因为几乎没有数学公式, 而我又喜欢纯文本的工作方式, 因此在排版上我选择了reStructuredText. 一般来说, 英文文本我都是直接在 vim 下工作. 可是苹果下的 vim 对中文的支持不是想像的那么好, 至少中文输入法打出来的常常有乱码. 名动天下的 TextMate 固然好, 显示中文却只有半个字那么宽, 看上去很不舒服. 其他的编辑器要么收费, 要么嫌大, 要么不顺手, 因此落到最后还是用了苹果的 TextEdit.

但是写技术文章的人都知道, 写文章不仅是打字这么简单. 特别是技术文章, 常常需要离开编辑环境跑脚本, 查Google, 看 wiki. (我不知道别人是不是这样, 至少我为了写出质量较好的文章, 写的时候也要做很多功课). 特别是因为我在用 reStructuredText, 一个最基本的需求就是处理当前文本生成 HTML/LaTeX 然后预览. 这些需求, 在 VIM 和 TextMate 中都是原生支持的, 很简单能做到. 但是在 TextEdit 这样的软件中, 乍一看, 毫无办法. 所幸的是, UNIX 家族的操作系统, 特别是苹果, 秉承了 简单的事情要有简单的解法, 复杂的事情要有可能的解法 这样的设计思路, 提供了不止一条解决问题的方法. 因为苹果设计了一个叫做”脚本化应用程序”的东西, 使得这些任务可以很简单的完成. 我研究了几天, 学到了一些东西, 拿出来和大家分享.

1. 最常常被忽略的菜单栏, Service 菜单.

用具体的例子来说可能更加简单. 用 Gmail 的各位都知道, 如果邮件里面有 90081243455 这样的数字串, 或者有 北京是西城区XX路YY号 这样的地址, Gmail 就很贴心的显示出 “使用 Google 追踪快递包裹” 或者 “使用地图查看地址” 这样的链接. 也就是说, Gmail 会识别出这些特定的样式, 然后给你一个上下文相关的(也就是说, 和你内容相关的) 服务. 这样的服务贴心舒适, 相信不少人都很喜欢. 一般我们把这个叫做上下文相关的服务.

再说 Firefox. 前不久 Firefox 出了一个很贴心的小插件, 叫做 Ubiquity, 我在 Firefox 下几乎离不开它: 选中任何一行地址, 只需要敲几个键, 就可以查 Google Map. 选中任何一个名词, 可以立即查 Wikipedia, 等等. 一般我们把也把这个叫做基于选定的服务.

苹果上的 Service 菜单, 就是这样的一个基于上下文相关和基于选定的服务. 举个最简单的例子, 如果你的机器上装有 Skype, 然后你选中了一串电话号码, 这时候你可以到 Service 菜单中选择 Skype -> Call this number. Skype 就会自动拨打这个电话. 或者选中一个单词, Service 菜单中就会自动出现: Look up in the Dictionary. 选中它以后, 你机器上的字典就会跳出来, 把单词的释义呈现给你. 所有的这一切, 都是基于苹果的一个叫做 System Service 的框架. 简单的说, 每个应用程序告诉系统: 在什么情况下, 你给我什么信息, 就可以让我做什么事情. 这样, 在选定的上下文符合条件的时候, 你就可以在菜单中选择这个服务. 这个东西方便无比. 比如说, 常常老板在邮件里面写: 你这周做啥啥. 我就可以选中整行句子, 按下 ⇧⌘ Y. 这样整行句子就被纪录到 Stickies 里面去了, 相当于随时写下一个便签.

现有的菜单栏固然好, 但是自己编写的更加好玩. 所幸的是, 你不需要会苹果的生涩的 Obj-C 才能编写菜单. 有一个强大的工具, 叫做 ThisService, 能够把 Ruby, Python, Applescript 和 shell 脚本都包装成 Services. 比如说我昨天就花了一分钟写了一个很简单的叫做 Translate 的脚本, 通过 Python 提交文本给 Google Translate, 返回中文. 在阅读有很多不认识的单词的新闻文章时候, 只要选中段落, 选择这个 Translate. 一个翻译成中文的窗口就出现了. 在这个软件的帮助下, 任何脚本都能点一下鼠标就变成一个服务: 具体我就不一一详述了.

总的来说, 我们可以注意到, 在日常使用计算机的时候, 一个应用程序中的一些内容(文本或者图像), 往往要作为另外一个程序的输入, 交给另外一个程序处理. 比如选中的单词查字典, 选中的地址查阅地图, 和选中的电话号码拨打, 选中的句子翻译等等, 这些需求都是存在的. Windows 家族解决问题的方法, 是通过程序切换和剪切板. 而苹果就是通过统一的接口来完成的, 思想和UNIX管道是非常相似一脉相承的 (当然, windows 家族从来就没有 UNIX 家族管道这个思想, 所以也就想不出统一接口这样的设计了).

在苹果下, 统一脚本接口可以用来处理像 Service 这样的程序之间通信的问题, 这个是 windows 家族从来没有想过的. 同时, 据我个人经验, 这个思想在 Linux/X11 家族也没有全部实现. 在 Linux 命令行下, 我们都知道, 不同的小工具之间是正交的, 一个程序的输出是一个程序的输入, 这个 UNIX 哲学长盛不衰. 直到今天, 正常的 Linux/UNIX 用户也会天天用管道做程序之间的通信. 可是在图形界面出现后, 这样的好传统在 X11 系统中似乎没有被继承. 比如, 不能选中一段文字直接送给 wc 计算字数. 究其原因, 是因为图形界面再也没有明显的输入输出这个概念了. 字符串和图像变成了要在程序之间传送的基本单位. 在这种情况下, 就需要不仅仅以文件为基本单位, 而是以被选中的那些字符串, 句子, 链接, 图片等为基本单位的通信. 在这个层面上, 苹果的确先行一步. (当然 Emacs VIM 等等也是可以通信和调用外部程序的, 但是毕竟不是整个系统都能这样).

2. 史上最简单的语言, AppleScript.

故老相传, 当年 Knuth 大神访问苹果, 总裁 乔布斯 同学发挥爱吹牛的光荣传统, 说: 高教授, 您来了. 我特别崇拜你, 您的书我都读过. 各位读者都知道, 要是Jobs 真的把高教授的书都读过, 全世界大学计算机系99%的教授基本上都要自杀让位给Jobs. 所以, 高爷子知道他吹牛皮. 以下的故事分两个版本, 第一个版本是硅谷的版本, 是高爷子当即就说: 斯蒂夫同学, 你又扯淡忽悠了. 第二个版本是我的版本: 说高爷子一听, 也不生气, 心想, 你我共同作为硅谷两个最懂字体设计的搞IT的, 我也不好得罪你, 且问你一道简单的问题, 看你读了我哪几本书. 于是高爷子眼珠一转, 题上心来, 问到: 乔布斯同学, 请问世界上最简单的编程语言是啥子呢? 乔布斯是个不懂技术的主. 想起当年和 Woz 合作的时候搞的东西叫做 BASIC, 听名字就觉得简单, 于是说道: 乃是 BASIC. 高爷爷说, 还有比这个更简单的么. (话说高爷爷心想, 我手里握着 TeX, Metafont, MIX, Literate Programming, CWEB 等几张王牌, 我就不信你说不出一个搭不上边的). 乔布斯同学哪能体会到高爷爷的良苦用心, 他又不会编程, 于是就杜撰到: 我们公司最近正在开发世界上最简单的一个语言: 叫做苹果脚本(AppleScript). 高爷爷一听就知道又是吹牛, 于是立即回复: 斯蒂夫, 你又扯淡忽悠了. 斯蒂夫同学哪服软, 于是要求开发部门开发一个连他都懂怎么用的语言. 于是 AppleScript 就被捣鼓出来了.
(以上故事纯属扯淡忽悠)

话说AppleScript 有多简单呢? 就和说话一样简单. 我曾经也写过介绍. 再这里再给一个例子:


tell application "TextEdit"
set filename to name of document 1
print filename
end tell

看, 明明就是英语嘛! 这么平铺直叙几乎没有语法的编程语言, 真的是世界上最简单的编程语言唉.

别看这个语言简单, 威力可不小. Mac 上几乎所有的应用程序, 都能够用这个语言去控制, 特别是 iTunes 甚至还有一个专门的网站, 专门就让你下载千奇百怪的控制脚本.

Applescript 还有两个好处: 1. 他是苹果原生支持的, 可以直接编译成苹果的应用程序来用. 因此, 简单的任务处理都可以用这个语言. 比如我们上面说的做成 Service 的, 也可以用 Applescript 来实现. 2. 他能够和其他应用程序交互. 其实在苹果系统中, 在脚本桥技术(Script Bridging) 出现之后, 理论上C/Javascript/Python/Ruby 都是可以和应用程序交互的. 可是AppleScript 的支持更加好, 更加直接. 比如取当前播放的iTunes乐曲名字, 可以用
AppleScript:

tell application "iTunes" to get the name of the current track

也可以用 Python

from Foundation import *
from ScriptingBridge import *
iTunes = SBApplication.applicationWithBundleIdentifier_("com.apple.iTunes")
print iTunes.currentTrack().name()

显然 Python 要显得笨重一些.

同时, AppleScript 也是苹果中小机器人 Otto 的底层技术, 因此在整个系统中发挥了基础性的作用. 虽然学习 AppleScript 显得很偏很不入流, 但是在苹果上做一些小任务有时候还是用得着的. 比如说我的让 TextEdit 支持 ⇧⌘ R 自动编译的例子, 就是先写了一串脚本, 取当前文件名, 然后使用 do shell script 这个指令调用 make. 这样, 调用这个脚本, 就可以完成自动编译. 用快捷键来调用脚本, 有两种方式, 一种是通过把脚本拷贝到应用程序的脚本目录并且添加一个菜单项. 另一种是用 QuickSilver 来帮忙了. 第一种方法应该最简单, 但是我没有尝试成功, 有兴趣的读者可以读这篇文章.

3. QuickSilver

我曾经间或的提到过 QuickSilver. 刚开始我并不能体会到他的魔力, 直到最近在玩脚本控制的时候才发现. 简单的说, QuickSilver 就是能让你用键盘控制程序的程序. 或者说, QuickSilver 就是键盘命令中枢. 比如说播放音乐, 只要一键激活 QuickSilver, 再打一下 play (有时都不要打全), 回车. iTunes 就放起音乐了. 上面我们说的任何控制系统的脚本, 都能够被 QuickSilver 控制. 我在拔掉你的鼠标一文中也提到, “你要是苹果用户又不用QuickSilver或命令行, 那你是把苹果当Windows用”.

QuickSilver 作为应用程序快速启动的功能大家想必都很熟悉. 控制 iTunes 大家也得心应手. 我就介绍一个被大家忽略的功能: 设置上下文相关的快捷键. 传统上, 快捷键是被应用程序定死的. 即使你写了一个扩展应用程序的脚本, 你也得点点鼠标去调用, 而不能设置键盘快捷键去调用. 解决的方法有三个, 第一个是我在第一部分讲的, 包装成一个 Service. 这样的好处是可以包装出一个快捷键, 坏处是这个Service 是全局都有用的, 不管你在哪个应用程序里面, 这个 service 都能用. 第二种是我在第二部分提到的我没有尝试成功的, 即添加一个菜单项目的方法. 第三个就是采用 QuickSilver 的 Trigger 功能了. QS 用户可以使用 ⌘’ 调出 trigger, 然后添加一个 Custom Trigger, 把目标指向自己写好的一个 AppleScript, 给这个 trigger 分配一个快捷键, 并且把使用的范围 scope 限定在一些应用程序中. 这样, 不费任何功夫, 只写一个简单的脚本, 您就可以在你的计算机上把你的 TextEdit 也改造成一个功能强大的编辑器了.

结语: 如我在拔掉你的鼠标一文中说的, 完全使用键盘能强迫人使用高效简洁和正确的工具. 随着图形界面和多媒体的出现, 鼠标的反而成了一个最常见的动作. 其实认真分析鼠标点选操作就发现, 无非是两种: 一种是用鼠标选择内容, 准备用一定的工具和程序处理这个内容, 还有一种是鼠标选择操作. 前者对内容的选择, 鼠标是高效的. 而后者对操作的选择, 鼠标是低效的 (因为键盘快捷键和命令行更加高效). 因此, 如果我们想要高效的工作, 就要避免后一种操作, 并且进一步解放前者.

对后一种的避免很简单: 拔掉鼠标, 强迫用键盘. 对前者的解放, 随着各种各样如 Service, Ubiquity 以及上下文敏感程序的兴起, 使得我们不需要在不同的程序之间拷贝来拷贝去, 而是直接通过程序之间的标准接口通信, 使得我们甚至不需要切换程序. 所有的都应该这些基于命令行和后台来完成, 而不是通过分散注意力切换程序来完成. 这些新的工具, 更加符合一个高效能的现代人的需求, 更加能够提升你的效率. 当然, 决定做事的效率的不仅仅是工具, 因此, 不要迷恋这些工具. 为了获得 1% 效率的提升, 而过于强调工具的各种淫巧, 反而降低效率. (警告: 这些工具都是非常迷人的, 很容易一用就爱上它们. 记住, 效率是最好的判断标准 :).

附 “完全用命令行工作系列” 文章一共五篇, 写了也不少了, 有些工具我一笔带过, 有些我花了几句话讲了一下. 基本上我介绍到的提到的, 都是我自己天天用的, 也是我认为非常有用的. 我写作的时候也都是带着 “Smart and Get Things Done” 的哲学, 只介绍干净的设计良好的软件. 各位想实践 GTD 的读者可以自行选择趁手工具. 有更多的 GTD 的完全用 键盘/命令行 工作的软件, 也不妨向我推荐.

完全用命令行工作-3: 常用工具

完全用命令行工作-2: 常用软件

完全用命令行工作-1: 拔掉你的鼠标

完全用命令行工作: 几条趁手语句

软件, 没有圣杯的荣耀, 只有梦萦的代码 — “Dreaming in Code”书评

code2.jpg当日我读完第三遍 “Dreaming in Code“,  是夜, 代码冰山入梦来. 我想, 应该整理一下我的读书笔记, 写一篇书评了.

两打程序员, 三年的时间, 4,732 个 Bug, 以及一个愿望: 打造一个卓越的软件. 这就是 “Dreaming in Code” 所描述的故事. 而这个故事, 却没有 “新机器的灵魂 (The soul of a new machine)” 那样波澜壮阔, 甚至没有 “人月神话 (The Mythical Man-Month)”  那样告诉你一个明确的教训. 单论情节,  这就是一个沉郁到极点的故事. 没有英雄人物, 没有情节跌宕, 所有的人草草的出场, 草草的收场. 可是依旧一个梦在那里. 这个梦让全世界的程序员心动, 萦绕每个敲下过 Hello World 的人 — 怎样打造卓越软件.

软件难做,  高德纳 (D. E. Knuth) 这样写道. 为何难, 他没有回答. 而 “Dreaming in Code” 带来了一个具体的例子, 一个鲜活的, 几乎失败了的例子. 一个有资金, 有优秀程序员, 有开放的管理文化和明确的雄心的项目, Chandler, 曾经如恒星一样耀眼, 又如流星一样湮没. 不禁要问: 为什么? “幸福的家庭是相似的, 不幸的家庭各有各的不幸”. 这句话用来解释失败的项目再合适不过了.  如 Fred Brooks 那个著名的论文 “没有银弹 (No Silver Bullet)”[PDF] 揭示的一样, 这个问题, 没有一个明确的答案.

即使没有答案, 人们也在求索. 这本书, 和 “人月神话” 一样, 都是探索怎样打造优秀软件的著作. 不同的是 “人月神话” 近似于总结, 而这本书, 却只有问题,  无数的问题: 为什么不能像架设桥梁一样打造软件? 为什么构建软件不能像搭乐高积木一样? 为什么一个创造奇迹的人不能再次创造奇迹? 为什么有的开源软件成功了, 有的却失败了? 太多的为什么, 太多的问题. 而这个问题的答案, 不在书上, 确在书的字里行间, 在读者的思考里.

我是个天天写代码的程序员, 也是个常常在开源社区参与项目的程序员, 说起来再熟悉不过代码这个东西了. 可是怎样编写代码来组建卓越的软件, 我没有答案, 谁都没有答案. 可是这本书能帮助你找答案. 我极其推崇这本书的原因是, 读完之后, 你能问自己一些问题, 答案是这个么, 是那个么? 这本书本身, 不是答案, 而是获得答案的钥匙. 随着记者特有的写实的文风, 你会在书的字里行间, 跟着作者找到这把钥匙.

对于想找钥匙的人, 这本书有两个优点. 一是提出问题多, 资料多. 都说提出问题是解决问题的一半, 这本书在这一半做得相当好. 问题的另一半, 也就是答案, 这本书没有具体给出. 但是这就给了读者独立思考的余地. 有人会说是因为没有风险投资的压力, 有人说是前期需求不明确, 有人认为管理混乱. 无论怎样的答案, 你都会找到翔实的资料来佐证, 或者推翻. 这是一部活生生的不带评论的记录片. 这样的一手资料, 是极其有价值的.  第二个优点是故事的本身. 这是一个让人郁闷的故事, 一个失败的故事. 成功的故事通常激动人心, 但成功的光环往往掩盖了很多问题. 在成功的耀眼光芒下, 即使钥匙就在阳光下, 也很难找到光闪闪的它. 而在失败的废墟和尘土中, 钥匙如小钻石一样藏着, 细微的光芒引导勇敢的探索者, 并给他高价的回报. 失败总是比成功, 更加容易获取教训.

这是一本文笔优雅, 细节具体的书. 可是缺点也是显而易见的. 最明显的就是琐碎八卦太多. 作者是一个知识面很广的人, 能够从日历软件扯到图灵机和停机问题, 也称得上科班出身. 但是有时候可能也是为了显示知识面宽广吧, 把不相干的概念堆砌在一起到了没有边际的程度了. 比如第九章, 完全就是软件工程领域的最最基本的知识, 从软件工程到GOTO大战, 再说到 Ajax 和 Google 的崛起, 这之间的逻辑线就完全是天马行空了. 任何一个称得上关心现代开发技术, 或者科班出身的开发人员, 对于书里面提到的这些琐碎的事情, 都是烂熟于心的. 概念的过分堆砌和琐碎事情的描述至少占了这本书的1/2. 幸好这本书是值得读很多遍的, 再次阅读的时候, 跳过去就好了.

如果这篇书评能有副标题的话, 我愿意写上: “五个月的时间, 三遍阅读, 三十二页的读书笔记和无数的批注, 以及一个萦绕在脑海中的思考: 如何打造卓越的软件?” 这就是我这个读者的故事. 这本书国内的翻译是韩磊老师做的. 他翻译做 “梦断代码“. 我更加喜欢 “梦萦代码” 这个名字 (Dreaming 翻译成梦萦, 意音俱佳).  当年希尔伯特被问起, 五百年后若是他重回世间, 第一件事情是什么. 他说, “我要一定问黎曼猜想有没有被证明”. 相似的是, 宋朝僵卧孤村壮志未酬的陆游, 当年眼见收复中原无望, 梦里恍惚见到铁马冰河,  一梦醒来后对子女说: 王师北定中原日, 家祭无忘告乃翁.  无数的开发者梦萦代码, 一梦醒来, 就想问一个问题: 软件工程有没有找到银弹? 这个银弹之梦, 打造卓越软件之梦, 会一直萦绕着开发人员, 直到永恒.

情系软件, 梦萦代码

几天前, 偶然看到 SUN 公司 CTO Greg PapadopoulosIT 产业红移学说. 慢慢思考, 做了很多笔记, 有几条写下来.

所谓的红移学说, 就是 IT 公司的计算能力需求是指数增长的. 这个结论没什么奇怪的, IT 产业的摩尔定律说, 每18个月微处理器的计算能力就能翻一翻了. 除此之外, 存储能力, 带宽需求等等, 都是指数增长的, 有供给就有需求,这一点原没有什么大惊小怪的.

但是仔细分析, 对计算能力需求的那些应用里, 是不是所有的需求都是同样的指数增长的呢, 不是. 那么, 有快有慢的时候, 就必然有超摩尔定律增长的和低于摩尔定律增长的. Greg 把超摩尔定律增长的定义为红移, 把低于摩尔定律增长的定义为蓝移. 并且从SUN自身角度指出, 为了让 SUN 跟上 IT 产业的发展大潮, SUN 必须找到那些超摩尔增长的子领域, 把产品销售给超摩尔的应用, SUN 才不会被摩尔定律谋杀.

Greg 的立论基于互联网带宽的超摩尔, 高性能计算需求的超摩尔和软件作为服务的超摩尔速度. 我个人的见解是, 至少目前有5+1 个领域是超 Moore 的. 第一, 复杂的关系计算的发展是超摩尔的. 比如 Netflix 的推荐系统, Google 的 AdSense 系统. 这些关系计算的增长速度, 是超过 2倍/18个月的. 处于提供这些服务的行业, 对计算能力的需求必然是超摩尔的. 因此, 他们必须购置新的 IT 设别, 更新软件效率. 第二, 海量数据的处理的发展是超摩尔的. 互联网数据目前的翻倍速度是 30%/年. 其他领域的数据也有如此的指数规律. 一般情况下来说, 这个速度是低于摩尔律的. 但是任何一家公司海量数据处理的, 都是从无到有发展起来的. 大如 Google, 也不敢说覆盖互联网所有信息. 因此在这十年中, 海量数据处理(特别是搜索, 爬虫, RSS) 将经历从无到有的过程, 其增长, 肯定是超摩尔 的. 第三, 海量带宽的发展是超摩尔的. 以 Flickr YouTube 和 Twitter 为例, 他们的带宽翻倍速度是以天为单位的. 在网络基础设施领域, 中国移动等网络服务商也曾超摩尔发展, 以此为契机很多网络设备服务商如思科华为都超摩尔发展的. 第四就是海量的交互是超摩尔发展的. 网游, 信用卡服务系统都是这方面的例子, 就不一一例举了. 第五可能不为大家熟悉, 就是海量的高性能计算. 这10年内, 世界超级计算机的发展速度是超过摩尔定律的. 在科研领域, 我们清楚的看到高性能计算的需求远远超过 Intel 每年提供的增长, 多达千个万个节点的超级计算机在美国的国家实验室已经很平常. 还有一个额外的是海量的多媒体处理, 全球的电影业, 多媒体业的发展, 横跨前面提到的五个上面, 对每个领域都有超过 摩尔律速度的需求.

这几年这几个子领域的超 Moore 发展, 造成了几个非常有意思的现象.

1 . Google 在超摩尔发展

Google 是一个及其有野心的企业. 首先, 它站在这个时代最快的超摩尔的领域, 海量数据的处理. 它同时还拥有复杂关系计算[Orkut, AdSense], 海量带宽处理 [YouTube] 和海量交互处理 [Talk, Gmail, Google Maps] 三个方面. 这个方阵中的每一个模块, 都是行业的翘楚. 各位读者应该在最近的三个月内都用过Google的不少服务, 而且使用Google 各项服务的总次数相比一年半前肯定是翻倍了. Google 的超摩尔发展和它的策略, 即整合人类所有信息并使之可用 是分不开的. Google 的心大, 舞台就大, 现在爬虫能爬到的信息, 远在人类可用信息之下. 因此, Google 为了完成目标, 必须远快于信息生产速度. 总有一天, Google 抓完了互联网, 增长速度和网络信息的速度一样了, 那Google 也就变为今天的微软了. 20年前或许一人一台计算机是最大的幻想, 谁能想到20年后整合人类信息是不是一定达不到呢. 不过 Google 是善于开创和培育新领域的公司, 因此至少在10年内, Google 依然会超摩尔发展下去.

2 . 超摩尔企业控制了整个产业的需求, 联盟的瓦解和重新结合是常态.

摩尔定律既规定了半导体企业的光明前途, 又是半导体企业的魔咒. 因为半导体的速度提高速度太快, 如果耗用半导体的应用没有发展起来, 或者在18个月内需求量没有更新一倍, 那么, 半导体和硬件制造企业就不会喜欢这样的公司. 想当年 Windows 如日中天的时候, 平均三年就推出一个新版本, 而且每推出一个版本, 能支持前一个版本的硬件都几乎不能完美支持后一个版本. 因此, 操作系统和应用软件在超摩尔发展, 反过来拉着 Intel 超摩尔发展. 而 XP 以降, 微软花了 5 年时间才更新到 Vista, 而且消费者还不喜欢. 个人计算机的增长速度和操作系统的需求速度已经慢于摩尔率. 这时候, Wintel 联盟的瓦解早就是预料中的事情了. 这时候 Intel 和其他硬件制造商, 不需要依靠微软, 也能存活. 为什么呢? 原因是, 他们在 IT 产业链中, 发现了除了软件企业以外的其他的超摩尔的东西, 这个东西, 就是上面说到的5+1, 而最重要的, 是网络计算.

3 . 传统软件企业被摩尔律超过

在解释网络计算之前, 我先解释一下为什么传统的软件企业被摩尔律超过. 一个产业, 如果不是新兴产业, 那么正常的发展速度应该是和国家的 GNP 发展速度相当的. 即使在发展速度最快的中国, 目前也只能做到 120% / 18 月. 这一点, 是摩尔律所不能容忍的. 软件产业, 和其他信息产业一样, 都是社会服务业. 其终极形态, 应当是和 GNP 发展等速的. 简单的说, 有多大的经济需求, 就有多大的软件服务需求. 但是, 产业从来就不是均等的. 当一项新的需求被发现的时候, 因为没有现存的应用, 巨大的需求缺口会拉着原本应该正常发展的产业超速发展. 比如中国的移动电话, 石油产业等等, 都是如此的例子. 在 70-90 年代, 所有的行业都在走信息化的道路. Office, 电算, 电子商务, ERP, CRM 等耳熟能详. 他们的需求缺口相比于产业所能提供的, 都是几个数量级的高. 这种所谓的蓝海让任何跳进去的公司都能发财, 而且发展速度都超过摩尔律. 在此情况下, 硬件企业作为市场的支撑, 也在蓬勃的发展. 到了1999年左右的时候, 互联网的第一次泡沫前, SUN, HP, IBM 等公司靠出售硬件, 大赚了一笔.

然而, 好景不长, 2000年问题解决以后, 发达国家原来轰轰烈烈信息化的公司基本上已经完成基础设施建设了, 软件的需求一下子放缓了. 电子消费品的出现使得个人电脑发展开始放缓, 各大公司因为网络泡沫的破碎也开始紧缩 IT 投资. 软件企业的需求在源头上被紧缩. 另一个致命的打击来自开源软件. 1999 年的互联网泡沫客观上促使了一样东西的普及, 就是开放源代码软件. 传统的软件企业的定价策略是一台计算机安装算一个价钱. 在互联网泡沫时候, 网站需要以超过摩尔律的速度购买计算机, 但是这样就会同样把指数级别的投资送给微软和 Oracle. 因此, 很多公司在购置硬件的同时, 缩减软件投资, 采用免费的开放源代码的软件. 这样, 软件的需求就是一个常数, 这一点一下子拉开了硬件商和软件商地位上的差距. 虽然互联网产业发展了, 软件公司却没有跟着超摩尔发展. 等到微软和 Oracle 等传统软件公司回过神来的时候, 超摩尔的互联网已经把他们扔在了车轮之后. 那么, 应对开源大潮和超摩尔的网络计算, 硬件公司在做什么呢?

4 . SUN, IBM 等硬件提供商放下身段生产平民硬件

如果我们观察从 99年到 2007 年, 这些硬件行业的大哥大做了什么, 我们就能清晰的看到一个个放下身段的巨头. 我们知道, 超摩尔的互联网必然以超过摩尔律的速度需要硬件. 哪个硬件商能赶上此大潮, 就能做弄潮儿. Intel 芯片商要做的, 是提供更加强力的处理器; 存储商做的, 是开发大容量的存储器. 而 SUN 和 IBM 这些系统商来说, 选择就是从阳春白雪走向平民硬件. 当年的 IBM 和 SUN, 固守自己的 RISC CPU 生产线, 投入大量资金开发专有 UNIX 操作系统. 昂贵的售价使得超摩尔的企业不愿采购他们的系统. 此时, 要不固守阵地, 要不另找出路. 我们看到, 他们都开始另找了出路: 出产便宜的基于 x86 架构的硬件, 支持开源的基于服务收费的软件. 他们知道, 只有把硬件做便宜了, 超摩尔速度才能眷顾他们, 否则出路就是 SGI 和 DEC. 只有把软件开源免费送出去, 超摩尔的软件需求才能最后通过软件服务的形式让他们受益. 而一个机器一份拷贝的那种传统软件商业模式, 已经不再实用. 短短几年内, IBM SUN Novel 包括苹果, 都拥抱开源, 拥抱 x86. 我想这不是没有原因的.

5. 如何抓住超摩尔律, 投入新兴 IT 市场?

A. 硬件

硬件厂商在这场互联网浪潮当中扮演了极其重要的角色. 首先, 他们联合了新兴的开放源代码社区, 透过降低了硬件系统的总体成本. 因为此, 硬件商既不需要自己投入大量经费研发软件并把成本转嫁给新兴产业, 又能避免自己口袋中的利益流向传统软件企业. 而网络服务商, 消费类电子制造商和设备提供商, 这些年都是活在超摩尔里的. 如 CISCO, 中国移动, Apple 都是时势造就, 不发财都不行. 当然, 硬件的制造需要较大的投入,除拥有一流的技术外, 尚需要一流的设计和制造. 目前这两点在中国都不具备.

B 软件作为服务/ 软件作为基础设施

前面已经提到传统软件企业, 比如微软, 正在被硬件厂商, 软件服务商, 开源社区合力绞杀. 那么, 做软件就不赚钱了么? 非也. 既然软件已经变成基础设施和服务, 那就应该发挥基础设施和服务的作用. 软件作为服务, 如 Gmail, Facebook, 如 Amazon 的 SimpleDB, 如 Google 的 Documents, 都作为某种服务提供. 前面提到, 因为软件不能按照一台机器一台钱卖掉了, 所以落后于 摩尔律. 虽然这么说, 实际上网络应用对软件的使用需求依然是超摩尔律的. 因为超摩尔的速度造成计算机数量的增长, 系统越来越大, 很多硬件瓶颈就随之出现. 这时候, 传统的单机软件无法解决所有的问题, 必须有一个支持大系统的软件系统出现. 作为提供给网络计算系统的软件, Google 是自产自销, 自己解决基础设施问题. Amazon 和 SUN 则是提供如 EC3, SimpleDB 等工具, 以便把超摩尔的需求转化为自身产品的超摩尔发展. 开源社区也分立出很多创业公司, 提供一揽子的软件解决超摩尔的方法.

我个人看好的是作为软件咨询和软件服务的服务提供商. 机会可以在提供海量信息处理系统, 提供海量带宽处理系统, 提供海量关系处理系统等方面入手. Amazon 的经验让我们看到, 这些软件服务都是可以从系统应用中剥离分立并且产品化的. 在面向互联网的软件服务方面, 我挺看好银杏的.

结尾语: 写完这些, 突然悟到, 所谓的蓝海, 不就是一个超过行业平均水平的新兴子产业么. 想要不陷入和 GNP 一样发展速度的的红海, 就要找到超过行业发展速率的新兴子产业.

[Disclaim: It's evil, don't use it unless you are fighting with some even more evil software.]

Short Intro. [Skip it if you don't know much about OS or aren't interested in the technical detail ]

As you might know, every program on Linux system runs on the kernel instead of directly contacting with actual machine. For modern operation systems like Linux, BSD(Mac) and Windows, a mechanism called system call is used to request the system resources via operation system so that operation system has the full control of all programs. In brief, when user program need call a function in library, e.g. print in stdlib, library function forwards (usually library function is a light-weighed wrap of the system call) the request to operation system. Since the system is highly hierarchical and user program is built on the top of libraries and OS kernel, it’s possible to insert some layers in between program and OS to intercept the request. Don’t panic about the nerdy name. Actually this strategy is commonly used on Window platform in anti-virus software as well, because anti-virus software want to monitor every system resource usage for any program and prevent the malicious resource requests.

Here, we simply want to intercept a system call named “TIME” so that every time a program request the current time (so that it can verify whether the licence has expired), we feed the program with a fixed (fake) time. By fooling the program around, you can literally use a program forever. God, doesn’t this mean I can use all software forever? The bad news is for some OSs like Windows, it’s very hard to do system call interception as all the APIs are undocumented and software might have other ways to prevent this. The good news is lots of software on Linux and Mac are simply reading system time. Actually, only top developers and Microsoft partners know how to do system interception. However, for Linux, since the system itself is open source from bottom up, there is no way to prevent such kind of interception (Now you know why some software companies don’t like Linux :).

Approach 

On Linux, tons of methods are around. Here I just introduce three of them briefly under the assumption that you don’t have the source code of that software. [Otherwise you can just modify the source code]

Method 1: Intercept library call in linking time.

Sometimes you have a library (A) that can be used as a part of your program and you want to intercept the library call of that library (A). The best way and the easiest way is to write a fake function and link it in the compile time. This method is totally harmless to your system and very neat. If you can do some modification in makefile, then this procedure is totally transparent to both developer and user.

Method 2: Intercept system call in the run time

If you’ve already got a execute program, then there is no way to intercept the system call in compiling time. To intercept the system call in the run time, there are two ways. The first approach is putting the target program in a designed container. Typically, a container fork/create/call the target program as child process. Since in OS, parent process has accessibility to the child process, it can intercept syscalls easily via ptrace toolset. The second method is to hack the kernel, namely, to tell the Linux kernel to response syscall in a certain way. Since now Linux supports kernel module, a very convenience approach is to compile a program as kernel module and install it on the fly. However, this method is less flexible then the previous method as now all the syscall are intercepted, even for system calls from other programs. [Sure, you can restrict the module only applicable to a certain process via a pid comparison, but then you need to feed the kernel module with PID, it's awkward ]

In my implementation, I use ptrace/container method. I’ve tried kernel module method but failed as there were not so much well-formed documents on Linux 2.6 kernel.

Here is my code, it’s self-explanatory, have fun with hacking. [Download the C file]

/* Faketime wraps a user program and feed it with user-specified fake system time
   so that it can be used forever without any "licence expired" problem
	 
    Copyright (C) 2007 Eric You XU, Washington University ( youxu [@T] wustl.edu ) 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 

*/

#include <sys/ptrace.h>
#include <asm/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <linux/user.h>

/*
Register layout defined linux/user.h, but actually in 
 asm-$(arch)/user.h
struct user_regs_struct {
        long ebx, ecx, edx, esi, edi, ebp, eax;
        unsigned short ds, __ds, es, __es;
        unsigned short fs, __fs, gs, __gs;
        long orig_eax, eip;
        unsigned short cs, __cs;
        long eflags, esp;
        unsigned short ss, __ss;
};
*/

/* Note that EAX now is RAX in x86-64
 	we can also find the actural offset for any register
 	from <asm-$(arch)/ptrace-abi.h>
#define RAX 24
*/

#define ORIG_RAX 44
/* ORIG_RAX stores the number of syscall */

#define SYS_TIME 13
/* Machine specific syscall number is defined in 
	unistd.h */

#define back_to_future 1175737392

/* Time is stored as a long interger in C, you can get 
	current time via time(NULL). Thus, it's very easy to 
	get a long integer denoting some time in the past. 
	
	Python/Java can also be helpful in figuring this out 
	
	If you don't know how, just keep in mind that 
	Dec. 1, 2007 is about 1196476452. 
	One day interval = 60*60*24 = 86400 [Time flies fast]
*/

char* host_program = "your program name here";
char* arglist = "your program fake list here";
/* Make modifications for these two lines, then 
	compile it via
		gcc faketime.c -o faketime
	use it via 
		./faketime
*/

int main()
{   pid_t child;
    long orig_rax, eax;

	 struct user_regs_struct regs;
	 int status;
    int insyscall = 0;
    child = fork();
    if(child == 0) {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl(host_program, arglist, NULL);
          }
    else {
       while(1) {
          wait(&status);
          if(WIFEXITED(status))
              break;
          orig_rax = ptrace(PTRACE_PEEKUSER,
                     child, ORIG_RAX, NULL);

          if(orig_rax == SYS_TIME ) { /* Intercept SYS_TIME syscall */
             if(insyscall == 0) { /* Syscall entry */
                insyscall = 1;
             			  }
         	 else { /* Syscall exit */
					ptrace(PTRACE_GETREGS, child, 0, &regs);
				   	/* We can also use ptrace(PTRACE_SETREGS, child ,RAX, &back_to_future); 
						but it doesn't work. There might be some tricky here */
					regs.eax = back_to_future;
					ptrace(PTRACE_SETREGS, child, 0, &regs);
            	                 }
          } // End if with SYS_TIME 
       ptrace(PTRACE_SYSCALL, child, NULL, NULL);
        }
    }
    return 0;
}

我一直很推崇使用 Python, 但是推崇不是迷信, 今天就说说几个 Python 相对于 Java 和其他语言的缺点, 以及初学者要注意什么.

1. 文档不完善, 最少惊奇原则不适用

很多 C++ 和 Java 开发者都知道, MSDN Java Doc 堪称技术文档的典范. Java 更是充分体现 Knuth 提出的 Literate Programming 的精华, 用一个专门的叫 javadoc 的工具自动提取程序元信息, 生成交叉引用的 JavaDoc 文档. 任何复杂的类, 框架, 在javadoc 的统一管理下, 可以生成非常漂亮的文档. 如果再配合设计模式中的 facade 模式, 一个复杂的框架很简单就可以上手. 这方面, Hibernate, Lucene 都是优秀的典范. 当然, 交叉引用的文档也有明显的缺陷, 就是极其庞大冗长, 因此 JavaDoc 和 JDK 是独立的两个部分. MSDN 更加绝了, 直接出几张光盘, 你爱装不装. 虽然冗长, 好处就是在面向对象的汪洋大海中顺着链接, 永远不会丢失. 而 Python 的文档还远远不够细致. 所有的对象方法, 只是简单介绍. 如果一个对象方法返回另外一个不熟悉的对象, 则必须手工定位新对象的位置, 而不能顺着文档继续前进. 为了搞清楚一个包, 完成一个小任务, 可能要多次搜索文档. 对于黑客级别的用户来说, Python 文档言简意赅, 喜闻乐见, 只要记住了每个对象的每个函数, 写程序如行云流水. 对于初学者和用 Python 写稍大项目的人来说, 这就是最恐怖的噩梦. 因为以介绍单个函数功能为组织方式的文档, 对于刚上手的用户来说, 很难顺着对象调用关系,通过文档指引完成任务. 即使著名的框架比如 django, 也缺少交叉引用的细致的文档. Python Library Reference 只能说是一个完成了 50% 的README. 在这种情况下, 除非对一个框架很熟悉, 否则这个框架对于初学者来说简直是”惊奇的噩梦” — 一般用户只能看例子然后顿悟到: 哦, 原来是这样用的.

除此以外, 因为语言的动态性, 使得 IDE 能给的帮助特别少. 对于 Java 程序员来说, 很多时候都是靠着 Eclipse 的提示去选择正确的方法调用. 而因为 Python 的动态性, 变量的类型到运行时才能确定, 因此能够给的提示相对变少. Python 既是动态的, 又是强类型的, 所以代码的阅读者必须要一步一步手工追踪中间对象的类型, 并且查阅对象方法文档, 才能搞清楚到底一段代码做得什么. 而这一切, 偏偏是没有 IDE 帮助的. 对于开发者来说, 如果不熟悉对象的可用方法, 用 Python 开发也会事倍功半, 因为要常常停下来去 Google.

当然, 文档不够完美, IDE 不支持的现状, 根本是因为 Python 的动态性: 谁让 Python 返回值不规定类型呢? 我的建议是, A. 对于现成已知的一些对象方法上, Python 社区应该注意文档的维护. B. 对于不熟悉对象的可用方法这个问题, 要买一本 Pocket Reference. 或者下载一些 cheet sheet. 在刚接触 Python 或者 刚接触 Python 框架的时候, 一本薄薄的可供快速查阅所有对象的所有方法的小书, 无比重要. 这也间接的说明, 和 shell 脚本, 正则表达式语法等一样, 脚本语言必须要很熟悉才能发挥魔力.

(– 附实例,对Java 不熟悉的可直接跳过: 我曾经做过一个游戏, 这个游戏类似于推箱子, 其中的一个核心是要把多幅带alpha 通道的图像拼成一个图像, 然后显示在窗口中. 打开 API Doc, 简单的搜索一下, 发现有 java.awt.Image 类. 不过这个是抽象类, 没关系, 文档下面就明明白白写着两个可以直接用的类, 一个是 BufferedImage, 另一个是 VolatileImage. 看上去 BufferedImage 更像, 于是点进去, 正是想要的. 看看构造函数, 一个空白的图像就构造好了. 顺着方法向下看, 看到 createGraphics() 方法, 返回 Graphics2D 方法, 说明中写着这个可用做在 Image 上绘图, 正是想要的, 点返回值, 是 Graphics2D, 有方法叫 drawImage. 任务完成, 轻松自然.

回到 Python. 我遇到过一个需求, 是用 Python 提取所有一个 Zip 文件的注释. Google Zip+Python 定位到 zipfile 模块. 找到 ZipFile 对象. 读文档没发现可以往外读注释, 只能碰运气, 开一个解释器尝试. 看上去 getinfo 可以. 因为没有交叉引用, 只能搜索ZipInfo 这个对象. 终于看到一个方法是 comment. 不知道类型, 仍然需要自己在解释器下先 type 测试一下, 才能在程序中写下一行完整的语句.–)


2. 文档上有, 但是你不能用

我用 Ubuntu/MacOSX系统, Python 是自带的. 以为万事大吉, 实际上, 因为 Python 所有的库都是运行时装载的, 所以有些库在有些系统上是缺失的, 而文档则是所有标准安装的超集. 因此即使文档上有, 你也没法用.

这个问题对于 Java 程序员和 .Net 程序员来说是不能接受的. 对于任何一个良好设计的 SDK, 文档和实际可用的应该具有同等的覆盖面. 在 Java 中, 有一个包就是有, 没有就是没有, 绝对不会出现标准文档中提到了, 实际中却不可用的情况. 对于可选包, 用户也能理解需要下载模块. 而对于 Python, 开发人员很难理解为什么标准文档中提到了, 实际中却缺这个组件缺那个组件. 一个潜在的伤害就是, Python 的跨平台能力没那么强. 试想, 开发的机器上有一个模块, 而用户没有. 而标准文档偏偏又认为这个是一个标准组件, 那么, 在装载的时候,找不到这个组件了, 让用户根本就无从下手. 我的建议是 A. Python 应当有更好的 import 报错机制, 对于不是每台机器都安装的模块, 应当给出平台中立的更加友好的帮助信息, 而不是简单的 ImportError. B. 应当在 Python 语言中支持更加强大的内省机制, 对于系统当前可用的包, 应当有一个全局的缓存. 这样, 用户才能知道哪些可用哪些不可用, 不可用的时候怎么办. C. 对于用户来说, 只能求助于Google 或新闻组, 或者从 Python 源代码中找线索. 一个较好的办法是在 Python 源代码上 执行 make test, Python 会汇报哪些模块已经编译, 哪些模块被跳过了.

(–实例: 前几天想写个程序抓Google Reader. 认证的时候要用到 SSL. 因此找到 Python 文档, 照着例子 import urllib2, 然后直接连 Https 服务器. 出错了, 原因是没有 ssl 支持, 不能访问 https. 所有的 Python 在线文档都说, 这个是标准模块. 对呀, Python Library Reference 里面都有的模块, 怎么能有问题呢? Google 搜索也帮不了什么忙, 因为 Python 是跨平台的, 不同的平台有不同的解决方法. 所有的在线帮助只能提示到 _ssl.o 这一层. 于是, 只能翻看 Python 源代码, 在 Modules/_ssl.c 中看了一圈, 发现需要 openssl/ssl.h 等文件, 这才意识到, 需要安装 openssl 和 libssl-dev. 在 Ubuntu 上, 同样的问题还存在于 sqlite3 模块, tk 模块 和 bz2 模块. 分别需要安装 libsqlite3-dev, tk-dev 和 libbz2-dev, 再重新编译 Python 系统. 使用 make; python setup.py install 来安装 –)

3. Python 是胶水语言, 用是简单的, 模块开发是痛苦的

Python 用到一定程度以后, 一个自然的需求就是把其他语言, 特别是C语言写的库包装成 Python 库. 这个问题说复杂也不复杂, 在 Python/C Reference Manual 中也提到了所有必要的信息. 只是层次太细. Boost.Python 有更加好的实现, 不过需要对 C++ 模板很熟悉才行. 当然, Python 的模块开发的痛苦比起 JNI 复杂的 JNIEnv 对象, Lua 的 基于栈的 Push Pop 已经是大大轻松了. 相比较于 Ruby 的自动生成 makefile, 也算各有千秋. 只是模块开发设计到的技术细节太多, 要对 Python/C API 了然于胸才行. 因此我的建议是: 不要从轮子造起. 如果有这样的模块, 就用. 如果没有, 而又非要用特定功能的模块不可, 那就暂时先不要考虑用 Python. 相比较于其他更加成熟的语言, 比如 Java, Python 的可用模块还是显得少了一点. 因为 Jython 的出现, 这个情况已经大大缓解. 不过工业界历来喜欢用最稳定的东西, 所以可以想象, 多少 Web2.0 公司是为了用 Lucene 才用 Java 服务器的, 而每天又有多少新的公司投入 Java 的怀抱 :)

A little bit history

Being in the academical area for years, one of my dreaming tool is an online collaborative paper reader . You can say it’s YouTube with documents, or Digg with pdf files–name actually doesn’t matter. The ultimate goal for this system is to support collaborative document reading/organization for professors and Ph.D. students. Since we now have Ajax (more interactive than before) and Flash(as powerful as PDF), I would expect an light-wighted solution. arXiv and Citeseer do really good jobs in storing and linking all the documents, but the ultimate goal for me is to read.

On winter 2006, I expanded my ambition to a larger project: read the f source code and read the f books (Here of course the f word means fine :). I launched my domain name rtfsc.org [now redirected to Apache.org]. Our team members thought that rtfsc was easier than rtfm because we only need to deal with text information instead of PDF file. However, we set ourselves on the wrong track. It turned out that if we let the users upload and update their codes all the time, we have to implement a Subversion or even a Sourceforge. For us, an online code-reading community without code management and code search is terrible, but we don’t have that much time to finish all these ambitions. The other obstacle comes from my lacking experiences of Ajax development. Anyway, at last, we gave up. We stopped the development after Christmas. On that week, I was the person of the year 2006.

New motivation

I have been thinking about this project for nearly one year without any action. Lots of similar projects were there during this year, for example, flashpaper, edocr and scribd. But none of these are my dreaming tool. I took a retrospect about our project again and found that nobody actually wanted to do that except us because they didn’t actually understand our requirements. Their goals are usually becoming the next DocTube thing, while my goal is to have a handy system open for academical research. Also, I found some open source tools that were really inspiring. These make me interested in the project again.

Technical aspects:

1. PDF to SWF is not so that hard, there is an open source tool: PDF2SWF.

2. I thought that I can’t control the converted SWF as there is no API exposed as Flashpaper, but I was wrong. Actionscript can control it directly. What I need to do is to learn Actionscript, which is not so that hard.

3. I was thought that Flashpaper use some undocumented APIs which we could never know, but I got SWFmill today and now I can study the code of Flashpaper. [Disclaimer: I didn't say anything about how to decompile flashpaper or violate the EULA of flashpaper]

4. I thought Ajax + Flash development was hard and had a very steep learning curve, but again I was wrong. On hacking Google Finance these days, I found the Flash Ajax Integration Package, which is very handy to use for some newbie like me without any Ajax/Flash developing experiences.

Proof of concept

Here is a proof of concept product. I convert my PDF version CV to swf and then combined it to a controller I wrote based on the default controller provided in PDF2SWF. (This CV has two pages, you can proceed to next page by click the red arrow)

cv.swf

Final Word

What I need is an sophisticated viewer which can be as powerful as flashpaper can communicate with other Ajax components on that webpage. So for developers, if you find this idea interesting and you are good at Ajax/Flash, feel free to take this idea and do it, as I might not have time to do it or it will take me a long time to finish it. I desperately need an Ajax style flash-based document reader with inline comment support. If you can do thing and get your own startup based on the idea, my only request is letting me be your user.

BTW, if there was some lessons in this project, I would say, choose the handy tools before you get start.

[Keywords for search engine: Ubuntu dell vostro 200 live cd can't boot harddisk]

Make sure add “irqpoll” as the kernel parameter, i.e. :

title           Ubuntu gutsy, kernel 2.6.22-12-generic (recovery mode)
root            (hd0,0)
kernel          /boot/vmlinuz-2.6.22-12-generic root=UUID=78dea514-8cc8-40a9-9e51-13c359bc681b ro  quite splash irqpoll

initrd          /boot/initrd.img-2.6.22-12-generic

These are cited from here.

When the PC boots up, you will see the Grub countdown, which is set to 3 seconds by default. Press “Esc” to intercept this countdown and go enter a Grub menu. Then

  • Press ‘e’ to start editing.
  • Scroll down to the “kernel…” line. The is the line that tells Grub which kernel to boot with and the parameters to be passed to the kernel when it boots are placed at the end of this line.
  • Press ‘e’ again to edit this line.
  • Move to the end of the line. You will see any existing parameters and can add other new parameters to the end. [Add your irqpoll here]
  • Parameters are separated by spaces and are mostly either a single word (e.g. nolapic), or an equation (e.g. acpi=off).
  • Once you have added the parameter to the end of the line, press Enter to accept the editing.
  • Then press ‘b’ to boot using that kernel and those parameters.

Then, go here to see how can you modify the parameter permanently.

Here is a quite funny picture I found today on digg. It is called the Best Phishing Email, Ever. Despite the funny nature of this hilarious letter, have someone really noticed that in this letter, we have G mail or Gma il instead of Gmail? (one extra blank in between the letter “G” and “m”, or “a” and “i”).

gmail_scam.jpg

Last year when I was interviewed in Google, I reported this bug to Gmail team, (Sadly to say, they didn’t take it very serious, as it’s still there now). Maybe they can argue that it’s a feature, but let me take five minutes to explain why is it. (I guess it’s fairly simple and straightforward).

Let’s start from a little background about HTML. We all know that when we send colorful texts like “Google” via gmail, the email format is actually HTML. Here is a quite important aspect about HTML: An HTML user agent should treat end of line in any of its variations as a word space in all contexts except preformatted text.(Page 20, RFC1866) That is to say, a cstring in HTML source file like “a\nb\nc” will have the redering output identical to “a b c”. Therefore, it’s really confusing that we have to use <br/> to make a newline within a paragraph in HTML text, and “\n” is equivalent to a white space in most of the cases.

Now let’s go back to Gmail. When I send myself a piece of email with a colorful string “Google” via Gmail, I got “Googl e“. Via viewing the source of the HTML, we can actually find that there is a “\n” in between those letters. For example, this is a piece of HTML(javascript) excerpted from Gmail source related to this colorful Google:

\u003cfont color\u003d\”#000099\”\>G\u003c/font\>\u003cfont color\u003d\”#ff0000\”\>o\u003c/font\>\u003cfont style\u003d\”background-color:#ffffff\” color\u003d\”#ffcc00\”\>o\u003c/font\>\u003cfont color\u003d\”#3333ff\”\>g\u003c/font\>\u003cfont color\u003d\”#33cc00\”\>l\u003c/font\>\u003cfont color\u003d\”#ff0000\”\>e\u003c/font\>\n \u003c/div\>\n\u003cdiv\> \u003c/div\>\n\u003cdiv\>

Here \u003c is “<”, \u003d is “=”, without the bold “\n” in this line, the result should be “Google“. So, why we have an extra “\n” here? Who did this trick? The answer is simple: “Gmail”. For some reason, Gmail breaks a long line in HTML source file into multiple lines and sends the email out (I haven’t figure out the rule that Google uses to break lines in HTML source file). By doing several trival experiments like sending mail from Gmail to Hotmail and vice versa, I am now pretty sure the problem is caused by Gmail automaitc line breaking strategy. That is to say, Gmail client automatically inserts a newline(“\n”) symbol in the HTML source file and causes this “visual bug”. Actually this bug is quite easy to fix, for instance, just break the line at the first blank after the label name, for example, like:

<span
style=”color: rgb(255, 0, 0)”>red</span><span
style=”color: rgb(0, 255, 0″>green</span>

instead of say

<span style=”color: rgb(255, 0, 0)”>red</span>
<span style=”color: rgb(0, 255, 0″>green</span>

or

<span style=”color: rgb(255, 0, 0)”>red</span><span style=”color: rgb(0, 255, 0″>
green</span>

The first generates “redgreen“, and last two give “red green

BTW, here is a nice tip for interviewees: love your prospective employer, love their products. Eventually, you would have a very nice understanding about their culture and products. All companies are willing to hire guys who actually love their culture and products (and can even find bugs :).

PS: in preparing this article, I found that Gmail team has secretly updated the text format system from using plain old <font> to fancy (and elegant) XHTML+CSS <span>.

PS2: http://www.opinionatedgeek.com/dotnet/tools/Base64Decode/Default.aspx is a nice online tool for decoding the base64 format.