我们从解读几个问题开始:
1、各种软件技术之间是怎样的关系?
软件技术分为三个层次:
问题域:计算机图形学、音视频编码、信息安全、模式识别、信息检索、自然语言分析、人工智能、科学计算、专家系统等;
系统集成:C++和Java等面向对象语言、Python等解释型语言、LISP等函数式编程语言、GUI、中间件、编译器与解释器、虚拟机、数据库、网络服务、并行计算、集群、Peer2Peer、系统管理等;
系统功能:硬件描述语言、计算机体系结构与编程模型、指令集与汇编语言、C语言、内核、文件系统、设备驱动、网络协议、POSIX等。
计算机最终是做什么用的呢?最终是通过问题域的各种技术为用户解决问题的,这些技术都包含很高深的算法,然而它们必须在一个平台上运行,它们需要利用平台提供的各种基础设施,比如计算能力、I/O能力和网络互联能力。系统功能和系统集成层就是用来实现这个平台的。系统功能层实现计算、I/O和网络的基本功能,系统集成层对这些基本功能做一些抽象和包装,提供更方便灵活的接口。
2、为什么要学习嵌入式技术?
学习嵌入式系统,你将需要详细了解系统功能层的各部分是如何工作的,也会在面向对象编程、GUI、数据库、网络服务等系统集成层的技术方向进行探索。嵌入式系统可说是麻雀虽小五脏俱全,虽然没有PC和服务器那么复杂,但计算机系统的各种组成一样也不缺。因此,以嵌入式系统作为切入点开始学习软件技术是非常好的选择,避开不必要的复杂性,把握计算机系统最根本的概念和技术要点。打下扎实的基础之后,你的职业发展则完全不必局限于嵌入式领域,即使你日后做PC或服务器开发,学习嵌入式所得的知识和技能同样使你终生受益。正如庖丁解牛,心中有全牛,自然就能游刃有余。
另一方面,你一定见到各大媒体都有大量的宣传,说现在嵌入式行业前景空前的好,嵌入式人才紧缺,有几百万职位空缺等等。这些说法也是对的,但我们需要更深入地理解这说明了什么问题。刚才我们说,嵌入式系统也是一个完整的计算机系统,和PC或服务器没有本质的区别,事实上,嵌入式、PC和服务器的界限已经越来越模糊了。以前的嵌入式就是单片机,只能做简单的运算处理,现在的很多嵌入式处理器性能比从前的奔腾还强,打游戏、看电影都没问题,谁能说它不是PC呢?另外有些专用的嵌入式系统已经在充当服务器的功能了,而集群技术更是可以使许多廉价的处理器组合在一起发挥大型服务器的作用。由于嵌入式越来越多地应用到家用电器、汽车和控制领域的各种设施上,无处不在,并且与PC、服务器呈融合的趋势,所以嵌入式行业前景空前的好。
那么,为什么说嵌入式人才紧缺呢?其实,学嵌入式系统就是学计算机系统,本质上并没有什么特殊的只有嵌入式系统才有的技术。说嵌入式人才紧缺,其本质上是说真正懂计算机的人才少。现在很多学校的所谓“软件学院”培养的软件人才都是一叶障目不见泰山的:只懂J2EE和.NET,即便学过C和汇编,也没有把它们作为核心课程;只会调库函数sort(),而对各种排序算法一无所知;设计模式、软件工程讲得头头是道,却不知道好的软件还是要靠牛人靠智商来做的。培养人像蒸包子一样一屉一屉地出,靠软件工程搭一条生产线,然后让熟练工人站在生产线上拧螺丝,幻想着这样就能生产出好的软件,那是把软件工程和程序员的作用本末倒置了。现在嵌入式开发对程序员的素质要求更高了,以上这类“软件人才”不能胜任了,因此说嵌入式人才紧缺。所以要培养一种有完整的计算机系统概念的软件人才,而不是只会拧螺丝的软件工人。
3、有人说软件技术变化太快,现在学的东西过两年就要完全淘汰,是吗?
你知道这话是什么人说的吗?必然是已经被淘汰的人说的。比如Delphi、BCB、PB这些开发工具和语言,都曾经很是风光了一阵,但现在已经完全被Java和.NET取代了。那么Java和.NET会不会被取代呢,也许在相当长的时间内还不会。
这些被淘汰的程序员有一些共同的特点:只会用鼠标拖拽控件,离开IDE就不知道如何工作,学点儿花拳绣腿的功夫就想吃一辈子,对学习新技术不感兴趣,做一天和尚撞一天钟。现在请回头看看1,在整个软件技术领域,这些变化快的技术其实只占了很小的一块,却成了这些人的全部看家本领,这样的人能不被淘汰吗?
与这些流行的开发工具和语言相反,很多技术和思想是很少变化的。比如,POSIX和SUS标准规定了一套系统函数接口和基本命令的语义,只有实现了这些才可以称作UNIX,因此今天的Linux、Solaris跟20多年前的UNIX在系统功能层上是基本一致的。而指导计算机科学发展的数学理论,甚至是老祖宗们在计算机还没诞生的年代就替我们想好的:布尔代数发表于19世纪,直到一个世纪后发明了计算机和数字电路才有了用武之地;数论在17世纪就出现了,一直都被数学家们当成一套好玩的理论,但只是好玩而已,直到计算机密码学诞生后才发现它的实际用处。
各种流行的开发工具和高级语言虽然变化很快,但是底层的编程语言却非常稳定,各种操作系统的内核都是用C语言写的,以前是这样,以后也不会改变。另一方面,各种编程语言的设计思想也是非常稳定的。其实世界上只有两种编程语言,一种是C,一种是LISP,前者是imperative的,是对计算机模型的抽象,后者是functional的,是对数学函数模型的抽象。面向对象是一种重要的软件工程思想,却算不上一种新的语言模型,应该归在C的一类。属于同一类的各种语言其实都大同小异,一个精通C++的人学习Java需要多长时间?熟悉语法一个星期,熟悉类库三个星期,一个月足矣,以往的经验都可以套到新的编程语言上。然而要想习得深厚的算法功底、逻辑思维和抽象思维修养,能够真正说清楚“系统”是什么,如何分析和设计“系统”,需要多长时间?恐怕要数十年。
4、C和Java哪种语言更好?
计算机科学与编程语言无关,甚至与计算机本身也没太大关系,它研究的对象并不是计算机,而是人分析问题解决问题的方法论。程序写出来最主要不是为了给计算机执行的,而是为了给人看的,使用编程语言和使用自然语言一样是为了表达和交流,只不过程序还可以顺便给计算机执行而已。
以上这些话并不是我说的,而是一位著名的计算机科学家说的。所以,在编程语言的层面上争论是没有意义的。很多初学者错误地认为掌握了编程语言就等于学会了计算机,一种编程语言都还没有掌握好,更没有上升到方法论的层面,只有这种无知的人才会去争论哪种语言好的问题。掌握了编程语言远远不等于学会了计算机,而只是最开始的一步,最简单的一步,到了工作中,用到什么语言就去学什么语言,什么语言过时了就丢掉,编程语言不需要积累因为它太简单了,真正需要积累的是方法论。
很多人喜欢参与到这类争论之中,毫无例外,每个人都在为自己熟练掌握的编程语言辩护,就是“我会的语言最好,我不会的语言都不好”,其实这些人真正想说的是“我会的语言最好是千秋万代,我就不必学新的语言,不必适应新的变化了”,概括起来说就是一个字,懒。真正的高手都是会很多编程语言的,国外有些做技术咨询的,每年都要学好几门新的编程语言,这样才能应对市场的变化。不断丢掉旧的编程语言学习新的,看起来好像完全是白费力气,没有积累,其实,每种编程语言的设计都有独到之处,体现了每种语言的精髓,在融汇百家之后积累下来的正是方法论。
争论哪种语言能做的事情更多、功能更强是没有意义的。从理论上说,任何一种符合图灵机模型的编程语言,加上适当的I/O扩展都可以做任何事情,用shell脚本也可以写出很像样的游戏来。只不过各种语言的设计目标不同,表达能力不同,做不同的事情所需的代码量不同而已。
另外一种错误认识是:哪种语言的市场最大,开发人员最多,哪种语言就最好。单从这种意义上说,Java的确比C更好,所以往届有学员问我们为什么只教C语言。请你注意,操作系统内核是用C写的,各种底层的应用程序包括Java虚拟机也是用C写的。如果你想学Java,学习完嵌入式后你将有能力分析Java虚拟机的实现,站在计算机系统的高度来学Java才会使你成为真正的Java高手。
在FAQ3里说过,世界上有C和LISP两种编程语言,你现在学了C,以后学了C++、Java、.NET等等,也仍然只认识了半个世界。LISP和Haskell构成了另外半个世界,现在很常见的Python和JavaScript等解释型语言也借用了functional programming的思想。如果你只管闷着头写代码,而不去广泛涉猎,那么你将错过很多精彩。
5、我听说编程编到三十五岁就没人要了一定要转行找出路是吗?
这个观点虽然很流行,但根本不值一驳。现在三十五岁转行的那些人,都具有前面所说的那些特征,对学习新技术不感兴趣,对探索计算机的本质不感兴趣,得过且过,下了班就是打游戏、看电视,总之就是懒。如果是开出租、摆摊,勤快人懒人都有饭吃,而IT这一行对懒人是非常无情的,懒人就不该入IT这一行,不从自身找原因,却到处散布这种言论,怪社会不好,打击新人的信心,着实可恨。
另外一种情况,编程编到三十五岁,进入公司的管理层,或者自己创业,这都是很勤快的人,如果他们把这种勤快用在技术上肯定也可以做得更好,所以也无法证明编程编到三十五岁会因为没有出路而转行。
编程不是个体力活,需要高强度的思考和智力投入,分析能力、思考能力都需要时间积累起来,所以并不是越年轻干得越好。相反,我认为三十岁以前写的代码都是垃圾,三十岁以后才能写出像样的程序来。如果希望一辈子走技术的道路而不会被迫转行,就要不断地把自己的工作性质从“体力活”变成“脑力活”。什么叫“不断地”变呢?这里的“体力活”并不是指种地、盖房子这种劳动,而是指简单重复地编写代码,这时你会感觉,派给你的工作都能凭以往的经验轻松应付了,但是千万不要满足于现状,就像温水煮青蛙一样,这是危险的处境!要摆脱这种处境就需要学习、思考、提高,让公司派给你更有挑战性的工作,在工作中应用新学到的知识和技术就是“脑力活”了,但是用得久了又会变成简单重复的“体力活”,这时就需要再学习、再提高,所以叫做“不断地”把自己的工作性质从“体力活”变成“脑力活”。如果有一天你发现,自己长期陷于简单重复的劳动之中,并且业余时间非常少,无法学习提高,这说明公司不会用人,你就该考虑跳糟了。
从另外一个角度来说,如果希望一辈子走技术的道路,就要有自己的核心竞争力,这个核心竞争力决不是凭以往的经验能够做某些工作的能力,而是学习能力、思考能力和解决有挑战性的新问题的潜力。在IT这一行,凭借以往的经验干重复的活是干不长久的,原因很简单,一个问题不会被解决两次,当你发现你的经验能够解决一类问题时,别人早把解决这一类问题的套路编写成framework,新上手的人即使不具备你的这些经验也可以调用framework中的类和函数来解决问题,然后在这个framework的基础上积累新的经验解决新的问题。正因为如此,全世界开发人员的经验才会积累起来,促使软件技术发展得如此迅速。这并不是说经验完全没有用,最关键的,学习和思考也是建立在以往经验的基础之上的。另一方面,现有的framework并不一定是某方面开发经验的完美整合,也需要不断发展,用新的办法重新解决老问题,以Web开发为例,从早期的ASP、PHP到后来的.NET、J2EE,到现在的Ruby on Rails、Django等等,这些framework解决的是同一问题,就是如何快速有效地开发Web应用,这方面的经验被不断重新整合,推陈出新。总结一下,什么才是核心竞争力呢?应该是在经验的基础上学习新技术、解决新问题的能力。
6、做驱动开发还是做应用开发更有前途?
意思就是说,“你告诉我哪个更有前途,我就好好学哪个,另外一个就不用学了”。问这种问题的学员往往会同时问另外一些问题:我以后就想做驱动开发,你教我这些应用开发的技术有什么用?C++用得多吗?学了有什么用?我以后不想做GUI,你教我Qt有什么用?
学习最忌讳的就是“有用的就学,没有用的就不学”这种功利的态度。两个问题:第一,在你还没学进去、还不了解这种技术时,要如何判断这种技术学了有没有用?只能是根据道听途说,看各种论坛上都怎么说的,岂不知论坛上参与这种讨论的100%都是菜鸟,有的水平还不如你。第二,就算你学的技术没有用上,有什么损失吗?从嵌入式工程师可以从事各种各样的开发工作,有做驱动的,有做系统编程的,有做GUI的,有做Web开发的,只要确定了做一类工作,就不可能把嵌入式所学的知识都用上,但至少也用得上3/4的知识,假设剩下的1/4你一辈子也没机会用上了,那也就损失你一个月的学习时间而已,相比于你的收获,这算是很大的损失吗?请注意,上面的假设是不成立的,没用上的那1/4也只是暂时没用上而已,程序员要换工作或者换项目是很常见的,任何人都不可能只涉及一类开发工作,只要有扎实的基础、完备的知识体系,任何工作都能轻松上手。
扎实的基础,完备的知识体系,我们在安排课程体系的时候,正是以这两点为依据的。有的课程内容很少有学员在以后工作中会用到,但是缺了这一环就不成为一个完备的知识体系,例如通过C++来讲面向对象编程,通过Qt来讲面向对象、事件驱动和状态机编程,这些编程思想是程序员必备的基本素质,而C++和Qt可能有些学员以后工作用不到,那这种课该不该上呢?毫无疑问该上。至于还有些人争论说C++不如Java用得多,Qt不如GTK用得多,请翻回去看FAQ4,这种争论是无意义的,有工夫争论谁优谁劣,不如把两种都学了,会更有收获。
回到做驱动开发还是做应用开发更有前途的问题。我只能说,做好了都有前途,做不好都没有前途,只会做一样而完全不懂另一样是最没前途的。不要以为内核开发者就不写应用程序,Linus写了一个源代码管理系统git来维护内核,因为觉得现有的源代码管理系统都不好用。牛人都是这样,需要什么就写什么,才不管是kernel space还是user space。同样,做应用开发如果不懂内核,也没有办法很好地利用内核提供的服务写出性能最优的程序。做内核难,因为调试难,要跟踪大量的并发线程,因为入门难,要写一个hello world都需要学很多知识。做应用也难,回头去看1,计算机科学从理论到实践大部分都在上面两层做文章。所以不存在哪个更难哪个更有前途的问题,任何关于哪个更难的讨论都是too naive的。
7、我一开始看什么书都看不懂,怎么才能理出一个学习顺序?
以前有个学员在学C语言时说,“C语言很多地方都很奇怪,都得用内核的知识去解释,可是你又不先教我内核,我没法学C语言。我只好自己看操作系统的书,看内核代码,可是看不懂。”当然看不懂了,内核代码都是用C写的,如果不学内核就没法学C语言,那不学C语言又怎么可能看懂内核?看来这是一个鸡生蛋还是蛋生鸡的问题。
懒真的是人的本性,就连学习的过程都希望是一条路顺利地走下去,不用动脑就能学会的:身后走过的路都是“已知”,每走一步就把眼前新的“未知”变成“已知”,如此一路走来,把所有的“未知”都变成“已知”就算学成了。可惜,知识不是一条路,而是一个圈,你从任何一个地方跳进这个圈开始走,身后都是“未知”,眼前也都是“未知”。有的人就是不能容忍自己的身后是“未知”:看一本书,一个新的概念A是用我不了解的概念B、C来解释的,我连B、C都不懂怎么学A?没法学了!
不是人家书写得不好,而是没有任何办法能把一个圈扯成一条直线的。学习的过程本质上就是一个循环往复的过程,唯一的办法就是“存疑”:在本子上记着,有B、C这样两个概念是我暂时不理解的,然后就不再去想这回事,而是相信自己已经理解了B、C,基于自己的理解和假设去学习A,由A再去理解X、Y,这样学下去,走完一圈之后再回来,自然就明白当初对B、C的假设正确不正确了,理解了这两个概念,就从本子上划掉,这时需要再走一圈,把原来的一些错误认识纠正过来。所以,任何书都要至少看两遍,第二遍看的时候你会对很多概念有新的认识,因为你看过这个概念后面的章节,在此基础上产生了新的认识。古人早就明白这个道理,所以提出了“温故而知新”。
有哪些好书可以推荐一下吗?
能问出这个问题的都是聪明的人。看书学习是入门过程中非常重要的一环,如果用一本烂书入门,浪费时间还是小事,如果被误导了就麻烦了,如果形成的错误认识不能及时纠正,变得根深蒂固了就更麻烦了。所以,看书一定要有“品牌意识”,在决定看书学习一门技术时先问问这一领域最权威的书是哪本,这里列举一些Bible级别的书:
The C Programming Language, 2nd Edition;
C++ Primer, 4th Edition;
Structure and Interpretation of Computer Programs, 2nd Edition;
Introduction to Algorithms, 2nd Edition;
Compilers: Principles, Techniques, and Tools;
Advanced Programming in the UNIX Environment, 2nd Edition;
TCP/IP Illustrated, Volume 1: The Protocols;
UNIX Network Programming Volume 1, 3rd Edition: The Sockets Networking API;
Understanding the Linux Kernel, 3rd Edition;
Linux Device Drivers, 3rd Edition。
在学习过程中,眼界一定要开阔,不要学到一点东西就沾沾自喜,坐井观天,以为这就是技术的全部。要多和别人交流,多了解别人在看什么书、别人对技术的认识是怎样的。书是看不完的,活到老学到老,对于程序员来说尤其如此。
9、如何处理打游戏和学习的关系?
《如何成为一名黑客》这篇文章中也说过,黑客们都有一些特别的业余爱好,并且往往是非理性思维的爱好,大概是需要换换脑子吧。典型的例子是因滑翔机事故去世Stevens,他写了FAQ8所推荐的Bible当中的三本。爱打游戏的优秀程序员肯定大有人在,有一些还成了优秀的游戏开发人员。但是我认为,要想成为优秀的程序员,必须有两点基本素质,一是对编程非常感兴趣,二是对所有别的事情都不感兴趣,或者都不如对编程感兴趣。有人说,人的一天有24小时,8小时休息,8小时上班,另外的8小时在干什么就决定了你以后有多大发展。一有时间就看书学习,这就是一个程序员应该做的。今天打游戏,明天看电影,后天炒股,那么你就等着三十五岁下岗。如果你不能够认同编程和思考是比其它事情更有吸引力的,那么你不必学编程,集中精力去做最喜欢的事情或许会有更大的成就。