计算机软件

在计算机的起源史上,就已经确定了计算机的可编程理论,也就是确定了计算机是可以由程序来控制输入输出以及计算法则。计算机的硬件可以提供内容丰富、形式多样的输入输出;多种逻辑运算功能做数据加工;多种控制开关做状态转换或指示。这些硬件提供的可操作能力(或说可编程能力)就可用指令来操作。计算机的多条指令按逻辑排成的序列就称为程序,按照规律能让计算机实现特定功能的程序就可称为软件。

抽象笼统的说,软件的根本作用就是按逻辑控制硬件,目的就是将人为给定的输入数据流转换为所需的特定输出数据。软件能实现复杂的数理逻辑功能,在文字处理、表格统计、图形图像处理、音视频编解码、电子游戏、互联通讯等多个领域硕果累累。硬件为软件服务,而软件才直接为用户服务。倘若把硬件比喻是计算机科学的筋骨,那软件就是计算机科学的灵魂。

在二十世纪40年代计算机才显雏形的时候,刚问世的程序还只是科学家为配合特定硬件实现特定目的所特定编写的二进制码,甚至还没有命名为软件。在随后的多年里,随着硬件的发展,对程序的需求日渐膨胀。那个年代的人们面对的是一个前所未有的计算机世界,对软件的前景并没有充分的理论准备和清晰的发展思路。新兴的软件行业也经历过一些失败和混乱(史称软件危机),软件工程的概念由此被提出。

人们发现随着程序的规模日渐膨胀,程序开发的难度和成本令人吃惊的提高。需求的压力促使编程思维和编程语言都一步步进化蜕变,简洁而又强大的精髓逐渐沉淀成为经久不衰的经典。一些公司和开源社区逐渐奠定了在软件业的地位,制定了一些软件的规则规范,引领了软件业的潮流。软件开发的流程和思路开始逐渐清晰,软件行业开始朝着规范化和有序化发展。

在计算机历史上,由贝尔实验室发明的C语言和unix操作系统在软件中的历史地位相当于MOS管在集成电路中的历史地位。C语言和操作系统的编程思想逐渐奠定了现代软件系统生态圈的轮廓,操作系统的历史在某种意义上来说也是软件的历史。

计算机软件生态圈一直在一层又一层的加码快速演进,软件业的霸主从微软到谷歌再到facebook,很少有行业能像软件业这样快速的城头变幻大王旗,由此可见软件的发展之快和巨大的能量。软件业的潜力到底还有多大,恐怕只能继续去探索吧!

推荐书籍《计算机程序的构造和解释》,原作名《Structure andInterpretation of Computer Programs》作者是 Harold Abelson /Gerald Jay Sussman / Julie Sussman;《硅谷百年史》,原名《A History ofSilicon Valley: The Greatest Creation of Wealth in The History of The Planet》,[美]阿伦·拉奥(Arun Rao) / [美]皮埃罗·斯加鲁菲(Piero Scarruffi)著。

下面就来按软件的开发思路做些简单介绍,希望能给读者以真正全面的软件研发内涵的启示。

 

程序执行的基本过程

以基本概念来解说:微处理器有运算器和译码器,只要译码器能吃进指令,运算器能吃进数据,微处理器就能向总线上输出结果,这样微处理器就算是执行了程序。

微处理器有着复杂的逻辑电路,内部各种逻辑电路必须要能逻辑同步运行才能保证时效和精准。逻辑同步的一个重要条件就是时钟。一个周期稳定可靠的时钟是所有同步逻辑电路能精确同步的关键。微处理器必须按时钟节拍一拍一拍(这里的一拍就是一个时钟周期)的吃进指令和数据,再一拍一拍的输出结果。根据微处理器的设计不同,各个环节(比如指令执行环节)所需的时钟数都可能不同。不仅是微处理器,计算机中所有的同步逻辑电路都必须按时钟节拍计数进行同步。

现在知道,程序是指令和数据的打包组合。那么程序执行的基本过程可以理解为这个问题:译码器和运算器从哪里吃到指令或数据?微处理器又如何执行程序?

