巴别塔上的雇工


全是Beta版
5月 31, 2006, 12:47 下午
Filed under: 工作心情
本地机器上的有:
Beta版的Live Messenger
Beta版的IE 7.0
Beta版的Office 2007
 
网上的有:
Beta版的Live Mail
Beta版的Gmail
Beta版的Yahoo! Mail
 
工作中的:
Beta版的Live Safety Center
 
处处是Beta版,现在守住最后的防线,Windows Vista在摘掉Beta的帽子之前且先不用它
 
 
 


CPU的Instruction Pipeline给软件带来的一些后果
5月 30, 2006, 1:11 下午
Filed under: 技术体会
现代CPU广泛采用指令管道(Instruction Pipeline)技术,也就是CPU可以同时执行多条机器指令。从编译汇编的角度来看,机器指令似乎是执行的最小单位了,但是从CPU内部角度看,每一条机器指令还有的分。一个机器指令可能要跨好几个时钟周期,就是因为可能涉及好几个动作,这样,它后面的指令没有必要等着它全做完了才进入CPU,完全可以在前面的指令还没有执行完的时候就一头扎进CPU的pipeline。这是一个硬件级别的问题,对软件几乎是透明的,但是还是有一点影响,下面调侃一下下:p
 
我们的印度同行写的code是很有特点的,如果需要几条语句重复执行四遍,他们不会用上一个循环,而是Ctrl-C然后Ctrl-V三次,他们会说这是为了看得清楚,当然,以我们很多国人的观点,这么写有点弱智。不过,这种写法可能无心插柳导致执行效率比用上一个循环要快,为什么呢?因为Pipeline技术最害怕跳转了。想想看,如果指令一条顺的下来,pipeline效率很高,但是如果碰上处理一个跳转指令,CPU需要抉择一下,读取的下一条指令,应该从哪个branch上选择呢?先驱者尝试过两条路都选择,然后抛弃一条路,但是实践证明这样行不通,还是回到必须二选一的道路上来,也就是只能先选择一条,如果选对了最好,如果回头发现选错了,算这次倒霉,只好重新load另外一条branch的指令,pipeline带来的好处全部失去了。所以为了优化程序,有时候会故意使用重复的语句,有时候循环100次不是for(int i=0;i<100;++i),而是for(int i=0; i<100; i+=4),循环体里面语句重复4遍。下次不要再笑话印度同行了,别管他们知不知道这一点:)
 
CPU害怕跳转指令还影响编译器对循环语句的处理,以C/C++为例,我们写循环可以用for(;;){ },可以用while(){…},最不常用的是do {… } while(至少我是很少用),但是编译器在优化的时候,都会把循环编译成do-while的形式,因为编译器看到循环就假设这个循环怎么着也要循环多次,如果是while(){},那么进入循环体要有一个判断跳转,结尾也有一个跳转,循环n次,就要经历2*n次跳转指令,而do-while,进入的时候不要跳转,结为一个判断跳转,循环n次,只要经历n次跳转指令。这是under-the-hood的细节,如果有一天需要读优化后的汇编或者机器代码,不要太吃惊,除此之外,只能作为程序员的娱乐了:)
  
 


可以这样使用Blogger
5月 29, 2006, 1:01 下午
Filed under: 技术体会
 
上面URL中的文章介绍了让Blogger上的内容呢能够在国内访问的方法,用blogger.com后台编辑,用一个国内可以访问的支持ftp的网页空间做前台,不过我找了一下似乎也没有合适的空间:)
 
Blogger不支持TrackBack,只好直接把这个Post的URL直接贴在这里了,文中提到的用haloscan提供TrackBack的方式似乎也有局限,没法提供TrackBack URL,我从space上是没法引用他了。不明白Blogger.com为什么要特立独行不支持TrackBack,不过除了这点之外,Blogger.com还是不错,主要是能够任意编辑HTML。
 
现在我用的msn space虽然容易用,但是渐渐感觉不能随心所欲的添加HTML有点不爽,虽然可以用CustomHTML Powertoy加上定制HTML的块,但是只能加一个,用一点小花招可以加多个,但是内容都是一样,所以加多个CustomHTML块也没有意义。而且CustomHTML里面无法添加script tag,这样Technorati提供的Blog Embed就没法添加到Space上了。不过反正technorati也被封了,添了国内的朋友也用不了,作罢吧~~
 
 


