[《编程高手箴言》读后感]

时间:2022-02-22 03:44:36

[《编程高手箴言》读后感]

毕业也有几年了,也看了和学了不少东西,《编程高手箴言》读后感。有时也想写点什么,但总是觉得头绪很多,一直没有动笔。最近翻了翻梁先生的《编程高手箴言》,突然想写点什么,权且用读书笔记的形式写点东西。

等号上面的摘字《箴言》,下面则是笔者自己的 感想。希望大家指教,但是谩骂就不必了,谢谢。注:这一部分涉及《箴言》第一章。
在PC这个领域,现在的程序已不等于软件了。
===
现在的程序不等于软件,那么什么时候的程序等于软件呢?我想,不管什么时候,都存在有用的和没有的程序,而软件,software,在计算机领域里就应该指那些有用的程序,而不论这些程序有没有 商业化。呵呵,应此只要我们在为自己或者为别人写有用的程序,那么我们就可以说我们是在写软件了。
商业软件的功能和所要达到的目标就不是一 个人能"玩"的起来的了。这就是美国新的软件 公司没法产生的原因。比如Netscape网景是在1995~1996产生的新软件公司,但是,两三年后他就不见了。
===
所谓商业软件的功能和目标从来都没有过严格的定义,也不会有严格的定义。何谓商业软件?看发布时的代码量?看可 执行程序的尺寸?看有没有复杂神妙的算法?看有没有优良的售后服务?还是干脆就把大公司发布的东西就叫做商业软件?当然,现在在一些通用领域,一些不涉及复杂算法设计的场合,一些已经有大公司进入的场合,单凭个人的力量想要做出可以和大公司抗衡的东西确实几乎不太可能。但是,计算机科学是门涵盖很广的学科,很多分支,比如数字图像处理,视频音频处理,人工智能和机器人,等等,只要有人得到了突破性的发现,我想快速形成商业软件也非不可能。当然了,很可能这些刚出现的小公司很快就被那些巨无霸吞并了。如果稍微看看现在这些巨无霸公司的发展轨迹,就会发现它们吞并刚出现的小公司是家常便饭的事。但即便是这样,硅谷还是有很多小软件公司出现。毕竟,软件业这块平面上单凭巨无霸公司那些大圆还是填不满的,圆和圆的结合部总会有空隙存在。至于说到Netscape的消失,原因大家都明白,这其实更多的不是取决于技术。事实上微软进军这个领域太直接不过了,软件上已经有了Visual Studio和MS Office,因此开发浏览器的技术对他而言几乎都是现成的。即便这样,Microsoft的IE还是在NCSA Mosaic的基础上完成的。所以Netscape没有被收购,而是彻底被打败了。
任何一个行业初始阶段时的门槛都很低,但是,只要发展到一定的阶段后,它的门槛就必然抬高。
===
笔者十分赞同这句话,软件业创意太重要了。什么东西都是最先做出来的那几家获益最多,后来者通常都是分些残羹。前两天在同事那里看一个 搞笑的flash,突然冒出一个念头,怎么当初我就没有想到在浏览器里写个插件来支持动画和音乐呢。呵呵,归根结底还是个人的 平有限啊:-)大家每天睡觉前不妨 个几分钟想想,说不定就被你想到个点子从此一步登天了呢,呵呵。
现在 中国软件行业正在形成,所以现在做一个程序员一定要有耐心。
===
我想程序员不管什么时候都需要耐心,耐心可以说是软件开发者的必备素质,并且体现在各个方面:写程序的时候没有耐心那你就等着后面抓不尽的虫吧;给自己充电的时候你没有耐心,那么你永远只能掌握肤浅的东西;追女 朋友的时候没有耐心,那你就.,晕,怎么有番茄扔过来了,我闪:-)
你一定要把所有的问题搞清楚,然后再去做程序。
===
软件设计是门要靠脑力的活,而软件发展的迅速和需求的不断提高是人所共知的。什么时候我都不敢奢望把所有的问题都搞清楚了。实际上每个开发者,哦,不,:-)是我本人在开发的过程中总是不断发现新问题,不断在解决问题,是个螺旋提高的过程。我一向认为在开发中 学习是最快最有效的。
事实上,美国的商业编译器也不是一个人能"玩"的,现在你可能觉得很简单的,甚至Linux还带了一个GCC,且源程序还在。你可以把它改一改,做个VC试一试,看它会有人用吗?即使你再做个界面,它也还是GCC,绝对不会成为Visual C++那样能商业化的软件。
===
我依稀记得曾经看过一篇章,说Borland当初的Turbo Pascal主要就是一个牛牛用汇编写出来的。呵呵,如果有人给GCC写个类似VC的界面我举双手双脚赞成,免费帮他测试:-)有时我在想,Borland当初开发Delphi的时候不用Pascal而用C++的话,现在开发工具的 市场份额会是个什么格局?(本人绝对没有瞧不起Pascal的意思,事实上我的第一门语言就是Pascal,只是因为图书馆里Pascal的书被人借光了才 自学了C)如果我给Gcc写了个界面,当然还是GCC。用过GCC的人从来不会说GCC比不上Visual C++,两者实在没有办法比,不在一个数量级上。GCC是个强大的编译器,支持N种硬件平台和官方的软件标准,同时也引入了很多软件开发者急需的好特性。大多数优良的库,罕有不能在GCC上编译通过的。嘻嘻,有为GCC做广告之嫌?至于GCC的商业化,我就看到过一些卖硬件产品的公司,它们附带的编译器就是GCC或者其变种。再说了,大量大型的软件都可以用GCC编译出来的,从稳定上讲我想不会比Visual C++差吧。事实上,我用Visual C++的时候就遇到过所谓的Internal Error,而我用GCC,就从来没遇到过这种莫名其妙的内部 错误的抱怨。我想,GCC绝对有商业软件的潜质,呵呵,就是在可视化方面比不上Visual C++,虽说也有一些GCC的图形前端。
机遇是从耐心中产生的,越有耐心,就越有机遇。
===
名言啊名言,我有耐心啊,机遇快来吧,呵呵。大家还是埋头苦干吧,别真的机遇来了你还没有准备好,呵呵~
如果你是从MFC入手的,或者是从VC入手的,那么要做出一个真正的能应用个人领域的通用软件,就会走
非常多的弯
===
怪了去了,怎么从MFC或者VB入手就会走非常多的弯路呢?从MFC或者VB里调用Win32 API很直接,尤其在Visual C++MFC里。《箴言》很看重底层,Win32 API难道还不够底层吗?难道非要在汇编一级才可以写出真正的通用软件吗?那我干脆去给CPU写微码去了,呵呵~。VB我用的很少,就不说了。至于MFC,如果你真正弄懂了MFC那么你对于Windows的各个方面几乎就全部精通了(当然,我是指Windows内核外用户空间的东东)。
计算机这个东西不管是硬件还是软件,层次很重要。开发很重要的一个方面就是要弄明白你自己需要在什么层次上做东西。一个用java写中间件的开发人员,有多大必要去精通系统底层的东西呢?我想如果你不立足于自己的层次做东西,而胡乱搞跨层的东西,结果可能就是出力而不讨好了。自己研究研究还行,如果在 工作中还是这样层次不清楚的话,呵呵,就很危险了。
当然,我没有让大家不去钻研,但我想最好还是找个前辈请教,根据自己的兴趣制定自己的学习 计划。人的精力毕竟有限,我们要把有限的精力投入为人民服务之中去嘛,可不要浪费了哟,呵呵。
只想混口饭吃,找个工作,可能教你成为MFC的高手之类的书对你就足够了。
===
现在的同志好 幸福啊,国内在不停的引进国外的名书。想当年在95年左右的时候,外国参考书实在是不多。我建议大家在计算机领域里面看书最好是找老外的。不是我崇洋媚外,老外出书基本上还是蛮负责的,而国内引进的大多还不错。但是即使你在修炼国外大牛们关于MFC的书,如果你不认真 实践,那么光靠书你是不可能成为MFC高手的。MFC这个类库的设计已经有很多人在抨击了,我们不多谈,但是如果你真的深入到MFC的源代码里面去,其他我不知道,但是你肯定可以对Windows的运作有个很深入的理解。
从最低层做起,从最基本坐起。
===
笔者的看法是从中间层做起。就以Win32上的Java为例,一开始我绝对不会从Java虚拟机规范,java和本机系统的交互,Java垃圾回收算法的实现等等很底层的东西着手。也不会一开始就涉及那些什么设计模式,Frameword框架之类的高层抽象。我会就从Java语言本身着手,熟悉它的语法,熟悉它的基本库,试着不断用Java描述问题。在这个过程中,你 自然会遇到一些或高层或底层的问题,这个时候你在去钻研它们绝对不迟,并且只可能是事半而功倍。
高手 成长的六个阶段
===
《箴言》一书把程序员的成长分成了六个阶段。笔者却认为只有第一阶段,即熟练的使用某种语言是每个程序员必备的。其他的一些能力对于不同的开发方向应该是不同的。比如《箴言》认为第二阶段是精通某种平台的接口(比如Win32 API)。然而,很多做高层开发的同志,往往不太接触这些底层的API,因为在他下面,操作系统上面已经叠加了很多的层次了。比如,如果你用Java在Win32上面编程,几乎不需要和系统API打交道。这其实也体现了软件分层的 思想:每一层只负责自己的职能,只和自己相邻的层 通讯
《箴言》认为能够进行VxD编程,或者进行操作系统内核的修改就算进入了高层次了。且不说VxD已经被Microsoft抛弃了,新的Win32驱动模型WDM,Linux/Freebsd kernel的小修改笔者都参经碰过,但是我从来不认为我到了很高的层次,尤其和那些做高层开发的朋友比。因为实在是没有办法比,比较是要在同一个层面上进行的,不同层面的东西你怎么比?就算你设计了操作系统,如果让你去 规划一个ERP系统,你也未必 成功。再说了,我写过WDM,觉得WDM也不那么神秘。但反观如果让我设计一个ERP Framework,我倒是觉得很多东西需要学习,我想反之也是一样。至于说到底层开发,难度大概应该实在比较少的 资料和例子程序(尤其在Win32下面),不太友好的调试工具,以及较少的系统支撑。不妨举个例子,在做应用程序开发的时候,开发 环境往往有完善的调试工具,也不太容易把整个操作系统搞死。然而做Kernel开发就不一样了,一不小心操作系统就崩溃了。记得笔者在做WDM开发时,就挂了第二个硬盘,随时准备Ghost,呵呵。
这时Win32或Linux在你眼里已经没有什么差别了。
===
从操作系统的角度看,Win32和Linux就都是操作系统,没什么差别。但是涉及到具体的开发,你就不得不清楚的 认识到两者的不同,用户态程序还好一点,大致把几个重要的发面,比如线程, 消息机制,GUI等方面抽象一下,就像QT,GTK做的那样,就可以实现Win32和Linux的跨平台。然而,在内核里面,两者差别是很大的,我从来没有看到过有什么Driver开发库能够做到很好的在Win32和Linux下面跨平台。我所看到的最接近的可能是Driver Works带的那个C++库。BTW,也许有什么好的库我不知道,希望知道的朋友不吝赐教:-)
初级程序员他会觉得VB也能做出应用来,到中级程序员他可能就不会 选择VB了,可能会用MFC。到高级程序员时,他绝对不是首先选择以上工具,考虑的绝对是什么才是具有最快效率,最稳定性能的解决问题的办法。
===
不知道《箴言》怎么把VB和初级程序员绑到一起去了,不管你用什么开发工具,你水平不到家你就是初级程序员,并不是说你选择了看似高档的开发工具你水平就高了。再说VB和MFC两者是不同的 概念,没的比!至于说到高级程序员,我觉得《箴言》的看法还是对的,我有个 老师就曾经把一个大 项目分成了多块,每块都选择了适当的开发工具,最后项目成功了。每每看到论坛上有人在 讨论 开发工具的优劣,我就想只要讨论就落入下乘了。没有一个工具是十全十美的,聪明的人应该根据自己项目的 情况做正确的评估。你想想,如果真的出来个十全十美的东东,那别的人还怎么 生存?
最高境界的时候,想的就是"我就是程序,程序就是我"。
===
"我就是程序,程序就是我",这是不是走火入魔了?人脑和电脑的差别无疑是巨大的,否则就不至于人工智能领域多年来都缺乏革命性的进步了。我想最高的境界反而应该是认清人脑和电脑的巨大差别,设法用程序搭桥,让他们能够沟通。实际上,我们想想数据结构里,操作系统里的很多概念是不是都是直接反映了人类 生活的一些 经验呢?(比如字符串匹配的快速算法,各种 同步和Cache技术)。我想,让电脑去适应人脑还可以,反之怎么才能进行?
如果你达到高手的状态,你就是操作系统,你就能做任何程序。
===
假设我现在就是操作系统了,我就可以做任何程序了吗?操作系统不是什么特别的东西,它就是在替我们 管理硬件资源而已。什么是程序?程序是给人解决问题的代码的集合。操作系统的功能是受硬件资源约束的,而人类需要解决的问题是误穷尽的
实际上技术到达最高境界,是没有分别的。
===
对于同一种技术,到了比较高的境界,确实是相通的。为什么呢?其实可以理解,你们都是在解决一个具体的问题,这个问题本身所具备的特性你怎么也回避不了的。比如你做分布式数据库,那么数据的一致性问题就是分布式数据库的一个没法绕过的问题,不管你用什么 方法,你最终还是要回到这个问题上来。然而,这种所谓的没有差别只是概念上的,你可千万不要奢望说你明白了Microsoft的DCOM,你就可以不需要花什么力气就能很快上手Sun的分布式技术。我想这也就是我们这些做软件的家伙命苦的地方吧。如果有个什么标准化组织把软件的方方面面也定义好严格的规范,我们也能省去好多事了啊。
作者说微软的那些"老头"在64位的机器上去做虚拟windows,然后就说"微软的技术能不 先进吗?是Linux那几个玩家
能搞定的吗?
===
怪了去了,在CPU上跑几个操作系统就很牛了吗?这根本就不是什么新的想法,且不谈Java环境就具备了初步的虚拟计算机的能力,sourceforge上一度 排名很考前的bochs就是个很好的x86模拟器,我就在它上面跑过一个小小的linux。再说现在的VMWare,性能已经很好了,我同事在PII350的机器上都可以在VMWare里跑Win2000,那里还要什么64位机啊。再说了,64位的威力就在可以同时跑跑多操作系统,不会吧?
微软的技术非常雄厚,世界计算机的最新技术绝对集中在这几个人(指微软设计内核的人)手里。
===
晕啊,计算机是门涉及面很广的学科,操作系统就是计算机全部吗?我看不见得吧。我们如果看看Microsoft的发展轨迹,就可以看到它的"原创"其实也不是 想象中的多。Microsoft本身就不是靠先进的技术起家的。我想让《箴言》举一些Microsoft的最新的而别人没有的技术,恐怕也不容易吧。(也许还锁在比尔的保险柜里,我还不知道,呵呵)
尤其现在很多的程序员,都被误导从MFC着手,这样很容易形成封闭式的思维模式。
===
奇怪了,从MFC着手怎么就算被误导了?怎么从MFC入手就容易形成封闭的思维模式了呢?MFC恐怕应该算是对Win32 API封装最薄的了吧。而对MFC的研究你迟早要深入到源代码一级。你想想,Microsoft一向对源代码视如珍宝,它还是公开了MFC的源代码,为什么?很明显,微软明白MFC的封装是不完善的,它几乎是鼓励你自己做扩展了。难道非要我们哪怕打开一个窗口还要做那些手工编写资源文件,注册窗口类,大的CASE语句处理消息分发,消息循环那些"杂事"?我记得当初我对在Borland C++3.1下面不得不做那些蠢事恼火的很,甚至还写了个小程序用来生成那些千篇一律的代码。
所以我认为用MFC不但不会形成封闭的思维模式,恰恰相反!呵呵,如果MFC的设计者看到《箴言》对他们的 评价不知会做何感想了。
在你能够成为高级程序员的时候,对MFC这些是不屑一顾的。
===
从对C++威力利用的角度或者从纯面相对象的角度看,MFC的封装确实不算最高明,但是如果你去看看它的源代码,闪光点还是随处既是的。不知道《箴言》是不是想到过用C语法的宏来构造消息处理系统呢?我想不是对系统和语言都有深入了解的人,恐怕是不会想得到吧。微软作为一个大公司,里面有很多牛人很正常。在research.microsoft.com上有一个正则表达式库就是微软一个牛人用C++实现的,现在正则表达式库不希奇,但是微软那个牛人是用C++ 模板来构造自动机的,确实不失巧妙,据说性能还不错。
所以,MFC能够在微软的产品里存在这么多年,不是没有原因的吧。呵呵,我也希望我有能够对MFC不屑一顾的那一天啊:-)
正确的程序设计思路是成对编码,先写上面的大括号,然后马上写下面的。当你要用到一个内存的时候,写一个分配函数分配一块内存,马上接着写释放这块内存的代码。
===
呵呵,对于大括号的问题,不同的编码规范有不同的定义,我想没有必要强行规定吧,至于《箴言》所说的便于查找,我看这个理由也站不住脚。稍微好一点的编辑器,比如GNU Emacs就支持括号的自动匹配,哪里用的着自己去找,不累啊。
至于说到内存的分配和释放,这是C/C++永恒的 话题了,如果就如《箴言》所说的这么容易解决,也就不会被讨论这么多年了。你想想,很多时候都是在这个地方分配了内存而在别的地方释放,而这个别的地方有可能是在别的人的程序里, 读后感《编程高手箴言》读后感》( http://www.unjs.com)。比如在Linux网卡驱动里分配的内存,就可能被很多其他的高层 协议处理程序释放(当然,对于一块特定的内存,只会被释放一次),这时你怎么才能做到成对编码呢?
集成环境的TAB首先要设成8,因为TAB的基本定义就是8,而现在的VC把它设成4,这样你编出的程序放到一个标准的环境里看的时候就是乱的。
===
应为我 习惯了TAB是4的风格,但我还是做了试验,在TAB是4的VC环境里,写的代码在UltraEdit打开一点问题都没有。不知道《箴言》是用的什么标准环境,呵呵~
如果写出来的代码大家都看不懂,那绝对是垃圾。
===
我觉得这话可以和这句话类比,如果写出来的 论文大家都看不懂,那绝对是垃圾。呵呵,怎么可以这么武断哟。什么代码是垃圾?我想对于一个大家都知道的思想而你用别人看不懂的代码表达,那才是垃圾。工作中这种代码是十分有害的,不利于维护还会隐藏很多bug在里面。这里可以举个例子,有个网站收集很多以难懂著称的C源程序,有些还是在一个什么难懂程序 比赛里得奖的东西。然而我下回来后,很多都在现在主流的编译器里不能成功编译了。
比如你从Linux中或者网上下载了一个"烂"程序后。
===
看来《箴言》是把Linux中的程序和那些"烂"程序相提并论了,我就不懂它的这种结论是从哪里来的。怎么它就看不到Linux里大量优秀的源程序呢,呵呵,我都不愿意多说了,大家都有自己的眼睛。
(Linux里)那些程序还在不断的升级,那程序还会有新版,如果你把他拿来看一下,对你来说其实没有什么价值。
===
没有价值?!别人我不知道,但我自己的事我明白,我可以坦率的对大家说我从Linux/BSD的代码里获益良多。各个方面,算法,系统架构,硬件等等方面,都可以从这些优秀的代码里获益,希望大家不要听信《箴言》里的说法,有空根据自己的兴趣去多读读源代码,保证获益不浅。就以超级解霸擅长的东西而言,Linux 社区里一些播放器,性能就十分出色。而且免费,源代码全都有,只要你水平高,想怎么改就怎么改,多好啊。不知道《箴言》里说上面那些话的用意是什么。
VC自动给你生成一堆堆的垃圾框架,相对于网上Linux程序来说,它可能更"臭"一些。
===
呵呵,又是赤裸裸对VC的打击,老实说我确实很少听人说VC生成的是垃圾代码,更很少听人说这些代码很臭。难道非要用纯C去给Win32编程才香吗?
Linux是个操作系统,很厉害呀!其实那些程序你拿回来,耐心的去读它,会发现,它里面乱的很。
===
难道操作系统能够和普通的应用程序相提并论吗?我就读过Linux里面网络部分和文件系统部分的源代码,我怎么没感到"乱的很"啊。操作系统程序直接架构在硬件上面,当然和普通的程序不同,你硬要用普通应用程序的 写作眼光来看操作系统的编码,我想这只能说明你的方法没有对头。实际的情况是,Linux里面核心的代码已经经过了多次提炼,有很多好的东西在里面。而一些牛人对于C和硬件系统的深入了解而写下的程序确实能够使后来人获益了。更别说一些特殊的场合,那些大牛们在没有办法的情况下写下"脏"的代码都会给出说明,看着那些充满幽默的注释,往往你会会心一笑,从心底佩服那些人的水平和率真。
所有程序都是调试出来的,不是写出来的。
===
呵呵,不先写出来你怎么调?再说了,也不是什么系统你都可以慢慢去调的。现在的很多大型软件代码量十分庞大,哪里有可能让你在汇编状态下慢慢琢磨,我想只有在那些很少的关键的部位才有可能吧。所以好的软件它们那些对效率十分敏感的部位才是在汇编状态下慢慢调慢慢改出来的。
int i;
extern int In,Out;
for(i=0;i 100;i++)
{
Out[i]*=In[i];
}
===
呵呵,当心Out[x]溢出变负数从而于你的预期不符哦。
如果说哪个系统是编出来的,那它肯定会有很多性能方面的问题,包括可能有不可预测的各种各样的问题。
===
现在硬件告诉发展,很多时候性能的考虑已经放到第二的位置上去了。我们更多的是关注代码的可理解性。至于可能有不可预测的问题,我想什么东西都自己动手而不去用一些饱经考验的库,可能更遭。以我为例,我在用了STL后,对于常用数据机构的使用就不再出现指针问题了。
有可能完全是编译器的错误,也有可能因你程序里面增加了什么,而对程序产生干扰。
===
记得在刚开始编程的时候,经常抱怨编译器有问题,可是越到后来这种抱怨就越少了,几乎都没有了。就比如现在吧,如果你给Visual C++打了最新的补丁后想要找个bug出来,保证不容易。至于在程序里添加了新东西而导致新的问题,这点很正常,所以才需要一些源码控制软件来跟踪软件的修改。
MFC里很常见的一种设计思维,就是任何一个东西,只管创建,不管释放和销毁。这种思路是现在很多程序员做的程序没用几下就会死机的原因。MFC让你这么做,就是让你永远成为不了高手,你写的程序永远不可能稳定。
===
我在MFC里编程也有几年了,怎么就是 第一次听说任何东西都是只管创建,不管释放和撤销呢?还有《箴言》怎么知道MFC就是让人不能成为"高手"了呢?呵呵,我好同情那帮设计MFC的人啊,居然有人说用MFC写的程序永远不可能稳定。
任何一个软件一定都是跟一个平台相关联的,脱离平台之上的软件几乎都是不能用的。
===
不知道《箴言》所谓的平台只什么,开放源码领域里很多程序都是跨操作系统和硬件平台的。为什么我能这么说?很简单,C可以跨硬件平台和软件平台,很多库在设计的时候就考虑到了不同的软件平台,所以在这样的基础上写出跨平台的软件再直接不过了。再比如,Borland就声称Win32上的Delphi程序几乎不用修改就可以跑在Kylix/Linux上。
一个很普通的程序员,如果他能够考虑到将程序分成好几个动态库,那么他的思维就已经有点开放性了,就已经不是MFC那些思维方式了。
===
软件用DLL实现就叫做有开放性思维了,原来这么简单啊,呵呵。MFC好像重来也没有限制过你在程序里用DLL吧,连动态连接的MFC程序里都要求目标系统里有MFC动态连接库啊。VC向导里好像也有生成DLL的Wizard吧。
你看MFC的思路,那就是一层套一层的,要把所有的类都实现了,然后继承。他从CWnd以后,把所有的东西都包含进去了,然后组成一个巨型的类。这个巨型的类连界面到实现系统都包括在里面。这时你怎么拆?根本没有拆的方法,这就是封闭式思维。
===
继承,当然就是一层套一层了!MFC作为一个Framework,实现很多的类有什么希奇的?我们看看MFC的类图,好像在CWnd下面还有很多继承关系吧?CWnd作为对Win32里window的抽象,其复杂性是天生的,因为win32的概念里窗口本来就是个复杂的概念,里面设计各种各样的资源的管理,自绘,消息处理等等。至于说道拆分,我看很容易啊,我实现个新的窗口类我就从CWnd派生,不是窗口类而要支持什么Serialize之类的,那么我就从CObject派生。
要么我就干脆自己实现自己的类层次体系。像BCGSoft的基于MFC的界面库就是大量的用自己的类去替代MFC,也没听他们抱怨说MFC没法扩充啊。
每个小函数出错的几率就会很小,那么组合起来整个程序出错的几率就很小。
===
软件设计的一个讨厌的地方就是有时它不是简单的1+1=2,我们常常会说联合调试,如果真的小部件OK,大系统就OK,我们干吗还需要联调啊。对于函数拆分的原则,可能没有个普适的并且能付诸实践定义。但是在设计的时候心中尽量想着软件工程里面的那些原则,比如什么松耦合之类的总是没错的。
动态库,包括它的代码和数据都是独立的,绝对不会跟其他的动态库串在一起。但是如果你把所有功能放到一个EXE工程里,他的数据和代码都是放到一起的,最后产生可执行程序的时候就会互相干扰。
===
谁说不会相互干扰的,别忘了,一旦DLL被加载了,它就会映射到你的程序地址空间里面来。一度大家讨论的很多的DLL注入大法不就是DLL干扰宿主程序运行的典型吗?当然这种干扰有良性和恶性的区别罢了。
通用软件是一行一行编出来的,而且每一行的结果要到达一种可预测性。
===
从《箴言》的上下文理解,好像是不推荐利用一些现成的库而崇尚什么都自己写。别忘了,很多有工业强度的库,比如STL是饱经考验的,而开发工具生成的代码都是那些很死板的东西,我倒是认为人工去写不仅效率低,而且反而在不经意间容易出错。如果这么不 信任开发工具,干脆自己写机器码算了,但是这可能吗?
也就是从症状就能判断出哪些代码产生了问题。
===
如果能够从软件的症状就可以知道哪些代码出了问题,我想微软也就不会要在它的错误代码 报告里包含一大堆系统映象数据了。尤其是软件的复杂性提高后,问题产生的原因往往是复杂的,交织在一起的。高手相对于普通人的一个显著标志就是对出现的问题能够直接或者借助调试器的情况下准确定位。当然了,如果程序规模不大,还是可以根据症状而定位出问题的代码的。
所有的代码必须是经过测试的,必须是一步一步调试过的。代码在汇编一级是怎么执行的,你都得非常清楚。
===
所有的代码都经过测试,这是理所当然的,然而对于经过测试的代码是不是要一步一步调试。我想是不必要的,都没有问题了,还调什么啊?再说,现在软件规模这么大也不太有 时间让你一步一步调试,一些不提供源码的第三方库你不会都在汇编一级慢慢调试吧,呵呵。
注:这一部分涉及《箴言》第二章。
CPU是计算机的心脏,是控制程序的核心。只有真正了解了CPU的结构和运行机理,才能真正编出优秀的
程序。.以至今天如火如荼的64位。
==
CPU确实是计算机的核心,但是是不是一定要了解了CPU的结构和运行机理了才能写出漂亮的程序呢?我觉得不一定。软件是分层的,每一层都有可能出漂亮的软件。如果你希望在底层,尤其是和硬件关系密切的层,比如操作系统硬件核心层写出漂亮的代码,当然需要深刻理解CPU的结构和运行机理。然而,如果你的工作层次较高,那么你实际看到的已经不是硬件CPU了,而是你的操作系统,开发运行环境等一起提供的一个"虚拟"的机器了。就如我们在用汇编开发程序的同志,一般不太会关注CPU内部的逻辑设计吧。
至于64位机,呵呵,前途究竟如何还是个未知数,毕竟AMD推出64机后,Intel并没有立即跟进,好像还没有到"如火如荼"的地步吧。开个玩笑,如果AMD的64位机工作在32位时能发挥哪怕1.5倍同档次32位机的性能,我也就会毫不犹豫的淘汰我这片AthlonXP了:-)当然,64位取代32位是必然的,只是个时间问题了,兄弟们努力啊,编出牛牛的软件,充分去发挥硬件的优势和特点啊;-)
RISC就是在设计CPU的时候,只把最常用的指令用硬件来实现,其他的指令都通过微代码用软件的方法模拟实现。CISC是一种指令对应一组执行单元的体系结构。不过,随着CISC工作频率的提高和技术的发展,RISC现在已经黯然失色了。
==
实际上,一些CISC CPU也是离不开微代码的,比如Intel就给它的P4 CPU出过微代码补丁程序,大家可以看看这个链接。当然,现在RISC体系正面临CISC体系CPU的强大攻势,一些传统RISC服务器供应商,比如IBM,SUN都为了留后手而推出了基于CISC的服务器。但是,市场的选择有时未必就是技术最先进的东西。毕竟最大的芯片巨无霸Intel是CISC的支持者。而RISC和CISC体系之争从来没有个最终的结论。
让我们记住一些闪亮的RISC CPU家族吧:DEC/Compaq Alpha,MIPS,HP PA-RISC系列,SUN Sparc系列,IBM/Moto PowerPC等等,即使有这么一天CISC CPU淡出江湖。
1989年,Intel推出80486芯片,速度上突破100MHz,超过了RISC CPU。
==
按照Intel自己的文档,它1989推出的486DX是25MHz工作频率,一级缓存是16Kb。如果Intel所言无误,那么以这种工作频率要超过同时的RISC,恐怕还是个问题。虽说作频率并不是CPU工作速度的唯一因素。引用中科院和科大做CPU的一个成员胡伟武的话:"我的 愿望就是超过Intel,孔子说朝闻道夕死可矣,我是早上超过洋鬼子,夕死可矣。
不是说卖得比Intel好,我要跑得比他们快,超过Alpha比较难,超过Intel有希望。我们要专注于结构的突破超
过Intel"。
80386提供了两种工作模式,其一为实模式,.,其二为保护模式。
==
根据Intel的手册,其实还应该有个系统管理模式。我没有碰到过在这个模式下工作的程序,感兴趣的同志请自行参考Intel的手册。
不同任务间的保护:通过把不同的任务放在不同的虚拟地址空间中去,来实现不同任务的隔离(即A程序不能访问B
程序的代码和数据)
==
虚拟空间的概念对于刚刚碰386保护模式的同志可能有点难理解,其实是这样的:假设一个程序被你在多任务操作系统里启动了两遍,那么你这个程序在两个实例里看到的虚拟空间其实是一样的,都是0~4G,然而底层的寻址机制能够让这两个实例里同一个数据或者指令虽然具有相同的虚拟地址,然而却有不同的物理地址。我们知道,软件最终要落实到硬件,如果最终的物理地址不一样,那么这两个程序就不会相互干扰了。当然了,如果你硬要让两个程序的某一个虚拟地址对应同一个物理地址也可以,对于操作系统而言是小菜一碟,只要把转换表格小小修改一下就可以达到目的。(具体的寻址方法见下面)
《箴言》一书对386 CPU的保护模式下的寻址给出了flat,segmented模式的寻址方式。
==
386CPU在进入保护模式之前,实际上要在主存里面初始化好一大堆数据结构的。按照Intel手册自己的说法,The contents of the protected-mode system data structures loaded into memory during software initialization,depend largely on the type of memory management the protected-mode operatingsystem or executive is going to support:flat,flat with paging,segmented,or segmented with paging.《箴言》一书给出的是一个简单的模型,可能早期的DOS程序员为了访问大内存,在没有OS的支持下选择这种方式。而在现在实际的操作系统里,不会用这种方式,基本上都是flat,paging模式。有的同志也许要问,《箴言》给出的访问模式不是挺好的嘛,简单易懂。那么为什么现在稍微好一点的操作系统不采用这种方式呢?
原因是为了支持"虚拟内存",只有启用了CPU的paging模式后,才可以设计出高效的虚拟内存管理程序来。何谓虚拟内存,简单的说就是一般应用程序设计者和某些底层操作系统设计者看到的存贮器。我们知道,32位的机器可以寻址4G的地址范围(实际上较新的Intel CPU通过一定的方式可以访问超过4G的物理存贮器,然而它们可以直接访问的虚拟存贮器还是4G),而我们绝大多数的同志并没有这么多的存贮器,怎么办?利用paging功能,在OS的支持下,就可以给程序员提供一个虚拟的存贮器,其大小是4G(当然,现在的操作系统并没有把全部4G给普通程序,而是保留了一些给OS内核,比如Linux好像就是给应用程序留下了3G的虚拟空间)。这种所谓的"虚拟内存"是如何实现的呢,要回答这个问题就必须从CPU在启用了paging后如何得到物理地址的过程说起。
下面我试图用尽量通俗的文字描述这个过程,尽量少设计386保护模式的一些术语。至于官方的说法请参考Intel 386程序员手册,这个手册可以从Intel免费下到。给个链接吧,免得有的同志再去搜了,。OK,let sgo!这里要描述的是一些主流操作系统使用的flat,paging模式。何谓flat,其实386CPU还是保留了早期CPU的分段功能,而flat模式就是将段的基地址设为0,而将这个段的范围设为4G,这样,其实是没有用分段功能。何谓paging呢,就是把虚拟内存分成许多小块,然后将这些块再映射到物理内存或者辅助存贮器,比如硬盘。这就解释了为什么可以提供给普通程序员大的多的(比如3G)内存空间,因为我们可以把一些虚拟内存映射到磁盘上,在需要的时候在将他们调到物理内存中去。
如果我们反汇编一些程序,就可以看到,从表面上看,好像访问内存的方法和过去没什么差别嘛。实际的情况是这种差别是藏在幕后的。我们在汇编程序里使用的其实是"虚拟地址",虚拟地址的形成还是和过去的段式管理一样,即段的开始地址加上偏移量。由于在flat模式下,所有的段开始地址都是0,所以我们在汇编程序里指定的偏移就是最终的虚拟地址了。
比如下面这个语句,movl$0x4321,0x 8049430,其含义是将整数0x4321送到地址是0x 8049430的地方去。(抱歉,由于我手头的机器上没有Win32平台的开发工具,只好从用gdb反汇编的程序中摘了一行)在flat模式下,0x 8049430就是最终的虚拟地址了。得到虚拟地址后,革命成功了第一步,下面就是要利用分页管理来得到最终的物理地址了。过程如下,首先把虚拟地址,也就是0x 8049430的高10bits取出来作为一个index去索引"第一个"表格,这个表格的起始地址放在系统CR3寄存器里。从"第一个"表格里取到一个32位整数,而这个整数就对应了"第二个"表格的起始地址。然后将虚拟地址0x 8049430的中间10个bits拿出来索引这第二个表格,又可以得到一个32位的值,这个值其实就是一个物理地址了,用这个物理地址加上刚才0x 8049430剩下的12个bits的值就可以形成最终0x 8049430对应的物理地址了。写完了上面这个过程,我自己也回头看了一下,确实写的不怎么清楚,是吧?那好,我们就举个实际生活中一个不存在的例子来说明(废话,既然不存在还说了干吗:-)。
假如你经营一个很大的 酒店,现在让你接待很多来自全国各地的旅客,聪明的你想到了一个安排他们的方法,每来一个人,你就拿过他的身份证,(假设身份证的 格式是"省名-城市名-出生序号")根据他所属的省份去查找"第一个"表格,让他到这个省份对应的 办公室去。这个旅客到了省份对应的办公室后,就有一个接待人员拿过他的身份证根据接下来的城市信息查"第二个"表格又把他安排到这个城市的旅客所属的楼层去。这个旅客到达相应的楼层后,根据自己的出生序号再找到自己的房间。也许你要问,干吗还要按照省份先进行一次安排操作啊,干吗不直接安排到相应的城市对应的办公室去啊?原因是为了 节约几个办公室,有的省份因为没有计划,所以聪明的你根本就没有安排这个省份下面城市对应的办公室,来了一个人,你根据他的省份立即就可以知道这个人应不应该接待,否则如果每个城市一个办公室,那多浪费啊,特别如果你的客人只集中在几个特定的省份的时候。
而我们的程序在运行的时候恰恰就是"客人只集中在几个特定的省份"这种情况,所以采取了两级索引的方式,这样可以节约用来维护分页机制所需要表格的尺寸。如果不这样,我们假设一个小块单元是4K bytes,那么映射4G空间我们就需要4M+4K bytes来存放分页所需要的表格。(这个值是这样计算的,"第一个"表格是4K大小,有1024个表项,每个表项对应一个"第二个"表,这样就有1024个第二个表,而每个第二个表尺寸也是4K,于是就需要4M的空间放"第二个"表)
呵呵,费了半天劲,也不知道说清楚了没有。对这个部分感兴趣的同志可以进一步参考Intel手册和Linux启动代码。另外,保护模式的一个很重要的概念就是"保护",系统里面的段啊,页啊都有很多属性和权限,低权限的代码不可以调需要更高权限的指令。你想想,如果不是这样的话,用户程序只要随便改改那两个表格,岂不是什么地址都可以访问了,呵呵。一些病毒代码挖空心思也就是想让病毒体在CPU的最高特权级下运行,CIH病毒就是一个很好的 证明
噢,对了,这个链接给出了关于分页机制的比我讲的好的多得 文章,感兴趣的同志可以去看看。如果有同志想写自己的小测试程序,推荐到sourceforge上下载bochs,一个x86模拟器,这样至少可以免去你reboot computer的 烦恼
对于386的中断处理,《箴言》给出了一页的描述。
==
而在Intel的手册里是好几章在讲。这一块我几乎没有碰过,呵呵,平时做试验的时候都是直接关中断了,呵呵。同志们同样可以参考Intel手册和Linux的中断处理部分。希望有同志贴出帖子来描述一下386下面中断的处理方法。
关于《箴言》所附的示例程序
==
这个程序我没有编译,一来没有bc3.1,二来没有纯DOS环境了。只是有几个问题不知道答案,希望明白的同志指教:
1)普通的机器没有4G的存贮器,那么肯定有一些4G里面的地址根本没有对应的物理设备,不知道访问这些地址会不会导致系统异常。
2)按照Intel的手册说明,进入保护模式需要一个长跳转,而示例程序里是个局部短跳转。不知道是不是真的像《箴言》里说得那样。
另外《箴言》程序里的英文注释实在是看不懂,希望理解的同志指点。
3)既然在实模式里一个段只有64K,那么我们又怎么在实模式里通过FS访问所有的内存呢?
呵呵,有机会一定要用bochs好好试一下。


      〔 《编程高手箴言》读后感〕随文赠言:【这世上的一切都借希望而完成,农夫不会剥下一粒玉米,如果他不曾希望它长成种粒;单身汉不会娶妻,如果他不曾希望有孩子;商人也不会去工作,如果他不曾希望因此而有收益。】