放数据的寄存器一般有多个,名字各有千秋,这些数据寄存器里的数据就喂给了运算器。至于这些寄存器怎么喂给运算器,运算器又如何处理这些数据就全看PC指针指向的指令了。译码器会把PC指针里的地址上所存的数据解码为相应的逻辑操作,根据指令可能会动用运算器里相应的某部分逻辑电路来实现数据的运算和赋值。

微处理器如何执行程序?这个问题换句话说就是都有什么样指令给PC指针执行,所以这个问题得先从指令说起。具体微处理器的架构不一样,指令概念和意义也不一样,往往微处理器厂家给出的指令分类也不一样,像CISC(复杂指令集计算机)和RISC(精简指令集计算机)两大类CPU架构的指令集就颇有差异,许多微机原理的教科书以intel 8086为标杆介绍指令及指令分类,实际有些过时和以偏概全。

程序员要想熟悉某种微处理器的执行程序细节,指令是不可回避的一课。这些指令按逻辑一条条码好,就写成了汇编。汇编指令才是微处理器执行后有确切结果的代码,而在汇编指令之上的高级语言C/C++都不能说是执行结果确切,因为同样的C/C++代码都可能被不同的编译器编译优化出略有不同的汇编指令。

指令分类没必要太细,笼统的分清指令概念和脉络即可,不论CISC和RISC,指令大致有如下几类:1. 运算类指令;2. 值比较类指令;3. 寻址跳转类指令;4. 赋值传送类指令;5. 特殊控制类指令;6. 协处理器扩展类指令。

运算类泛指“加、减、乘、除”算术类和“与、或、非、移位、位操作”逻辑类等等运算。值比较类泛指两数比大小。寻址跳转类泛指各种有或无条件的地址跳转。赋值传送类泛指寄存器之间的值传递和内存与寄存器之间的值传输。特殊控制类则各种微处理器都各有独特之处。协处理器是指一些微处理器用额外的大规模逻辑电路来强化某特定的功能,因此往往在原有指令基础上新添一套协处理器指令。

除寻址跳转指令外,还有一类特殊情况会让PC指针发生跳转,这类特殊情况通常是由硬件逻辑控制的跳转,一般微处理器厂商称这类情况叫exception(中文翻成异常),中断是异常情况的一种,如果送的指令或数据超出了微处理器的处理范围,也都属于异常情况。每种微处理器定义的异常或许有些细节差异,但异常与指令一样重要,是熟悉具体某种微处理器中重要的一课。

异常发生后,微处理器一般能自动备份异常发生前一刻的关键寄存器到备份空间比如备份寄存器或堆栈(有些书称之为现场保留),并且自动有相应的一些处理器状态及其寄存器的改变,然后将PC指针的地址自动(这里的自动指的是不需要软件参与)切到指定的地址。而在微处理器自动处理异常之后,也就是在PC指针被切到特定地址之后,后续的异常处理根据情况就可能要由软件来继续完成,所以特定地址上的程序就必须按微处理器设计的规范来写,否则相应的异常处理程序就无法继续正常执行完,或者可能无法正常的返回异常发生前的“现场”。

 

软件的层次和概念

科学事物总是分层次递进发展。计算机科学在日复一日的迭代发展过程中,成果日新月异,逐步成体系化成工程化。软件科学衍生出了软件工程,软件系统形成了繁茂的多层次的软件生态圈。

软件系统按生态依赖分层,可分为编译软件、驱动软件、操作系统、数据库、应用软件、脚本工具、互联网软件等。上述这些软件名词在经常使用个人电脑的人来说也是耳熟能详。这些软件互为依赖,互为调用或通讯来往。在功能层次,软件是从驱动软件到操作系统到应用软件到分布式互联的逐层递进。

在软件科学领域,编程语言由最初二进制码发展出汇编指令,再发展出各种高级抽象语言或面向对象语言。换句话说,软件在源代码层次是从汇编到C到C++/JAVA/XML的逐层抽象转化。

目前在计算机系统中,只有极少部分和芯片硬件密切相关的特殊寄存器读写配置类代码必须用汇编书写,其他代码都可以用高级程序语言来编写。