Windows的Message和Thread是有内在联系的
5月 28, 2006, 9:02 上午
Filed under: 技术体会
以前主要接触的是QT,虽然也是GUI的东东,但是和Windows还是不大一样,关于Message处理的区别,直到最近才弄清楚区别。
 
凡是GUI的东西肯定都是Event/Message-Driven的(QT里面叫Event,Windows里面叫做Message,下面都用Message称呼),用户对键盘鼠标等输入设备的动作直接或间接的产生Event,由GUI系统把这些Message送给每个应用程序来处理,应用程序肯定有一个Loop,可以认为是一个和进程生命一样长的循环,不断的接受Messag然后对其进行处理。做这个Loop的线程在某一个时刻只能处理一个Message,处理得再快也是有可能在这期间有新的Message出现,所以需要一个queue作为缓冲区来存放这些Message,这就产生一个问题,这个queue放在什么地方?
 
在QT中,每个进程只能有一个queue,而且只能有一个GUI Thread,但是并不是只能有一个Loop,为什么呢?因为在GUI Thread显示一个Modal Dialog的时候,实际上就是执行这个Dialog得Loop,这个Loop只处理这个Dialog的Message和这个Dialog之外的显示之类的Message,这样就产生了Modal的效果,这个Dialog之外的UI部分不会响应鼠标点击这样的事件,而同时又能够正常显示。QT中的Message和Thread是没有什么内在联系的,记住一个进程只能有一个GUI线程就好了。
 
在Windows32系统中,Message和Thread是有内在联系的,别忘了,QT这样的GUI系统不是OS的一部分,而Windows的GUI支持是OS直接支持的功能。Windows系统自己就有一个Message Queue,输入是设备驱动器或者从用户空间发过来的,系统会把这些Message发派到对应的Thread去(注意,是Thread不是Process),就和邮政局的作用一样,把Message发给每家每户的邮箱里去,每个Thread有一个自己的一个邮箱(Message Queue),里面装载的是属于他的Window应该处理得Message,那什么样的Window算是“属于”一个Thread呢?很简单,一个Window属于一个Thread当且仅当这个Window是在这个线程上创建的。每个线程都会有自己的Loop来进一步把Message分发到各个Window上去,每个Window都有自己的Procedure来处理Message,如果处理不了就由系统缺省处理。
 
要注意的是,因为UI线程上跑着Loop,不能在处理一个Message上耗费太多时间,不然用户就会明显感觉界面反应迟钝,这样需要做一个费时操作的时候,需要从UI线程上spin off一个线程出来工作,让UI线程继续他的Loop。
 
实际中,Windows并没有给每个Thread都分配一个Queue,只有一个线程调用了USER或者GDI系统函数的时候才自动分配一个,这点程序其实感觉不到。
 
可以看到,Windows容许一个进程拥有多个GUI线程,这有没有必要呢?QT每个进程只有一个GUI线程,也工作的不错,而且大多数Windows应用程序也只需要一个UI线程就足够了,但是我觉得Windows的这个特性是有作用的。QT要求程序员不能在非UI线程上做UI操作,否则后果是unexpected的,这样还是带来了一些麻烦,首先到QT3.x为止,没有函数能够判断当前线程是否是UI线程,如果我们需要开发的一个DLL或者说是Shared Libray需要使用QT的话,我们到底应该直接调用UI函数,还是Post一个消息给UI线程让它去处理呢?没有办法判断,这很不好。
 
据说微软还在参与OS/2的开发的时候(还记得OS/2吗?),系统维护的是Serialized Message Queue,也就是说所有的Message都是进入一个FIFO队列,如果一个Message没有被处理完,那么后面的Message也就没法处理。这样的设计看似很不错,想想如果Focus从一个Window A转移到另外一个Window B,如果不FIFO的话,在键盘上的输入可能会进入到不正确的Window,但是有一个问题,这样,只要有一个程序设计得不好,在处理某个消息的时候费很长时间,那么这样一个进程就会卡住整个系统,所以最后在开发自己的Windows NT/98时,还是选择了更进一步,采用Deserialzied Message Queue,让Message-Window和Thread挂钩。
 
 
 
 


Technorati反应还是很快的
5月 27, 2006, 7:53 上午
Filed under: 技术体会
昨天写的How Debug Works,没过几个小时去www.technorati.com上搜索"How Debug Works",第二篇就是,标题全都匹配上了,不搜中才有鬼呢:)
 
最近用WinDbg,有了些对Debugger工作原理的疑问,用了search.msn、google、technorati等搜索工具,都没有找到满意答案,最后参考了一下John Robbins的《Debug Microsoft .NET and Windows Applications》才知道和来龙去脉。
 
Technorati只做Blog搜索,需要检索的内容比网页搜索要少而且简单,但是大部分时间搜不出我想要的东西,也许这要怪Blogger们写的东西没有做SEO,所以没有让Technorati排在前面:)
 
 


Everyday Is A Battle
5月 26, 2006, 1:08 下午
Filed under: 工作心情
工作很忙,每天就和打仗一样,事情似乎永远做不完。
不过很充实,而且美国peer site的manager也是身先士卒的加班工作,我们也没有理由不拼命啊!
 
 


How Debugger Works
5月 26, 2006, 12:58 下午
Filed under: 技术体会
搞软件跑不了要fix bug,bug往往狡猾得很,只有软件运行时才能看清楚真面目,这样只是靠肉眼看静态的source code很难找到bug,需要使用动态运行时调试程序的工具,这样的工具叫做debugger,bug藏身的程序叫做debugee。不管是什么样的debugger,提供的基本功能都差不多,无外乎以下几点:
  • 观察调用栈信息(View Stack)
  • 设断点(Breakpoint)
  • 控制debugee执行(Step In,Step Out, Step Over)
  • 观察变量运行时值(view Variable)
  • ……
这里不介绍说怎么使用Debugger(How to work with debugger),而是介绍debugger的大概工作原理(How Debugger works)。
 
Debugger也是一个程序,能够让用户控制一个debugee程序的运行。对于C/C++这样的Native Code,要运行就先要编译连接成二机制文件,这样才能被机器理解从而被执行,但是这样得二进制文件又没有办法被常人理解(我说的是常人,真的有猛将能够读懂二进制代码的),Debugger要解决的首要问题就是把二进制代码和源代码联系起来。要做到这一点,需要在编译的时候产生symbol,symbol是一个编译中的概念,就是地址的标识符,函数、变量被编译之后其实都可以看作地址,所以实际上symbol可以表示组成程序得所有这些砖瓦,在编译连接之后,大多数symbol对程序运行来说没有作用了,比如局部变量的symbol,对其访问都是通过栈上的偏移来访问,所以说如果只是为了运行程序,大多数symbol都可以去掉(一些export得symbol还是要保留的),但是,如果需要debug,这些symbol是需要的,因为要靠他们起到二进制代码和源代码的桥梁作用。
 
对gcc,加编译-g参数会产生用于debug的信息;对MSVC,加参数/Zi会产生和二进制文件同名的PDB(Program Database)文件存储symbol信息。两种方式的不同是,gcc的symbol就放在二进制文件中,而PDB文件是独立于二进制文件的。我觉得后者比较好,因为这样可以发布比较小的二进制文件,同时还能用保留的PDB来debug,当然gcc也可以产生独立的symbol file。
 
对于Java,不需要symbol,因为Java source code被编译成byte code后,原来的所有变量、函数、类等信息都存在byte code里面,也就是说byte code是self-explainable的,从byte code里面完全可以恢复source code(注释除外),这也就是商用Java软件要使用obfuscator让别人反编译了也看不懂的原因。
 
好了,我们现在知道Debugger是通过symbol来联系二进制文件和源文件,虽然有众多symbol的格式,但是毫无疑问这种联系是可以实现的,这里就不列举各种格式了。
 
以Windows平台为例,Windows32 SDK中有关于Debug的一组API,Debugger就是利用这些API操纵Debuggee。Debugger可以在创建一个进程的时候指定一个特殊参数,然后这个进程就成为他的傀儡,也就是debuggee;debugger也可以动态的attach一个已经在运行的进程,使其成为debuggee。Debugger的主要任务就是一个循环,循环体调用一个函数获得Debuggee中发生的exception,这个函数是blocking的,debuggee没发生exception的话就阻塞,直到debuggee产生异常,然后这个函数返回,根据异常类型,debugger对debuggee进行一些操纵,然后继续执行,重复循环体,下面是伪代码
  do
  {
     WaitOnDebuggeeException(Exception &e)
     switch (e->type)
     {
       case X_EX:
           ….;
       case Y_EX:
           ….;
       ……
     }
     ActivateDebugee();
  }
  while (isNotFinished)
 