C语言可以编写几乎所有的驱动层和操作系统层的代码,C语言可以写出直接读写寄存器的代码,也可以写出几乎所有的计算机逻辑,能在较低的软件逻辑层面实现控制分配计算机硬件资源。

而面向对象类的程序语言如C++或JAVA等,依托其编译器的辅助和优化,以及操作系统的接口函数,更适合写应用层软件。在应用层,有各种成规模的程序组件或程序库的迭代继承,均可由编译软件灵活的支持,无需程序员细致的处理某些细节。对应用层软件来说,已经有操作系统层可以依赖。 C++或JAVA之类的编译器可以直接依赖操作系统的标准功能组件,将一些计算机硬件体系相关的问题轻松的照章处理,这类编译器自带的库还常带有许多常用的复杂逻辑或程序框架。有了这些辅助和简化,程序员写面向对象的代码时可以不用细致的处理诸如内存分配释放之类问题。程序员可以更专注的关注软件逻辑,只要记住各种库和接口名字,就能快速写出功能强大的应用层源代码。

在驱动层,操作系统层,应用层的内部,按照软件工程学的方法还可以细分为许多小层,常见的分层命名有有系统层,抽象层,接口层,配置层,服务层,协议层,底层,中间层,上层,前端,后台等等。良好的分层设计便于源代码的移植和重复利用,便于程序员的分工合作,便于软件的升级和维护。

在软件分层的设计及实现方法上,有庞杂的编程技巧和业内规范。初学者在某软件领域往往要花数年时间成体系的学习和实践,才能熟练掌握。虽然软件的历史只有短短的数十年,但经过数十万甚至数百万人一代又一代勤奋工作的积累,软件生态圈的规模如今已是浩繁无比,越来越少有人有能力有时间去涉及每个生态层的编程。如同现代社会分工提升效率,大多数基层程序员只在其中某一层生态圈深耕细作,即使是大牛程序员,在有限的时间里也往往要依靠大量的基层程序员或已有的程序模块才能完成超生态圈的软件系统。所以建议初学者先广泛了解整个生态圈,站在一个高点向下俯瞰寻找,找准自己感兴趣的点和方向再努力朝既定的方向深入学习。

 

软件的编写形式与工具

自从汇编器等各种编译软件问世之后,软件就可以用这些编译软件所能识别的文本形式进行编写了。只要按照编译器设定好的语法写好文本,就可以是程序的片段,如果整套文本遵守计算机软件的逻辑,能被编译成可运行的二进制文件,那这套文本就是软件的源代码。

通常按照编译器的惯例,源码所在的文本文件的名称会遵守某些命名规则,以便识别。这些命名规则有可能被编译器识别,也有可能不识别。这些命名规则作为国际惯例,也可以被诸如特定的源码文本编辑软件所识别,从而为程序员编写软件提供良好的辅助。

一般来说,汇编代码的文件名后缀有.a或.asm,C语言代码的文件名后缀有.c及.h,C++语言代码的文件名后缀有.cpp,JAVA语言代码的文件后缀名有.java,等等。

文本文件和二进制文件的区别在于,文本文件中存放的都是特定的二进制数,而二进制文件中存放的可以是任意二进制数。在计算机软件的发展史上,一直在发展壮大各种字符对应的二进制码的编码标准,也就使得能被识别为文本的字符数量在增大,各种字符都有独特的一个对应的二进制数。计算机领域中最著名也最重要的编码标准当属ASCII码,目前最广泛使用的和包含字符数最多的是国际标准组织ISO制定的unicode码(其中有UTF-8/UTF-16/UTF-32等),在中国有专为汉字码制定的国标 GB,最新版本是GB 18030。

在计算机上编辑文本文件的时候,键盘上任意键入的字符都是以对应的二进制编码数存在于计算机存储器中,只是以字符形式在显示器上给人看而已。至于那时输入的字符是以那种编码存在计算机中的问题,无需人们关注,那种编码取决于所用的文本编辑软件。如果想以数据形式输入二进制,二进制文件编辑软件可以输入数字,十六进制时可以输入abcdef,其他字符的输入都不会响应。