对Debuggee来说,每发生一个异常,他所有的线程都会停下来,直到Debugger在做完相应处理之后激活它。 
 
通过Windows Debug API,Debugger可以操纵Debuggee的内存空间,所以观察栈上的情况就不成问题,加上有symbol辅助,evalute任何个变量的值也不成问题。
 
有点意思的是设置断点(Breakpoint)的实现。虽然Debugger可以控制Debuggee,但是还不能直接发布这样的命令“你,执行到XXX位置停下来”,Debugger要设breakpoint还是要费点劲。Debugger可以通过symbol把源代码中的位置解析成二进制代码中的位置,在二进制代码中设置断点。断点其实就是一个产生异常的指令,在Intel X86族CPU上,就是INT 3,二进制表示就是0xCC,设置断点就是把这样一个字节放到debuggee的进程空间去,这点没问题,虽然可执行代码是Read-Only,但是Debugger可以把Debuggee的这块内存变成Writable的,把INT 3放进去,然后再恢复成Read-Only。虽然一般说的是“插入一个break point”,但是INT 3并不是“插”进去的,想一想,要是真的是“插”进去,那岂不是要后面的指令代码后移一个字节?这样既没有必要而且没有好办法实现,真正做的是直接覆盖掉断点位置的那个字节,当然覆盖之前Debugger需要把这个位置上以前的值保存下来,回头等断点执行完了,再写回去,呵呵,够复杂吧。
 
对于Step In、Step Out、Step Over这样的操作,其实还是利用了Breakpoint,不过是once breakpoint,也就是只用一次。Debugger通过symbol和对源代码的分析,确定Step之后应该停留的位置,在这个位置上设Once Breakpoint。在Once breakpoint产生异常之后,Debugger捕获异常,做完事情,在激活Debuggee之前,把之前保存的breakpoint位置的指令写回去,这样这个breakpoint就消失了。
 
相对而言,普通的断点实现还要复杂一点,为了让断点一直有效,每次在激活Debuggee之前还要做一件事,就是设置一下CPU的Trap Flag(对Intel X86 Family CPU而言),这样这个刚刚恢复的指令刚刚执行完马上又产生一个异常,Debugger重新获得控制权,这时候这个指令已经执行过了,所以Debugger又把0xCC写到断电位置上去,下次执行到这里,这个breakpoint就在这里等着呢。这样看出来了吧,普通的Breakpoint设置做的事情,比Step In/Out/Over要复杂一些。
 
其他平台上的Debugger,虽然各有特点,但是功能差不多,内部机理也大同小异。
 
 


http://dev.live.com : Cominig soon
5月 26, 2006, 12:51 上午
Filed under: 技术体会
目前还在建设中,不过在Internet上可以访问这个域名了。
 
Windows Live提供SDK让developer使用Live提供的服务。以前类似Windows32 SDK、J2EE SDK这样SDK的概念是本地使用的软件包,现在各大网站提供的所说SDK,往往指的是所谓web service这样的东西,通过http传递XML格式数据来调用和返回函数值,技术进化了。
 
 


SteveB is an influential and energetic man!
5月 23, 2006, 12:55 下午
Filed under: 工作心情
昨晚参加了SteveB来华的All Hands Meeting,感觉到他的确是一个很有煽动力、富有活力的家伙。CEO的主要任务就是要创建良好的企业文化环境,把企业推向良性循环之路,SteveB就是这么做的人。
 
不光只看到成功的领导怎么做的,也应该看看不成功的领导的语录,我收录两则:
“Samsung以为前三个季度能够超过我们就能今年超过我们,结果我们第四季度反超了,What a surprise to them!” 虽然这个领导比较失败,这段话我还是很佩服,巧妙的避开了和第一厂家的关系,而且把惨胜说成对对手的surprise,嗯,对他的敬仰犹如滔滔江水~~~~
 
-“我们的Sample Phone获得了很好的反馈,这里我要说,Thank you, Thank you”
-“但是,就我们所知道的,Sample Phone反馈很差,主要是效率比较低,怎么可能有很好的反馈呢?——你到底用没用过我们的样机?”
碰上不识相的下属,即使是善意的谎言,被戳穿了也很尴尬难受,不要以为舍得说Thank You就能够激励士气。
 
 
 


Dibert也开始搞搜索技术了
5月 23, 2006, 4:49 上午
Filed under: 工作心情
 
 
Dibert发明了一个搜索算法,但是似乎要面对一点Google的压力,嘿嘿