总而言之,如今的源代码都写成文本形式,不管最初的源代码是什么语言或者语法或者编写形式,都要依赖各种对应的编译器编译成计算机可用的软件。

在软件领域,有很多文本编辑软件针对编程做了很多辅助性的优化,比如关键字上色,变量名提醒,自动将很多相关联的函数或变量信息在旁边显示出来等等,这些优化能在很大程度上帮助人更轻松的阅读代码,下面就来简单介绍几款业内专业的软件编写工具。

1、Source Insight,在windows操作系统上工作的c程序员几乎都用这个。

2、UltraEditer,强大的文本编辑器,使用者众。

3、VI,基于linux系统环境编程的程序员几乎都用这个,强大到非专业人士几乎无法使用。

4、Eclipse,做JAVA应用程序的程序员几乎都用这个,插件众多,就是感觉反应速度有点慢,不过你可以买台更快的电脑来弥补。

5、Visual Studio,做windows系统应用程序的程序员几乎都用这个,关键是这是IDE套装,自带编译调试仿真全套工具。

除此之外还有相当多优秀的文本编写工具,有兴趣的读者可以网上搜索。

 

软件的调试和跟踪

在编写软件的时候,软件调试方法是最最重要的基本功,必须熟练掌握,否则休想十足的把握一套稳定的软件。

不同的编程语言和不同的软件架构在具体的方法上往往有许多细节的不同,这里只简单介绍下调试的基本概念和思路。

一般说来,基本的软件调试概念主要有四种:日志(log),断言(assert),内存镜像(Memory dump),仿真(单步跟踪)。若是调试驱动软件时涉及到硬件,还可以基于硬件测量电流电压的思路来做调试跟踪。这些调试手段各有优缺点,往往各自适合不同类别的问题分析。下面就来简单介绍四种基本调试概念及其优缺点。

日志这种分析方法也俗称抓log,是最常见的软件调试方法。一般初学一门编程语言常常先学写打印一条“hello,world”信息的代码,这就是添加log的经典例子。概括的说来,日志的概念就是在代码的某些地方加上专门用于输出信息的代码,然后在软件运行的时候获取日志信息,从而来分析软件的行为(包括软件运行的过程和当时的变量信息)。通常一套软件有许多许多条的log,程序员可以通过对比抓到的log和代码来了解到软件的运行流程和层次关系,从而分析软件问题。

日志的输出形式可以多种多样,一般视不同的场景的方便而定,日志输出的实现细节和具体使用方法也都有很多花招。常见输出日志方法有:从串口输出到其他计算机上显示,或者从显示屏幕上输出日志,或者将日志存到文件中由其他计算机来打开等等。

在代码中只加必要的日志是程序员的基本修养,无助于调试问题或跟踪流程的日志有害无益。无用的日志既影响系统性能又干扰调试,应该尽量避免。

日志往往能解决大部分逻辑类问题,其优点就在于可监控各种环境下真正的软件运行流程,能方便的输出关键信息。

日志也有些缺点,有些调试日志会给系统性能造成负担,甚至影响系统环境导致软硬件流程有变化从而隐藏某些问题,比如日志会占用内存和Cache,对有些内存或Cache问题就有影响,比如日志会有CPU运行时间开销,对有些时序问题就有影响。日志还有一个不便之处在于要事先在代码里添加好,对有些不一定事先添加了周密日志信息的场景或问题就无法分析,只能再添加日志然后再去复现问题,这可能需要许多时间,从而降低问题的解决效率。

断言的概念是判定某个逻辑为真或为假,如果为假就立刻让该软件进入特定的调试环境。这里说的特定的调试环境是因为不同的软件系统可以有不同的断言调试形式,这种调试形式是预先设计好的。断言最重要的一点是能及时提供逻辑出错时的信息,因为一旦运行到断言代码并且不符合断言逻辑就会马上停止软件运行并抛出断言信息。比如有些高级编程语言会提供断言的库函数或关键字,能在捕捉到逻辑错误的时候提供出错点的很多信息像堆栈,函数调用栈,重要寄存器等等。甚至有在编译时实现的断言,从而做到编译时就对某些特定逻辑进行检查。这种特定的调试环境也可以自行设计,然后封装成一个新的断言函数接口。

断言的使用和日志有类似之处,都是在程序中添加用于调试的代码,所不同的是设断言的时候要给一个逻辑条件,在设断言的这个点该逻辑一定能成立,否则断言就能马上抛出错误信息并中止后续本该运行的程序。比如在C语言中,断言的代码都类似:assert(ret >0); ,日志的代码类似于:printf(“%s hello,world”,__func__);

良好的断言设计对软件的调试或测试非常有帮助,效率比较高,而不尽合理的断言却在一定程度上能阻止软件的自我修复,或者将小问题放大成一个大问题。

本章说的内存镜像是指在某一时刻计算机的全部或部分掉电即丢失的关键静态数据(比如CPU寄存器,控制器寄存器,内存等),这些数据或以文件形式保存到ROM里,或以数据流形式输出到其他计算机上,总而言之是将某一瞬间不再有数据变化的计算机全部内存及寄存器数据完整保存到文件中。程序员拿着这个内存镜像数据,就可以以CPU的PC指针寄存器为入口点,堆栈指针寄存器为索引,通用寄存器为参照,内存数据为数据库,符号表(编译时生成)为桥梁,来反推整个系统在那一瞬间的软硬件状态和之前可能运行的软件轨迹或系统状态。捕捉内存镜像的时间点非常重要,如果捕捉晚了,有些中间运行过程就结束了,可能导致最后无法分析出具体出问题的第一现场。通常用断言或软硬件监控的技术来让计算机自动保存内存镜像。

内存镜像的优点就在于对捕捉点或者说触发时刻点的软件状态能肯定的逻辑推导,并且可提供完整的数据查看,仿佛是高清的成像照片。而内存镜像的缺点就在于对之前运行的全部轨迹或状态不一定能每次都做肯定的逻辑推导,所以再次强调捕捉内存镜像的时间点非常重要。像已经运行结束的,已经退出的某子函数,从内存镜像中就无从得知其中运行的详细过程,然而只要在该子函数中添加足够的日志就可以获知详细过程。所以日志和内存镜像这两种调试手段互相配合,就能更有利于快速解决问题。

仿真的概念是模拟真实情况的关键条件,和实际的差别往往是无关紧要的因素,但对人分析真实情况却是简化了环境。仿真在不同的环境可以有多种表现形式,像硬件的仿真是输入硬件参数的条件,按理论的算法去模拟该有的电气特性结果;像嵌入式的仿真是指用硬件仿真器(最闻名的如Trace32,简称T32)接到实际的硬件板子上,按JTAG国际标准协议在T32上运行实际的嵌入式软件,以便观察软硬件逻辑状态;像上层软件的仿真是指用软件虚拟机形式淡化硬件的物理性质,纯以计算逻辑来模拟软件的运行逻辑状态。

本章讲的仿真概念主要是T32仿真器和软件虚拟机仿真。这两种仿真器在软件层面上的仿真效果是一样的,都是可以按PC指针的单步执行程序来慢慢观察软件运行的每一步及软件变量。T32在嵌入式领域的强大是可以驱动实际的硬件以便观察硬件在实际软件指令下的反应。仿真的强大之处可以在一步步的程序运行中随意停下观察每一步的软件环境(这可以可以称为上下文,包括寄存器、全局变量、结构体数据、堆栈等等)。表面看这仿真器仿佛囊括了日志和内存镜像的功能,在调试中简直是把万能钥匙,其实不然,仿真器的最大优点也正好是其最大的局限。对于复杂软件系统中某些低概率性问题或者还没有把握问题点的时候,用仿真分析很可能会徒劳无功,因为面对数以万计的指令运行或需要非常多时间才能重现的问题,不可能用仿真去分析每一步程序运行的上下文及其结果,倒不如让软件全速运行,而用日志或断言来捕捉此类问题的现场。此外,T32和软件仿真都有其必备的输入条件和运行条件,比如T32是物理硬件且十分昂贵,软件虚拟仿真也只能限于纯软件逻辑的仿真等等,而在远程异地分析的环境下仿真调试更是有诸多不便。

Linux操作系统的调试书籍推荐电子工业出版社出版的《Debug Hacks中文版——深入调试的技术和工具》,吉冈弘隆编著。

对职业的软件工程师读者,推荐电子工业出版社出版的《软件调试》, 张银奎著。这本书对软件系统研发实力的提高非常有帮助。

 

软件系统核心

前面在《软件的层次和概念》一节已经介绍过软件系统的层次类别,应该说操作系统在计算机领域是重要的一章,是整个计算机生态系统中不可逾越的软件系统核心,其地位毋庸置疑。现代的操作系统和CPU类似,其真正的内涵之复杂度远非行外人所能想象。如今操作系统相关领域的著作汗牛充栋,细节多如牛毛。微型的如嵌入式操作系统,代码量只有数万行级别,主流的桌面操作系统如linux家族或者windows家族,代码量都在千万行级别。本章只对操作系统的一些基础概念和理论做简单介绍,这些是计算机入门者不得不知的常识。需要提醒的是,深入学习操作系统之前一定要打好基础,必须精通C语言编程,如果能再熟悉一种计算机CPU核心架构及汇编就会更好,否则只能随便翻翻书了解些基本概念和流程。

操作系统(英语:Operating System,简称OS)是管理和控制计算机硬件与软件资源的一整套系统软件。操作系统控制和分配计算机硬件资源,根据既定的资源按既定的逻辑处理硬件输入的数据和输出数据形式,给上层软件提供资源服务和调用接口。

在不同的硬件上,因为硬件资源的不同,操作系统也可能完全不同,具体的操作系统种类和版本都和具体的硬件资源息息相关。比如计算机核心是X86或ARM,即使是同一种操作系统,也会有很多核心代码不同,只是把两类代码都分门别类放在一套代码的各个目录和文件中而已,编译出来的计算机可执行文件一定是不能兼容的两套操作系统版本。再比如有些特制的计算机没有显示器,输入的按键也非常有限,显然用的操作系统就不能是通用的桌面电脑操作系统。再比如传统的手机只有很小的内存,无法满足桌面操作系统的内存要求,也只能定制开发精简的嵌入式操作系统。

正因为操作系统是有如此多的不同种类和内在架构,所以很难用一种通用的分层结构来剖析总结所有的操作系统。通常,按照目前主流操作系统理论来划分层次,一个现代意义上的操作系统可分为四大部分:

驱动程序:是最底层的、直接控制和监视各类硬件寄存器的程序,作用是隐藏硬件的具体细节,并向其他程序提供一个抽象的、通用的软件接口。

内核:操作系统的核心,通常运行在CPU最高特权级,负责提供基础性、结构性的功能。向下提供驱动程序的抽象层次框架;向上提供系统调用、中断服务和安全机制;内部实现CPU共享及并发,进程及线程的调度及通讯,内存管理、文件系统和网络通讯等。

接口库:是一系列特殊的程序库,它们职责在于把系统所提供的基本服务包装成应用程序所能够使用的编程接口(API),是最靠近应用程序的部分。

外围:是指操作系统中除以上三类以外的所有其他部分,通常是用于提供特定高级服务的部件。例如,在微内核结构中,大部分系统服务,以及UNIX/Linux中各种守护进程都通常被划归此列。桌面操作系统的图形显示服务也可划归此列。

正因为有如此多不同种类的操作系统,所以不仅有介绍目前主流操作系统理论的教科书,还有许多介绍特定操作系统的书。介绍操作系统理论的经典教材有《操作系统——精髓与设计原理》,(美)William Stallings(威廉.斯托林斯)著,陈向群,陈渝译;《现代操作系统》,英文名《Modern OperatingSystems》,Andrew S. Tanenbaum著。而写其他操作系统的书林林总总,数目繁多,仅以最便于学习的开源linux社区为例,介绍linux操作系统驱动、系统内核、应用编程、网络和服务器管理类的经典书籍就很繁多,基于linux衍生出来的其他操作系统又有很多。更好更新的书籍还在不断的被创作出来,不同的书各有侧重点,故无法一一给读者推荐全面,下面仅力荐linux相关的几本。

介绍linux操作系统驱动、内核、接口库和外围代码的经典书籍有《深入Linux设备驱动程序内核机制》,陈学松著;《Linux内核设计与实现》,(美)拉芙著,陈莉君,康华译;《深入理解LINUX内核》,[美]博韦,西斯特著;《深入Linux内核架构》,[德]莫尔勒 著,郭旭译;《Linux程序设计》,[英]Neil Matthew Richard Stones著;《UNIX环境高级编程》,(美)史蒂文斯(Stevens,W,R,),(美)拉戈(Rago,S.A)著,戚正伟,张亚英,尤晋元译。

 

软件的优化及测试

软件是用具体的编程语言编写而成,因此软件的优化包括几个方面,有代码层面、编译器层面、及软件系统层面。在代码层面的优良特性就体现在可读性、容错健壮性、可扩展性、可移植性、可调试性等。在编译器层面的优良特性就体现在语法简练高效、编译出来的机器码精炼短小,契合微处理器指令性能的特性等。在软件系统层面的优良特性主要体现在性能优良,资源调配合理,组织架构的层次契合软件需求、具备良好的扩展性兼容性、调试信息丰富而又简洁等等。

在代码层面的优化较为简单,代码的编写体现了程序员的修养和经验,也有一些对源代码做静态扫描的工具可以辅助优化,比如pclint。其次是编译器的优化涉及编程语法和具体的处理器架构及指令等等。相比较而言软件系统层面的优化更难,其与代码量成正比例。在多任务操作系统或上层复杂软件系统中涉及的代码量大、编程技巧复杂、涉及面代码架构层面广,优化的难度较大。

在软件系统层面的优化如同医生诊断开药,首先要有诊断的方法,再有诊断的数据,还要有可用的药,才能开出对症下药的药方。所以优化的前一步是测试,在此强调软件测试非常重要,一定要重视如何测试软件。如果没有有针对性的,行之有效的测试,来检查软件在容错健壮性、可扩展性、可移植性、可调试性等方面的情况,而只是重视软件在某方面的逻辑正确性,那么软件的大量细节将被忽略,最终将无法保证软件产品的质量。基于黑盒的测试最直观简单,但是容易忽略细节,无法完全保证软件真正的长期可靠的质量。基于白盒的测试更需要重视,因为这更能有针对性的检查软件的内涵,但这有赖于测试用例的设计与具体测试工具的实现,而这才是真正软件设计实力的体现。在有可靠测试的基础上,才能迈出优化的第一步,要设计有对应的调试工具来获取软件运行的相关信息。这类调试工具归根结底是基于前文提到的四种基本调试方法来输出信息,具体的工具实现方法和软件系统源码及架构密切相关。通常需要在软件系统的源码中加入监控的代码,并以某种形式输出友好的便于分析的信息,程序员基于有效的信息和源代码比照,才能理解软件的薄弱点,从而达成优化的目标。

在对软件系统做性能优化时,调试程序需要在软件运行的过程中将各种关键场景及其切换的时间及状态做信息输出。在有针对性的高负荷测试之后,才能通过输出的信息找到延迟的点。

在对软件系统做资源优化时,静态的资源(静态的资源是编译时就分配好的资源)通常可以通过编译时的输出信息来找到可优化的点;而动态的资源就有赖于软件运行过程中输出资源的分配信息和对应的场景。

通常各种优化中复杂的是其过程中可能还涉及具体的硬件或其他的模块,所以往往有赖于程序员对涉及知识面的把握和经验。

优化目标能否达成,上述调试过程的每一步都很重要,而最需要重视的是优化所依赖的工具,包括测试工具和调试工具。此类工具的设计与实现非常重要,也并不简单。测试工具必须有针对性的实现对特定目标的压力和多场景切换或并发。而调试工具的原理描述起来很简单,就是能输出关键点的有用信息,其实难点重点就在于如何找到软件系统的关键点,如何输出有用的信息,这有赖于程序员对具体软件系统的熟悉度和把握软件系统的功力。

在一些成熟的操作系统家族比如unix/linux及其变种或者windows系列,各种测试工具和优化工具都是有相当成熟度而且在研发人员眼中有重要地位。

你可能感兴趣的:(计算机科学原理)