目　录

第1章　计算机系统知识

1.1　计算机系统基础知识

1.1.1　计算机系统硬件基本组成

1.1.2　中央处理单元

1.1.3　数据表示

1.1.4　校验码

1.2　计算机体系结构

1.2.1　计算机体系结构的发展

1.2.2　存储系统

1.2.3　输入输出技术

1.2.4　总线结构

1.3　安全性、可靠性与系统性能评测基础知识

1.3.1　计算机安全概述

1.3.2　加密技术和认证技术

1.3.3　计算机可靠性

1.3.4　计算机系统的性能评价

第2章　程序语言基础知识

2.1　程序语言概述

2.1.1　程序语言的基本概念

2.1.2　程序语言的基本成分

2.2　语言处理程序基础

2.2.1　汇编程序基本原理

2.2.2　编译程序基本原理

2.2.3　解释程序基本原理

第3章　操作系统知识

3.1　操作系统基础知识

3.1.1　操作系统的定义与作用

3.1.2　操作系统的特征与功能

3.1.3　操作系统的类型

3.2　处理机管理

3.2.1　基本概念

3.2.2　进程的控制

3.2.3　进程间的通信

3.2.4　管程

3.2.5　进程调度

3.2.6　死锁

3.2.7　线程

3.3　存储管理

3.3.1　基本概念

3.3.2　存储管理方案

3.3.3　分页存储管理

3.3.4　分段存储管理

3.3.5　段页式存储管理

3.3.6　虚拟存储管理

3.4　设备管理

3.4.1　设备管理概述

3.4.2　I/O软件

3.4.3　设备管理采用的相关缓冲技术

3.4.4　磁盘调度

3.5　文件管理

3.5.1　文件与文件系统

3.5.2　文件的结构和组织

3.5.3　文件目录

3.5.4　存取方法和存储空间的管理

3.5.5　文件的使用

3.5.6　文件的共享和保护

3.5.7　系统的安全与可靠性

3.6　作业管理

3.6.1　作业与作业控制

3.6.2　作业调度

3.6.3　用户界面

3.7　网络与嵌入式操作系统基础知识

3.7.1　网络操作系统

3.7.2　嵌入式操作系统

3.8　UNIX操作系统实例

3.8.1　UNIX操作系统

3.8.2　UNIX文件系统

3.8.3　UNIX进程与存储管理

3.8.4　UNIX设备管理

3.8.5　shell程序

第4章　系统开发和运行知识

4.1　软件工程基础知识

4.1.1　软件工程概述

4.1.2　软件需求分析

4.1.3　软件开发项目管理

4.1.4　软件配置管理

4.1.5　软件工具与软件开发环境

4.1.6　软件过程管理

4.1.7　软件质量管理与质量保证

4.2　系统分析基础知识

4.2.1　系统分析概述

4.2.2　结构化分析方法

4.2.3　系统分析报告

4.3　系统设计知识

4.3.1　系统设计的内容和步骤

4.3.2　系统设计的基本原理

4.3.3　系统总体结构设计

4.3.4　结构化设计方法

4.3.5　面向数据结构的设计方法

4.3.6　系统详细设计

4.4　系统实施知识

4.4.1　系统实施概述

4.4.2　程序设计

4.4.3　系统测试与调试

4.4.4　测试策略和测试方法

4.4.5　调试

4.4.6　系统文档

4.4.7　系统转换

4.5　系统运行和维护知识

4.5.1　系统维护概述

4.5.2　系统评价

第5章　网络基础知识

5.1　网络概述

5.1.1　计算机网络的概念

5.1.2　计算机网络的分类

5.1.3　网络的拓扑结构

5.2　ISO/OSI网络体系结构

5.3　网络互连硬件

5.3.1　网络的设备

5.3.2　网络的传输介质

5.3.3　组建网络

5.4　网络的协议与标准

5.4.1　网络的标准

5.4.2　局域网协议

5.4.3　广域网协议

5.4.4　TCP/IP协议簇

5.5　Internet及应用

5.5.1　Internet概述

5.5.2　Internet地址

5.5.3　Internet服务

5.6　网络安全

5.6.1　网络安全概述

5.6.2　网络的信息安全

5.6.3　防火墙技术

第6章　多媒体基础知识

6.1　多媒体的基本概念

6.1.1　多媒体基本概念

6.1.2　虚拟现实基本概念

6.2　声音

6.2.1　基本概念

6.2.2　声音文件格式

6.3　图形和图像

6.3.1　基础知识

6.3.2　图形、图像文件格式

6.4　动画和视频

6.4.1　基础知识

6.4.2　视频文件格式

6.5　多媒体网络

6.5.1　超文本与超媒体

6.5.2　流媒体

6.6　多媒体计算机系统

6.6.1　多媒体计算机硬件系统

6.6.2　多媒体计算机软件系统

第7章　数据库技术基础

7.1　基本概念

7.1.1　数据库与数据库管理系统

7.1.2　DBMS的功能

7.1.3　DBMS的特征及分类

7.1.4　数据库系统的体系结构

7.1.5　数据库的三级模式结构

7.2　数据模型

7.2.1　数据模型的基本概念

7.2.2　数据模型的三要素

7.2.3　E-R模型

7.2.4　层次模型

7.2.5　网状模型

7.2.6　关系模型

7.2.7　面向对象模型

7.3　关系代数

7.3.1　关系数据库的基本概念

7.3.2　五种基本的关系代数运算

7.3.3　扩展的关系代数运算

7.4　关系数据库SQL语言简介

7.4.1　SQL数据库体系结构

7.4.2　SQL的基本组成

7.4.3　SQL数据定义

7.4.4　SQL数据查询

7.4.5　SQL数据更新

7.4.6　SQL访问控制

7.4.7　嵌入式SQL

7.5　关系数据库规范化

7.5.1　函数依赖

7.5.2　规范化

7.5.3　模式分解及分解应具有的特性

7.6　数据库的控制功能

7.6.1　事务管理

7.6.2　数据库的备份与恢复

7.6.3　并发控制

第8章　数据结构

8.1　线性结构

8.1.1　线性表

8.1.2　栈和队列

8.1.3　串

8.2　数组、矩阵和广义表

8.2.1　数组

8.2.2　矩阵

8.2.3　广义表

8.3　树

8.3.1　树与二叉树的定义

8.3.2　二叉树的性质与存储结构

8.3.3　二叉树的遍历

8.3.4　线索二叉树

8.3.5　最优二叉树

8.3.6　树和森林

8.4　图

8.4.1　图的定义与存储

8.4.2　图的遍历

8.4.3　生成树及最小生成树

8.4.4　拓扑排序和关键路径

8.4.5　最短路径

8.5　查找

8.5.1　查找的基本概念

8.5.2　静态查找表的查找方法

8.5.3　动态查找表

8.5.4　哈希表

8.6　排序

8.6.1　排序的基本概念

8.6.2　简单排序

8.6.3　希尔排序

8.6.4　快速排序

8.6.5　堆排序

8.6.6　归并排序

8.6.7　基数排序

8.6.8　内部排序方法小结

8.6.9　外部排序

第9章　算法设计与分析

9.1　算法设计与分析的基本概念

9.1.1　算法

9.1.2　算法设计

9.1.3　算法分析

9.1.4　算法的表示

9.2　算法分析基础

9.2.1　时间复杂性

9.2.2　渐进符号

9.2.3　递归式

9.3　分治法

9.3.1　递归的概念

9.3.2　分治法的基本思想

9.3.3　分治法的典型实例

9.4　动态规划法

9.4.1　动态规划法的基本思想

9.4.2　动态规划法的典型实例

9.5　贪心法

9.5.1　贪心法的基本思想

9.5.2　贪心法的典型实例

9.6　回溯法

9.6.1　回溯法的算法框架

9.6.2　回溯法的典型实例

9.7　分支限界法

9.8　概率算法

9.9　近似算法

9.10　NP完全性理论

第10章　面向对象技术

10.1　面向对象的基本概念

10.2　面向对象程序设计

10.2.1　面向对象的好处

10.2.2　面向对象程序设计语言

10.2.3　程序设计语言中的OOP机制

10.2.4　面向对象的程序

10.3　面向对象开发技术

10.3.1　面向对象分析

10.3.2　面向对象设计

10.3.3　面向对象测试

10.4　面向对象分析与设计方法

10.4.1　Peter Coad和Edward Yourdon的OOA和OOD方法

10.4.2　Booch的OOD方法

10.4.3　OMT方法

10.4.4　UML概述

10.5　设计模式

10.5.1　设计模式的要素

10.5.2　创建型设计模式

10.5.3　结构型设计模式

10.5.4　行为设计模式

第11章　标准化和软件知识产权基础知识

11.1　标准化基础知识

11.1.1　标准化的基本概念

11.1.2　信息技术标准化

11.1.3　标准化组织

11.1.4　ISO 9000标准简介

11.1.5　ISO/IEC 15504过程评估标准简介

11.2　知识产权基础知识

11.2.1　知识产权基本概念

11.2.2　计算机软件著作权

11.2.3　计算机软件的商业秘密权

11.2.4　专利权概述

11.2.5　企业知识产权的保护

第12章　软件系统分析与设计

12.1　结构化分析与设计

12.1.1　需求说明

12.1.2　结构化分析

12.1.3　总体设计

12.1.4　详细设计

12.2　数据库分析与设计

12.2.1　数据库设计的步骤

12.2.2　需求分析

12.2.3　概念结构设计

12.2.4　逻辑结构设计

12.2.5　数据库的物理设计

12.2.6　数据库实施与维护

12.2.7　案例分析

12.3　面向对象分析与设计

12.3.1　面向对象分析与设计的步骤

12.3.2　需求说明

12.3.3　建模用例

12.3.4　建模活动

12.3.5　设计类图

12.3.6　建模对象状态

12.3.7　建模序列图

12.4　算法分析与设计

12.4.1　算法与软件系统

12.4.2　算法设计过程

12.4.3　算法问题类型

12.4.4　现代优化计算方法

12.5　面向过程的程序设计与实现

12.5.1　指针类型

12.5.2　指针与数据结构

12.5.3　C语言实现面向对象设计思路

12.6　面向对象的程序设计与实现

12.6.1　设计与实现方法

12.6.2　设计模式的应用

全国计算机技术与软件专业技术资格（水平）考试指定用书

软件设计师教程

（第三版）

胡圣明　褚　华　主编

全国计算机专业技术资格考试办公室　组编

清华大学出版社

北　京

内容简介

本书作为中级职称的软考指定教材，具有比较权威的指导意义。本书根据《软件设计师考试大纲》的重点内容，阐述了12章的内容，考生在学习教材内容的同时，还须对照考试大纲（2009版），认真学习和复习大纲的知识点。

本书是在《软件设计师考试大纲》的指导下，对《软件设计师教程（第2版）》进行了认真的修编，重写而成。

本书适合参加本考试的考生和大学在校生作为教材。

本书扉页为防伪页，封面贴有清华大学出版社防伪标签，无标签者不得销售。

版权所有，侵权必究。侵权举报电话：010-62782989　13701121933

图书在版编目（CIP）数据

软件设计师教程（第三版）／胡圣明，褚华主编．—北京：清华大学出版社，2009.8

（全国计算机技术与软件专业技术资格（水平）考试指定用书）

ISBN 978-7-302-20584-5

Ⅰ．软…　Ⅱ．①胡…②褚…　Ⅲ．软件设计－工程技术人员－资格考核－自学参考资料　Ⅳ．TP311.5

中国版本图书馆CIP数据核字（2009）第117056号

责任编辑：柴文强　薛　阳

责任校对：徐俊伟

责任印制：

出版发行：清华大学出版社

地　　址：北京清华大学学研大厦A座

http://www.tup.com.cn

邮　编：100084

社总机：010-62770175

邮　购：010-62786544

投稿与读者服务：010-62776969，c-service@tup.tsinghua.edu.cn

质量反馈：010-62772015，zhiliang@tup.tsinghua.edu.cn

印刷者：

装订者：

经　　销：全国新华书店

开　　本：185×230　　　印　张：42.25

防伪页：　1　　　　　　字　数：890千字

版　　次：2009年8月第1版

印　　次：2009年8月第1次印刷

印　　数：

定　　价：　　元

本书如存在文字不清、漏印、缺页、倒页、脱页等印装质量问题，请与清华大学出版社出版部联系调换。联系电话：010-62770177转3103　产品编号：034385-01

序　言

软件产业是信息产业的核心之一，是经济社会发展的基础性、先导性和战略性产业，在推进信息化与工业化融合、促进发展方式转变和产业结构升级、维护国家安全等方面有着重要作用。党中央、国务院高度重视软件产业发展，先后出台了18号文件、47号文件等一系列政策措施，营造了良好的发展环境。近年来，我国软件产业进入快速发展期。2007年销售收入达到5834亿元，出口102.4亿美元，软件从业人数达148万人。全国共认定软件企业超过1.8万家，登记备案软件产品超过5万个。软件技术创新取得突破，国产操作系统、数据库、中间件等基础软件相继推出并得到了较好的应用。软件与信息服务外包蓬勃发展，软件正版化工作顺利推进。

随着软件产业的快速发展，软件人才需求日益迫切。为适应产业发展需求、规范软件专业人员技术资格，20余年前全国计算机软件考试创办，率先执行了以考代评政策。近年来，考试作了很多积极的探索，进行了一系列改革，考试名称、考试内容、专业类别、职业岗位也作了相应的变化。目前，考试名称已调整为计算机技术与软件专业技术资格（水平）考试，涉及5个专业类别、3个级别层次共27个职业岗位，采取水平考试的形式，执行资格考试政策，并扩展到高级资格，取得了良好效果。20余年来，累计报考人数近200万，影响力不断扩大。程序员、软件设计师、系统分析师、网络工程师、数据库系统工程师的考试标准已与日本相应考试级别实现互认，程序员和软件设计师的考试标准与韩国实现互认。通过考试，一大批软件人才脱颖而出，为加快培育软件人才队伍、推动软件产业健康发展起到了重要作用。

最近，工业和信息化部电子教育与考试中心组织了一批具有较高理论水平和丰富实践经验的专家编写了这套全国计算机技术与软件专业技术资格（水平）考试教材和辅导用书。按照考试大纲的要求，教材和辅导用书全面介绍相关知识与技术，帮助考生学习备考，将为软件考试的规范和完善起到积极作用。

我相信，通过社会各界共同努力，全国计算机技术与软件专业技术资格（水平）考试将更加规范、科学，培养出更多专业技术人才，为加快发展信息产业、推动信息化与工业化融合做出积极贡献。

工业和信息化部副部长

前　言

全国计算机技术与软件专业技术资格（水平）考试实施至今已经历了二十余年，在社会上产生了很大的影响，对我国软件产业的形成和发展做出了重要的贡献。为了适应我国计算机信息技术发展的需求，人力资源和社会保障部、工业和信息产业部决定将考试的级别拓展到计算机信息技术行业的各个方面，以满足社会上对各种计算机信息技术人才的需要。

编者受全国计算机专业技术资格考试办公室委托，对《软件设计师教程（第2版）》一书进行修订，以适应新的考试大纲要求。在考试大纲中，要求考生掌握的知识面很广，每个章节的内容都能构成相关领域的一门课程，因此编写的难度很高。考虑到参加考试的人员已有一定的基础，所以本书中只对考试大纲中所涉及到的知识领域的要点加以阐述，但限于篇幅所限，不能详细地展开，请读者谅解。

全书共分12章，各章节内容安排如下：

第1章主要介绍计算机系统基础知识、计算机体系结构以及安全性、可靠性和系统性能评测基础。

第2章主要介绍程序设计语言的基本概念与基本成分，阐述了汇编程序、编译程序与解释程序的基本原理。

第3章主要介绍操作系统中处理机管理、存储管理、设备管理、文件管理、作业管理以及网络语嵌入式操作系统基础知识。

第4章主要介绍软件工程基础知识、系统分析与设计基础知识、系统实施知识以及系统运行和维护知识。

第5章主要介绍网络体系结构、网络互联硬件、网络协议与标准、Internet应用以及网络安全知识。

第6章主要介绍多媒体的基本概念、声音的基本概念和格式、图形和图像、动画和视频、多媒体网络和多媒体计算机系统。

第7章主要介绍数据库的基本概念、数据模型、关系代数、SQL语言、规范化理论和事务处理。

第8章主要介绍数据结构中线性结构、数组、矩阵、广义表、树和图的基本概念，阐述了查找和排序的基本方法和算法。

第9章主要介绍算法设计与分析的基本概念，详细阐述了分治法、动态规划法、贪心法、回溯法、分支界限法、概率算法以及近似算法。

第10章主要介绍面向对象的基本概念、面向对象程序设计与开发技术、讨论了面向对象分析与设计方法，介绍了设计模式的概念和应用。

第11章主要介绍标准化与知识产权基础知识。

第12章主要介绍结构化分析与设计、数据库分析与设计、面向对象分析与设计、算法分析与设计以及面向过程、面向对象的程序设计语实现。

本书第1章计算机系统知识、第2章程序语言基础知识由张淑平编写，第3章操作系统知识由王亚平编写，第4章系统开发和运行知识由褚华、霍秋燕编写，第5章网络基础知识由严体华编写，第6章多媒体基础知识由刘强编写，第7章数据库技术基础由王亚平编写，第8章数据结构由张淑平编写，第9章算法设计与分析由覃桂敏、褚华编写，第10章面向对象技术由褚华、胡圣明、陈平编写，第11章标准化和软件知识产权基础知识由刘强编写，第12章软件系统分析与设计由王亚平、胡圣明、霍秋燕、覃桂敏、张淑平编写，最后由胡圣明统稿。

在本书的编写过程中，参考了许多相关的书籍和资料，编者在此对这些参考文献的作者表示感谢。同时感谢清华大学出版社在本书出版过程中所给予的支持和帮助。

因水平有限，书中难免存在错漏和不妥之处，望读者指正，以利改进和提高。

编　者

2009年4月

第1章　计算机系统知识

1.1　计算机系统基础知识

1.1.1　计算机系统硬件基本组成

计算机的基本硬件系统由运算器、控制器、存储器、输入设备和输出设备5大部件组成。运算器、控制器等部件被集成在一起统称为中央处理元（Central Processing Unit, CPU）。CPU是硬件系统的核心，用于数据的加工处理，能完成各种算术、逻辑运算及控制功能。存储器是计算机系统中的记忆设备，分为内部存储器和外部存储器。前者速度高、容量小，一般用于临时存放程序、数据及中间结果。而后者容量大、速度慢，可以长期保存程序和数据。输入设备和输出设备合称为外部设备（简称外设），输入设备用于输入原始数据及各种命令，而输出设备则用于输出计算机运行的结果。

1.1.2　中央处理单元

1．CPU的功能

（1）程序控制。CPU通过执行指令来控制程序的执行顺序，这是CPU的重要职能。

（2）操作控制。一条指令功能的实现需要若干操作信号来完成，CPU产生每条指令的操作信号并将操作信号送往不同的部件，控制相应的部件按指令的功能要求进行操作。

（3）时间控制。CPU对各种操作进行时间上的控制，这就是时间控制。CPU对每条指令的整个执行时间要进行严格控制。同时，指令执行过程中操作信号的出现时间、持续时间及出现的时间顺序都需要进行严格控制。

（4）数据处理。CPU通过对数据进行算术运算及逻辑运算等方式进行加工处理，数据加工处理的结果被人们所利用。所以，对数据的加工处理是CPU最根本的任务。

2．CPU的组成

CPU主要由运算器、控制器、寄存器组和内部总线等部件组成，如图1-1所示。

图1-1　CPU基本组成结构示意图

1）运算器

运算器由算术逻辑单元（Arithmetic and Logic Unit, ALU）、累加寄存器、数据缓冲寄存器和状态条件寄存器组成，它是数据加工处理部件，完成计算机的各种算术和逻辑运算。相对控制器而言，运算器接受控制器的命令而进行动作，即运算器所进行的全部操作都是由控制器发出的控制信号来指挥的，所以它是执行部件。运算器有如下两个主要功能。

（1）执行所有的算术运算，如加、减、乘、除等基本运算及附加运算。

（2）执行所有的逻辑运算并进行逻辑测试，如与、或、非、零值测试或两个值的比较等。

下面简要介绍运算器的各组成部件的组成和功能。

（1）算术逻辑单元。ALU是运算器的重要组成部件，负责处理数据，实现对数据的算术运算和逻辑运算。

（2）累加寄存器（AC）。AC通常简称为累加器，它是一个通用寄存器。其功能是当运算器的算术逻辑单元执行算术或逻辑运算时，为ALU提供一个工作区。例如，在执行一个减法运算前，先将被减数暂取出放在AC中，再从内存储器中取出减数，然后同AC的内容相减，所得的结果送回AC中。运算的结果是放在累加器中的，运算器中至少要有一个累加寄存器。

（3）数据缓冲寄存器（DR）。在对内存储器进行读写操作时，用DR暂时存放由内存储器读写的一条指令或一个数据字，将不同时间段内读写的数据隔离开来。DR的主要作用为：作为CPU和内存、外部设备之间数据传送的中转站；作为CPU和内存、外围设备之间在操作速度上的缓冲；在单累加器结构的运算器中，数据缓冲寄存器还可兼作为操作数寄存器。

（4）状态条件寄存器（PSW）。PSW保存由算术指令和逻辑指令运行或测试的结果建立的各种条件码内容，主要分为状态标志和控制标志，如运算结果进位标志（C）、运算结果溢出标志（V）、运算结果为0标志（Z）、运算结果为负标志（N）、中断标志（I）、方向标志（D）和单步标志等。这些标志通常分别由1位触发器保存，保存了当前指令执行完成之后的状态。通常，一个算术操作产生一个运算结果，而一个逻辑操作则产生一个判决。

2）控制器

运算器只能完成运算，而控制器用于控制整个CPU的工作，它决定了计算机运行过程的自动化。它不仅要保证程序的正确执行，而且要能够处理异常事件。控制器一般包括指令控制逻辑、时序控制逻辑、总线控制逻辑和中断控制逻辑等几个部分。

指令控制逻辑要完成取指令、分析指令和执行指令的操作，其过程分为取指令、指令译码、按指令操作码执行、形成下一条指令地址等步骤。

（1）指令寄存器（IR）。当CPU执行一条指令时，先把它从内存储器取到缓冲寄存器中，再送入IR暂存，指令译码器根据IR的内容产生各种微操作指令，控制其他的组成部件工作，完成所需的功能。

（2）程序计数器（PC）。PC具有寄存信息和计数两种功能，又称为指令计数器。程序的执行分两种情况，一是顺序执行，二是转移执行。在程序开始执行前，将程序的起始地址送入PC，该地址在程序加载到内存时确定，因此PC的内容即是程序第一条指令的地址。执行指令时，CPU将自动修改PC的内容，以便使其保持的总是将要执行的下一条指令的地址。由于大多数指令都是按顺序来执行的，所以修改的过程通常只是简单地对PC加1。当遇到转移指令时，后继指令的地址根据当前指令的地址加上一个向前或向后转移的位移量得到，或者根据转移指令给出的直接转移的地址得到。

（3）地址寄存器（AR）。AR保存当前CPU所访问的内存单元的地址。由于内存和CPU存在着操作速度上的差异，所以需要使用AR保持地址信息，直到内存的读／写操作完成为止。

（4）指令译码器（ID）。指令分为操作码和地址码两部分，为了能执行任何给定的指令，必须对操作码进行分析，以便识别所完成的操作。指令译码器就是对指令中的操作码字段进行分析解释，识别该指令规定的操作，向操作控制器发出具体的控制信号，控制各部件工作，完成所需的功能。

时序控制逻辑要为每条指令按时间顺序提供应有的控制信号。总线逻辑是为多个功能部件服务的信息通路的控制电路。中断控制逻辑用于控制各种中断请求，并根据优先级的高低对中断请求进行排队，逐个交给CPU处理。

3）寄存器组

寄存器组可分为专用寄存器和通用寄存器。运算器和控制器中的寄存器是专用寄存器，其作用是固定的。通用寄存器用途广泛并可由程序员规定其用途，其数目因处理器不同有所差异。

3．多核CPU

核心（Die）又称为内核，是CPU最重要的组成部分。CPU中心那块隆起的芯片就是核心，是由单晶硅以一定的生产工艺制造出来的，CPU所有的计算、接收／存储命令、处理数据都由核心执行。各种CPU核心都具有固定的逻辑结构，一级缓存、二级缓存、执行单元、指令级单元和总线接口等逻辑单元都会有科学的布局。

多核即在一个单芯片上面集成两个甚至更多个处理器内核，其中每个内核都有自己的逻辑单元、控制单元、中断处理器、运算单元，一级Cache、二级Cache共享或独有，其部件的完整性和单核处理器内核相比完全一致。

CPU的主要厂商AMD和Intel的双核技术在物理结构上有很大不同。AMD将两个内核做在一个Die（晶元）上，通过直连架构连接起来，集成度更高。Intel则是将放在不同Die上的两个内核封装在一起，因此有人将Intel的方案称为“双芯”，将AMD的方案称为“双核”。从用户端的角度来看，AMD的方案能够使双核CPU的管脚、功耗等指标跟单核CPU保持一致，从单核升级到双核，不需要更换电源、芯片组、散热系统和主板，只需要刷新BIOS软件即可。

1.1.3　数据表示

各种数值在计算机中表示的形式称为机器数，其特点是采用二进制计数制，数的符号用0，1表示，小数点则隐含表示而不占位置。机器数对应的实际数值称为数的真值。

机器数有无符号数和带符号数之分。无符号数表示正数，在机器数中没有符号位。对于无符号数，若约定小数点的位置在机器数的最低位之后，则是纯整数；若约定小数点的位置在机器数的最高位之前，则是纯小数。对于带符号数，机器数的最高位是表示正、负的符号位，其余位则表示数值。若约定小数点的位置在机器数的最低数值位之后，则是纯整数；若约定小数点的位置在机器数的最高数值位之前（符号位之后），则是纯小数。

为了便于运算，带符号的机器数可采用原码、反码和补码等不同的编码方法，机器数的这些编码方法称为码制。

1）原码、反码、补码和移码

（1）原码表示法。数值X的原码记为[X]原
，如果机器字长为n（即采用n个二进制位表示数据），则原码的定义如下：

若X是纯整数，则

若X是纯小数，则

【例1.1】　若机器字长n等于8，分别给出＋1，－1，＋127，－127，＋45，－45，＋0.5，－0.5的原码表示。

在原码表示法中，最高位是符号位，0表示正号，1表示负号，其余的n－1位表示数值的绝对值。数值0的原码表示有两种形式：[＋0]原
＝00000000，[－0]原
＝10000000。

（2）反码表示法。数值X的反码记作[X]反
，如果机器字长为n，则反码定义如下：

若X是纯整数，则

若X是纯小数，则

【例1.2】　若机器字长n等于8，分别给出＋1，－1，＋127，－127，＋45，－45，＋0.5，－0.5的反码表示。

在反码表示中，最高位是符号位，0表示正号，1表示负号，正数的反码与原码相同，负数的反码则是其绝对值按位求反。数值0的反码表示有两种形式：[＋0]反
＝00000000，[－0]反
＝11111111。

（3）补码表示法。数值X的补码记作[X]补
，如果机器字长为n，则补码定义如下：

若X是纯整数，则

若X是纯小数，则

【例1.3】　若机器字长n等于8，分别给出＋1，－1，＋127，－127，＋45，－45，＋0.5，－0.5的补码表示。

在补码表示中，最高位为符号位，0表示正号，1表示负号，正数的补码与其原码和反码相同，负数的补码则等于其反码的末尾加1。在补码表示中，0有唯一的编码：[＋0]补
＝00000000，[－0]补
＝00000000。

（4）移码表示法。移码表示法是在数X上增加一个偏移量来定义的，常用于表示浮点数中的阶码。如果机器字长为n，规定偏移量为2n－1
，则移码定义如下：

若X是纯整数，则[X]移
＝2n－1
＋X（－2n－1
≤X＜2n－1
）；若X是纯小数，则[X]补
＝1＋X（－1≤X＜1）。

【例1.4】　若机器字长n等于8，分别给出＋1，－1，＋127，－127，＋45，－45，＋0.5，－0.5，＋0，－0的移码表示。

实际上，在偏移2n－1
的情况下，只要将补码的符号位取反便可获得相应的移码表示。

2）定点数和浮点数

（1）定点数。所谓定点数，就是小数点的位置固定不变的数。小数点的位置通常有两种约定方式：定点整数（纯整数，小数点在最低有效数值位之后）和定点小数（纯小数，小数点在最高有效数值位之前）。

设机器字长为n，各种码制下带符号数的范围如表1-1所示。

表1-1　机器字长为n时各种码制表示的带符号数的范围

（2）浮点数。当机器字长为n时，定点数的补码和移码可表示2n－1
个数，而其原码和反码只能表示2n－1
－1个数（0的表示占用了两个编码），因此，定点数所能表示的数值范围比较小，运算中很容易因结果超出范围而溢出。因此引入浮点数，浮点数是小数点位置不固定的数，它能表示更大范围的数。

在十进制中，一个数可以写成多种表示形式。例如，83.125可写成10^3
×0.083125或10^4
×0.0083125等。同样，一个二进制数也可以写成多种表示形式。例如，二进制数1011.10101可以写成2^4
×0.101110101、2^5
×0.0101110101或2^6
×0.00101110101等。由此可知，一个二进制数N可以表示为更一般的形式N＝2E
×F，其中E称为阶码，F叫做尾数。用阶码和尾数表示的数叫做浮点数，这种表示数的方法称为浮点表示法。

在浮点表示法中，阶码通常为带符号的纯整数，尾数为带符号的纯小数。浮点数的表示格式如下：

很明显，一个数的浮点表示不是唯一的。当小数点的位置改变时，阶码也随着相应改变，因此可以用多种浮点形式表示同一个数。

浮点数所能表示的数值范围主要由阶码决定，所表示数值的精度则由尾数决定。为了充分利用尾数来表示更多的有效数字，通常采用规格化浮点数。规格化就是将尾数的绝对值限定在区间[0.5，1]。当尾数用补码表示时，需要注意如下问题。

①若尾数M≥0，则其规格化的尾数形式为M＝0.1×××…×，其中×可为0，也可为1，即将尾数限定在区间[0.5，1]。

②若尾数M＜0，则其规格化的尾数形式为M＝1.0×××…×，其中×可为0，也可为1，即将尾数M的范围限定在区间[－1，－0.5]。

如果浮点数的阶码（包括1位阶符）用R位的移码表示，尾数（包括1位数符）用M位的补码表示，则这种浮点数所能表示的数值范围如下。

最大的正数：
最小的负数：

（3）工业标准IEEE 754。IEEE 754是由IEEE制定的有关浮点数的工业标准，被广泛采用。该标准的表示形式如下：

其中，（－1）S
为该浮点数的数符，当S为0时表示正数，S为1时表示负数；E为指数（阶码），用移码表示；（b_0
b_1
b_2
b_3
…bp－1
）为尾数，其长度为P位，用原码表示。

目前，计算机中主要使用三种形式的IEEE 754浮点数，如表1-2所示。

表1-2　三种形式的IEEE 754浮点数格式

续表

在IEEE 754标准中，约定小数点左边隐含有一位，通常这位数就是1，因此单精度浮点数尾数的有效位数为24位，即尾数为1.××…×。

【例1.5】　利用IEEE 754标准将数176.0625表示为单精度浮点数。

解：首先将该十进制转换成二进制数。

（176.0625）10
＝（10110000.0001）2

其次对二进制数进行规格化处理：10110000.0001＝1◇01100000001×2^7
。这就保证了使b_0
为1，而且小数点应当在◇位置上。将b_0
去掉并扩展为单精度浮点数所规定的23位尾数01100000001000000000000。

然后求阶码，上述表示中的指数为7，而单精度浮点数规定指数的偏移量为127（注意，不是前面移码描述中所提到的128），即在指数7上加127。那么，E＝7＋127＝134，则指数的移码表示为10000110。

最后，可得到（176.0625）10
的单精度浮点数表示形式：

0　　10000110　01100000001000000000000

（4）浮点数的运算。设有浮点数X＝M×2j
，Y＝N×2j
，求X±Y的运算过程要经过对阶、求尾数和（差）、结果规格化并判溢出、舍入处理和溢出判别等步骤。

①对阶。使两个数的阶码相同。令K＝｜i－j｜，把阶码小的数的尾数右移K位，使其阶码加上K。

②求尾数和（差）。

③结果规格化并判溢出。若运算结果所得的尾数不是规格化的数，则需要进行规格化处理。当尾数溢出时，需要调整阶码。

④舍入。在对结果右规时，尾数的最低位将因移出而丢掉。另外，在对阶过程中也会将尾数右移使最低位丢掉。这就需要进行舍入处理，以求得最小的运算误差。

⑤溢出判别。以阶码为准，若阶码溢出，则运算结果溢出；若阶码下溢（小于最小值），则结果为0；否则结果正确无溢出。

浮点数相乘，其积的阶码等于两乘数的阶码相加，积的尾数等于两乘数的尾数相乘。浮点数相除，其商的阶码等于被除数的阶码减去除数的阶码，商的尾数等于被除数的尾数除以除数的尾数。乘除运算的结果都需要进行规格化处理并判断阶码是否溢出。

1.1.4　校验码

计算机系统运行时，各个部件之间要进行数据交换，为了确保数据在传送过程中正确无误，一是提高硬件电路的可靠性，二是提高代码的校验能力，包括查错和纠错。通常使用校验码的方法来检测传送的数据是否出错。其基本思想是把数据可能出现的编码分为两类：合法编码和错误编码。合法编码用于传送数据，错误编码是不允许在数据中出现的编码。合理地设计错误编码以及编码规则，使得数据在传送中出现某种错误时就会变成错误编码，这样就可以检测出接收到的数据是否有错。

所谓码距，是指一个编码系统中任意两个合法编码之间至少有多少个二进制位不同。例如，4位8421码的码距为1，在传输过程中，该代码的一位或多位发生错误，都将变成另外一个合法编码，因此这种代码无差错能力。下面简要介绍常用的三种校验码：奇偶校验码、海明码和循环冗余校验码。

1．奇偶校验码（Parity Codes）

奇偶校验是一种简单有效的校验方法。这种方法通过在编码中增加一位校验位来使编码中1的个数为奇数（奇校验）或者为偶数（偶校验），从而使码距变为2。对于奇校验，它可以检测代码中奇数位出错的编码，但不能发现偶数位出错的情况，即当合法编码中奇数位发生了错误，即编码中的1变成0或0变成1，则该编码中1的个数的奇偶性就发生了变化，从而可以发现错误。

常用的奇偶校验码有三种：水平奇偶校验码、垂直奇偶校验码和水平垂直校验码。

2．海明码（Hamming Code）

海明码是由贝尔实验室的Richard Hamming设计的，它也是利用奇偶性来检错和纠错的校验方法。海明码的构成方法是：在数据位之间插入k个校验位，通过扩大码距来实现检错和纠错。

设数据位是n位，校验位是k位，则n和k必须满足以下关系：

2k
－1≥n＋k

海明码的编码规则如下。

设k个校验位为Pk
，Pk－1
，…，P_1
，n个数据位为Dn－1
，Dn－2
，…，D_1
，D_0
，对应的海明码为Hn＋k
，Hn＋k－1
，…，H_1
，那么：

（1）Pi
在海明码的第2i－1
位置，即Hj
＝Pi
，且j＝2i－1
；数据位则依序从低到高占据海明码中剩下的位置。

（2）海明码中的任一位都是由若干个校验位来校验的。其对应关系如下：被校验的海明位的下标等于所有参与校验该位的校验位的下标之和，而校验位则由自身校验。

对于8位的数据位，进行海明校验需要4个校验位（2^3
－1＝7，2^4
－1＝15＞8＋4）。令数据位为D_7
，D_6
，D_5
，D_4
，D_3
，D_2
，D_1
，D_0
，校验位为P_4
，P_3
，P_2
，P_1
，形成的海明码为H_12
，H_11
，…，H_3
，H_2
，H_1
，则编码过程如下。

（1）确定D与P在海明码中的位置，如下所示：

（2）确定校验关系，如表1-3所示。

表1-3　海明码的校验关系表

若采用奇校验，则将各校验位的偶校验值取反即可。

（3）检测错误。对使用海明编码的数据进行差错检测很简单，只需作以下计算：

若采用偶校验，则G_4
G_3
G_2
G_1
全为0时表示接收到的数据无错误（奇校验则应全为1）。当G_4
G_3
G_2
G_1
不全为0说明发生了差错，而且G_4
G_3
G_2
G_1
的十进制值指出了发生错误的位置，例如G_4
G_3
G_2
G_1
＝1010，说明H_10
（D_5
）出错了，将其取反即可纠正错误。

【例1.6】　设数据为01101001，试采用4个校验位求其偶校验方式的海明码。

解：D_7
D_6
D_5
D_4
D_3
D_2
D_1
D_0
＝01101001，根据公式

因此，求得的海明码为：

3．循环冗余校验码（Cyclic Redundancy Check, CRC）

循环冗余校验码广泛应用于数据通信领域和磁介质存储系统中。它利用生成多项式为k个数据位产生r个校验位来进行编码，其编码长度为k＋r。CRC的代码格式为：

由此可知，循环冗余校验码是由两部分组成的，左边为信息码（数据），右边为校验码。若信息码占k位，则校验码就占n－k位。其中，n为CRC码的字长，所以又称为（n，k）码。校验码是由信息码产生的，校验码位数越长，该代码的校验能力就越强。在求CRC编码时，采用的是模2运算。模2运算加减运算的规则是按位运算，不发生借位和进位。

1.2　计算机体系结构

1.2.1　计算机体系结构的发展

1．计算机系统结构概述

1964年，阿姆达尔（G. M. Amdahl）在介绍IBM360系统时指出：计算机体系结构是站在程序员的角度所看到的计算机属性，即程序员要能编写出可在机器上正确运行的程序所必须了解的概念性结构和功能特性。

1982年，梅尔斯（G. J. Myers）在其所著《计算机体系结构的进展》（Advances in Computer Architecture）一书中，定义了组成计算机系统的若干层次，每一层都提供一定的功能支持它上面的一层，并把不同层之间的界面定义为某种类型的体系结构。Myers的定义发展了Amdahl的概念性结构的思想，明确了传统体系结构就是指硬件与软件之间的界面，即指令集体系结构。

1984年，拜尔（J. L. Baer）在一篇题为“计算机体系结构（Computer Architecture）”的文章中，给出了一个含义更加广泛的定义：体系结构是由结构、组织、实现、性能4个基本方面组成。其中，结构指计算机系统各种硬件的互连，组织指各种部件的动态联系与管理，实现指各模块设计的组装完成，性能指计算机系统的行为表现。这个定义发展了Amdahl的功能特性思想。显然，这里的计算机系统组织又成为体系结构的一个子集。

关于计算机体系结构、计算机组织和计算机实现三者的关系如下。

（1）计算机体系结构（computer architecture）是指计算机的概念性结构和功能属性。

（2）计算机组织（computer organization）是指计算机体系结构的逻辑实现，包括机器内的数据流和控制流的组成以及逻辑设计等（常称为计算机组成原理）。

（3）计算机实现（computer implementation）是指计算机组织的物理实现。

2．计算机体系结构分类

（1）宏观上按处理机的数量进行分类，分为单处理系统、并行处理与多处理系统和分布式处理系统。

·　单处理系统（uniprocessing system）。利用一个处理单元与其他外部设备结合起来，实现存储、计算、通信、输入与输出等功能的系统。

·　并行处理与多处理系统（parallel processing and multiprocessing system）。为了充分发挥问题求解过程中处理的并行性，利用两个以上的处理机互连起来，彼此进行通信协调，以便共同求解一个大问题的计算机系统。

·　分布式处理系统（distributed processing system）。指物理上远距离而松耦合的多计算机系统。其中，物理上的远距离意味着通信时间与处理时间相比已不可忽略，在通信线路上的数据传输速率要比在处理机内部总线上传输慢得多，这也正是松耦合的含义。

（2）微观上按并行程度分类，有Flynn分类法、冯泽云分类法、Handler分类法和Kuck分类法。

·　Flynn分类法。1966年，M. J. Flynn提出按指令流和数据流的多少进行分类。指令流为机器执行的指令序列，数据流是由指令调用的数据序列。Flynn把计算机系统的结构分为单指令流、单数据流（Single Instruction stream Single Data stream, SISD）、单指令流、多数据流（Single Instruction stream Multiple Data stream, SIMD）、指令流、单数据流（Multiple Instruction stream Single Data stream, MISD）和多指令流、多数据流（Multiple Instruction stream Multiple Data stream, MIMD）4类。

·　冯译云分类法。1972年，美籍华人冯泽云（Tse-yun Feng）提出按并行度对各种计算机系统进行结构分类。所谓最大并行度Pm，是指计算机系统在单位时间内能够处理的最大二进制位数。冯泽云把计算机系统分成字串行位串行（WSBS）计算机、字并行位串行（WPBS）计算机、字串行位并行（WSBP）计算机和字并行位并行（WPBP）计算机4类。

·　Handler分类法。1977年，德国的汉德勒（Wolfgang Handler）提出一个基于硬件并行程度计算并行度的方法，把计算机的硬件结构分为三个层次：处理机级、每个处理机中的算逻单元级、每个算逻单元中的逻辑门电路级。分别计算这三级中可以并行或流水处理的程序，即可算出某系统的并行度。

·　Kuck分类法。1978年，美国的库克（David J. Kuck）提出与Flynn分类法类似的方法，用指令流和执行流（execution stream）及其多重性来描述计算机系统控制结构的特征。Kuck把系统结构分为单指令流单执行流（SISE）、单指令流多执行流（SIME）、多指令流单执行流（MISE）和多指令流多执行流（MIME）4类。

3．指令系统

1）指令集体系结构的分类

从体系结构的观点对指令集进行分类，可以根据下述5种尺度。

（1）操作数在CPU中的存储方式，即操作数从主存中取来后保存在什么地方。

（2）显式操作数的数量，即在典型的指令中有多少个显式命名的操作数。

（3）操作数的位置，即任一个ALU指令的操作数能否放在主存中，如何定位。

（4）指令的操作，即在指令集中提供哪些操作。

（5）操作数的类型与大小。

按暂存机制分类，根据在CPU内部存储操作数的区别，可以把指令集体系分为三类：堆栈（stack）、累加器（accumulator）和寄存器集（a set of registers）。

通用寄存器机（General-Purpose Register Machines, GPR机）的关键性优点是编译程序能有效地使用寄存器，无论是计算表达式的值，还是从全局的角度使用寄存器来保存变量的值。在表达式求解时，寄存器比堆栈或者累加器能提供更加灵活的次序。更重要的是，寄存器能用来保存变量。当变量分配给寄存器时，访存流量（memory traffic）就会减少，程序运行就会加速，而且代码密度也会得到改善。可以用指令集的两个主要特征来区分GPR体系结构。这两个特征都是关于ALU指令，即典型的算术逻辑指令中操作数的本质。第一个是ALU指令有两个或三个操作数。在三操作数格式中，指令包括两个源操作数和一个目的操作数；在二操作数格式中，有一个操作数，既是源操作数又是目的操作数。第二个是ALU指令中有几个操作数是存储器地址，对典型的ALU指令，这个数可能在1与3之间。

2）指令格式及其优化

设计指令格式时需要优先考虑一些原则，如指令长度、是否有足够的空间表示所有期望的操作、存取指令速度的提高及寻址字段长度的选择等。

指令格式的优化既包括指令码的优化，也包括操作数的优化。需要从整个指令系统的利用率角度出发，尽量设法减少指令中的冗余信息量，以便用最少的位数提供足够的操作信息和地址信息。对指令码进行优化表示，使用频度高的指令采用短编码，反之可采用长编码。

3）指令集的复杂化

（1）CISC和RISC是指令集发展的两个途径。

CISC（Complex Instruction Set Computer，复杂指令集计算机）的基本思想是：进一步增强原有指令的功能，用更为复杂的新指令取代原先由软件子程序完成的功能，实现软件功能的硬化，导致机器的指令系统越来越庞大而复杂。事实上，目前使用的绝大多数计算机都属于CISC类型。

CISC的主要弊病如下。

①指令集过分庞杂。

②微程序技术是CISC的重要支柱，每条复杂指令都要通过执行一段解释性微程序才能完成，这就需要多个CPU周期，从而降低了机器的处理速度。

③由于指令系统过分庞大，使高级语言编译程序选择目标指令的范围很大，并使编译程序本身冗长而复杂，从而难以优化编译使之生成真正高效的目标代码。

④CISC强调完善的中断控制，势必导致动作繁多，设计复杂，研制周期长。

⑤CISC给芯片设计带来很多困难，使芯片种类增多，出错几率增大，成本提高而成品率降低。

RISC（Reduced Instruction Set Computer，精简指令集计算机）的基本思想是：通过减少指令总数和简化指令功能，降低硬件设计的复杂度，使指令能单周期执行，并通过优化编译，提高指令的执行速度，采用硬线控制逻辑，优化编译程序。在20世纪70年代末开始兴起，导致机器的指令系统进一步精炼而简单。

RISC的关键技术如下。

①重叠寄存器窗口技术。在伯克利的RISC项目中，首先采用了重叠寄存器窗口（overlapping register windows）技术。

②优化编译技术。RISC使用了大量的寄存器，如何合理分配寄存器、提高寄存器的使用效率及减少访存次数等，都应通过编译技术的优化来实现。

③超流水及超标量技术。这是RISC为了进一步提高流水线速度而采用的技术。

④硬布线逻辑与微程序相结合在微程序技术中。

（2）面向目标代码的优化。

为了提高目标程序的实现效率，人们对大量的机器语言目标代码及其执行情况进行了统计。对程序中出现的各种指令以及指令串进行统计得到的百分比称为静态使用频度。在程序执行过程中，对出现的各种指令以及指令串进行统计得到的百分比称为动态使用频度。按静态使用频度来改进目标代码可减少目标程序所占的存储空间，按动态使用频度来改进目标代码可减少目标程序运行的执行时间。大量统计表明，动态和静态使用频度两者非常接近，最常用的指令是存、取、条件转移等。对它们加以优化，既可以减少程序所需的存储空间，又可以提高程序的执行速度。

（3）面向高级语言的优化。

尽可能缩小高级语言与机器语言之间的语义差距，以利于支持高级语言编译系统，缩短编译程序的长度和编译所需的时间。

（4）面向操作系统的优化。

进一步缩小操作系统与体系结构之间的语义差距，以利于减少操作系统运行所需的辅助时间，节省操作系统软件所占用的存储空间。操作系统的实现依赖于体系结构对它的支持。许多传统机器指令，例如算术逻辑指令、字符编辑指令、移位指令和控制转移指令等，都可用于操作系统的实现。此外，还有相当一部分指令是专门为实现操作系统的各种功能而设计的。

4）指令的流水处理

（1）指令控制方式。指令控制方式有顺序方式、重叠方式和流水方式三种。

①顺序方式。顺序方式是指各条机器指令之间顺序串行地执行，执行完一条指令后才取下一条指令，而且每条机器指令内部的各个微操作也是顺序串行地执行。这种方式的优点是控制简单。缺点是速度上不去，机器各部件的利用率低。

②重叠方式。重叠方式是指在解释第K条指令的操作完成之前，就可开始解释第K＋1条指令，如图1-2所示。通常采用的是一次重叠，即在任何时候，指令分析部件和指令执行部件都只有相邻两条指令在重叠解释。这种方式的优点是速度有所提高，控制也不太复杂。缺点是会出现冲突、转移和相关等问题，在设计时必须想办法解决。

图1-2　一次重叠处理

③流水方式。流水方式是模仿工业生产过程的流水线（如汽车装配线）而提出的一种指令控制方式。流水（pipelining）技术是把并行性或并发性嵌入到计算机系统里的一种形式，它把重复的顺序处理过程分解为若干子过程，每个子过程能在专用的独立模块上有效地并发工作。如图1-3所示。

图1-3　流水处理的时空图

在概念上，“流水”可以看成是“重叠”的延伸。差别仅在于“一次重叠”只是把一条指令解释分解为两个子过程，而“流水”则是分解为更多的子过程。

（2）流水线的种类。

①从流水的级别上，可分为部件级、处理机级以及系统级的流水。

②从流水的功能上，可分为单功能流水线和多功能流水线。

③从流水的联接上，可分为静态流水线和动态流水线。

④从流水是否有反馈回路，可分为线性流水线和非线性流水线。

⑤从流水的流动顺序上，可分为同步流水线和异步流水线。

⑥从流水线的数据表示上，可分为标量流水线和向量流水线。

（3）流水的相关处理。

局部性相关处理由于流水机器同时解释多条指令，这些指令可能有对同一主存单元或同一寄存器的“先写后读”的要求，这时就出现了相关。这种相关包括指令相关、访存操作数相关以及通用寄存器组相关等，它只影响相关的两条或几条指令，而且至多影响流水线的某些段推后工作，并不会改动指令缓冲器中预取到的指令内容，影响是局部的，所以称为局部性相关。解决局部性相关有两种方法：推后法和通路法。推后法是推后对相关单元的读，直至写入完成。通路法设置相关专用通路，使得不必先把运算结果写入相关存储单元，再从这里读出后才能使用，而是经过相关专用通路直接使用运算结果，以加快速度。

全部性相关处理转移指令（尤其是条件转移指令）与它后面的指令之间存在关联，使之不能同时解释。执行转移指令时，可能会改动指令缓冲器中预取到的指令内容，从而会造成流水线吞吐率和效率下降，比局部性相关的影响要严重得多，所以称为全局性相关。

解决全局性相关有三种方法：猜测转移分支、加快和提前形成条件码、加快短循环程序的处理。

条件转移指令有两个分支：一个分支是按原来的顺序继续执行下去，称为转移不成功分支；另一个分支是按转移后的新指令序列执行，称为转移成功分支。许多流水机器都猜选转移不成功分支，若猜对的机率很大，流水线的吞吐率和效率就会比不采用猜测法时高得多。

尽早获得条件码以便对流水线简化条件转移的处理。例如，一个乘法运算所需时间较长，但在运算之前就能知道其结果为正或为负，或者是否为0，因此，加快单条指令内部条件码的形成，或者在一段程序内提前形成条件码，对转移问题的顺利解决是很有好处的。

由于程序中广泛采用循环结构，因此流水线大都采用特殊措施以加快循环程序的处理。例如，使整个循环程序都放入指令缓冲存储器中，对提高流水效率和吞吐率均有明显效果。流水的中断处理中断和转移一样，也会引起流水线断流。好在中断出现的概率要比条件转移出现的概率低得多，因此只要处理好断点现场保护及中断后的恢复，尽量缩短断流时间即可。

RISC中采用的流水技术有三种：超流水线、超标量以及超长指令字。

①超流水线技术。超流水线（superpipeline）技术是RISC采用的一种并行处理技术。它通过细化流水、增加级数和提高主频，使得在每个机器周期内能完成一个甚至两个浮点操作。其实质是以时间换取空间。超流水机器的特征是在所有的功能单元都采用流水，并有更高的时钟频率和更深的流水深度。由于它只限于指令级的并行，所以超流水机器的CPI（Clock Cycles Per Instruction，每个指令需要的机器周期数）值稍高。

②超标量技术。超标量（superscalar）技术是RISC采用的又一种并行处理技术，它通过内装多条流水线来同时执行多个处理，其时钟频率虽然与一般流水接近，却有更小的CPI。其实质是以空间换取时间。

③超长指令字技术。超长指令字（Very Long Instruction Word, VLIW）技术由LIW发展而来。VLIW和超标量都是20世纪80年代出现的概念，其共同点都是要同时执行多条指令，其不同在于超标量依靠硬件来实现并行处理的调度，VLIW则充分发挥软件的作用，而使硬件简化，性能提高。例如，超标量同时分发4条指令已经很困难，但是VLIW同时分发8条指令的产品已经指日可待。因此，VLIW有更小的CPI值，但需要有足够高的时钟频率。

（4）吞吐率和流水建立时间。

吞吐率是指单位时间里流水线处理机流出的结果数。对指令而言，就是单位时间里执行的指令数。如果流水线的子过程所用时间不一样，则吞吐率p应为最长子过程的倒数，即

流水线开始工作，须经过一定时间才能达到最大吞吐率，这就是建立时间。若m个子过程所用时间一样，均为∆t_0
，则建立时间T_0
＝m∆t_0
。

4．阵列处理机、并行处理机和多处理机

并行性包括同时性和并发性两个侧面。其中，同时性是指两个或两个以上的事件在同一时刻发生，并发性是指两个或两个以上的事件在同一时间间隔内连续发生。

从计算机信息处理的步骤和阶段的角度看，并行处理可分为如下几类。

（1）存储器操作并行；

（2）处理器操作步骤并行（流水线处理机）；

（3）处理器操作并行（阵列处理机）；

（4）指令、任务、作业并行（多处理机、分布处理系统、计算机网络）。

1）阵列处理机

阵列处理机将重复设置的多个处理单元（PU）按一定方式连成阵列，在单个控制部件（CU）控制下，对分配给自己的数据进行处理，并行地完成一条指令所规定的操作。这是一种单指令流多数据流计算机，通过资源重复实现并行性。

2）并行处理机

SIMD和MIMD是典型的并行计算机，SIMD有共享存储器和分布存储器两种形式。

具有共享存储器的SIMD结构（如图1-4所示）中，将若干个存储器构成统一的并行处理机存储器，通过互联网络ICN为整个并行系统的所有处理单元共享。其中，PE为处理单元，CU为控制部件，M为共享存储器，ICN为互联网络。

分布式存储器的SIMD处理机如图1-5所示，其中PE为处理单元，CU为控制部件，PEM为局部存储器，ICN为互联网络。含有多个同样结构的处理单元，通过寻径网络ICN以一定方式互相连接。

分布存储器的并行处理机结构中有两类存储器，一类存储器附属于主处理机，主处理机实现整个并行处理机的管理，在其附属的存储器内常驻操作系统。另一类是分布在各个处理单元上的存储器（即PEM），这类存储器用来保存程序和数据。在阵列控制部件的统一指挥下，实现并行操作。程序和数据通过主机装入控制存储器。通过控制部件的是单指令流，所以指令的执行顺序还是和单处理机一样，基本上是串行处理。指令送到控制部件进行译码。划分后的数据集合通过向量数据总线分布到所有PE的本地存储器PEM。PE通过数据寻径网络互连。数据寻径网络执行PE间的通信。控制部件通过执行程序来控制寻径网络。PE的同步由控制部件的硬件实现。

图1-4　具有共享存储器的SIMD结构

图1-5　具有分布存储器的SIMD结构

3）多处理机

多处理机系统是由多台处理机组成的系统，每台处理机有属于自己的控制部件，可以执行独立的程序，共享一个主存储器和所有的外部设备。它是多指令流多数据流计算机。在多处理机系统中，机间的互连技术决定着多处理机的性能。多处理机之间的互连，要满足高频带、低成本、连接方式的多样性以及在不规则通信情况下连接的无冲突性。

4）其他计算机

集群一般是指连接在一起的两个或多个计算机（节点）。集群计算机是一种并行或分布式处理系统，由很多连接在一起的独立计算机组成，像一个单集成的计算机资源一样协同工作，主要用来解决大型计算问题。计算机节点可以是一个单处理器或多处理器的系统，拥有内存、I/O设备和操作系统。连接在一起的计算机集群对用户和应用程序来说像一个单一的系统，这样的系统可以提供一种价格合理的且可获得所需性能和快速而可靠的服务的解决方案。

网格计算是伴随着因特网技术而迅速发展起来的，专门用于复杂科学计算的新型计算模式。这种计算模式是利用因特网把分散在不同地理位置的计算机组织成一个“虚拟的超级计算机”，其中每台参与计算的计算机就是一个“节点”，整个计算是由成千上万个“节点”组成的“一张网格”，所以这种计算方式叫网格计算。网格类似于集群，但是与集群相比，节点更加分散，节点的组织方式也更加灵活，相应地，平均每个节点所能提供的计算能力一般比集群要低，并且节点间的通信效率也较低。

1.2.2　存储系统

1．存储器的层次结构

存储体系结构包括不同层次上的存储器，通过适当的硬件、软件有机地组合在一起形成计算机的存储体系结构。现在大多数人都将高性能计算机的存储体系结构描述成图1-6所示的三层存储器层次结构。

三层存储器结构是高速缓存（Cache）、主存储器（Main memory, MM）和辅助存储器（外存储器）。也有人将存储器层次分为4层，即将CPU内部的寄存器也看做是存储器的一个层次。

有一些简单的计算机没有高速缓存，那么这样的计算机存储体系就剩下主存和辅存两个层次。

图1-6　存储器层次结构示意图

2．存储器的分类

1）按存储器所处位置分

按存储器所处的位置，可分为内存和外存。

（1）内存。也称为主存，设在主机内或主机板上，用来存放机器当前运行所需要的程序和数据，以便向CPU提供信息。相对于外存，其特点是容量小、速度快。

（2）外存。也称为辅存，如磁盘、磁带和光盘等，用来存放当前不参加运行的大量信息，在需要时，可把需要的信息调入内存。相对于内存，外存的容量大、速度慢。

2）按存储器的构成材料分

按构成存储器的材料，可分为磁存储器、半导体存储器和光存储器。

（1）磁存储器。是用磁性介质做成的，如磁芯、磁泡、磁膜、磁鼓、磁带及磁盘等。

（2）半导体存储器。根据所用元件又可分为双极型和MOS型；根据数据是否需要刷新，又可分为静态（Static memory）和动态（Dynamic memory）两类。

（3）光存储器。如光盘（Optical Disk）存储器。

3）按存储器的工作方式分

按存储器的工作方式可分为读写存储器和只读存储器。

（1）读写存储器（Random Access Memory, RAM）。既能读取数据也能存入数据的存储器。

（2）只读存储器。根据数据的写入方式，这种存储器又可细分为ROM、PROM、EPROM和EEPROM等类型。

①固定只读存储器（Read Only Memory, ROM）。这种存储器是在厂家生产时就写好数据的，其内容只能读出，不能改变。一般用于存放系统程序BIOS和用于微程序控制。

②可编程的只读存储器（Programmable Read Only Memory, PROM）。其中的内容可以由用户一次性地写入，写入后不能再修改。

③可擦除可编程的只读存储器（Erasable Programmable Read Only Memory, EPROM）。其中的内容既可以读出，也可以由用户写入，写入后还可以修改。改写的方法是写入之前先用紫外线照射15～20分钟以擦去所有信息，然后再用特殊的电子设备写入信息。

④电擦除可编程的只读存储器（Electrically Erasable Programmable Read Only Memory, EEPROM）。与EPROM相似，EEPROM中的内容既可以读出，也可以进行改写。只不过这种存储器是用电擦除的方法进行数据的改写。

⑤闪速存储器（Flash Memory）。简称闪存，闪存的特性介于EPROM和EEPROM之间，类似于EEPROM，也可使用电信号进行信息的擦除操作。整块闪存可以在数秒内删除，速度远快于EPROM。

4）按访问方式分

按访问方式可分为按地址访问的存储器和按内容访问的存储器。

5）按寻址方式分

按寻址方式可分为随机存储器、顺序存储器和直接存储器。

（1）随机存储器（Random Access Memory, RAM）。这种存储器可对任何存储单元存入或读取数据，访问任何一个存储单元所需的时间是相同的。

（2）顺序存储器（Sequentially Addressed Memory, SAM）。访问数据所需要的时间与数据所在的存储位置相关，磁带是典型的顺序存储器。

（3）直接存储器（Direct Addressed Memory, DAM）。介于随机存取和顺序存取之间的一种寻址方式。磁盘是一种直接存取存储器，它对磁道的寻址是随机的，而在一个磁道内则是顺序寻址。

3．相联存储器

相联存储器是一种按内容访问的存储器。其工作原理就是把数据或数据的某一部分作为关键字，将该关键字与存储器中的每一单元进行比较，找出存储器中所有与关键字相同的数据字。

相联存储器的结构如图1-7所示，各部件的功能如表1-4所示。

表1-4　部件功能说明

相联存储器可用在高速缓冲存储器中；在虚拟存储器中用来作段表、页表或快表存储器；用在数据库和知识库中。

4．高速缓存

高速缓存是用来存放当前最活跃的程序和数据，作为主存局部域的副本，其特点是：容量一般在几千字节到几兆字节之间；速度一般比主存快5～10倍，由快速半导体存储器构成；其内容是主存局部域的副本，对程序员来说是透明的。

1）高速缓存的组成

高速缓存的组成如图1-8所示。Cache由两部分组成：控制部分和Cache存储器部分。

Cache存储器部分用来存放主存的部分拷贝（副本）信息。控制部分的功能是判断CPU要访问的信息是否在Cache存储器中，若在即为命中，若不在则没有命中。命中时直接对Cache存储器寻址；未命中时，要按照替换原则决定主存的一块信息放到Cache存储器的哪一块里。

图1-7　相联存储器的结构框图

图1-8　高速缓存的构成框图

2）高速缓存中的地址映像方法

在CPU工作时，送出的是主存的地址，而应从Cache存储器中读写信息。这就需要将主存地址转换成Cache存储器的地址，这种地址的转换叫做地址映像。Cache的地址映像有如下三种方法。

（1）直接映像。直接映像是指主存的块与Cache中块的对应关系是固定的。如图1-9所示。

在这种映像方式下，由于主存中的块只能存放在Cache存储器的相同块号中，因此，只要主存地址中的主存区号与Cache中的主存区号相同，则表明访问Cache命中。一旦命中，以主存地址中的区内块号立即可得到要访问的Cache存储器中的块，而块内地址就是主存地址中给出的低位地址。

图1-9　直接映像示意图

直接映像方式的优点是地址变换很简单，缺点是灵活性差。例如，不同区号中块号相同的块无法同时调入Cache存储器，即使Cache存储器中有空着的块也只能空着。

（2）全相联映像。全相联映像的示意图如图1-10所示。同样，主存与Cache存储器均分成容量相同的块。这种映像方式允许主存的任一块可以调入Cache存储器的任何一个块的空间中。

在地址变换时，利用主存地址高位表示的主存块号与Cache中的主存块号进行比较，若相同即为命中。这时根据块号所对应的块就可知道要访问的是哪一块。Cache存储器的块找到后，块内地址就是主存的低位地址。这时便可以读写Cache块中的内容。在变换时，当找到主存块号命中时，还必须知道主存的这一块存到了Cache的哪一块里面。

全相联映像的主要优点是主存的块调入Cache的位置不受限制，十分灵活。其主要缺点是无法从主存块号中直接获得Cache的块号，变换比较复杂，速度比较慢。

（3）组相联映像。这种方式是前面两种方式的折衷。具体做法是将Cache中的块再分成组。例如，假定Cache有16块，再将每两块分为1组，则Cache就分为8组。主存同样分区，每区16块，再将每两块分为1组，则每区就分为8组。

图1-10　全相联映像示意图

组相联映像就是规定组采用直接映像方式而块采用全相联映像方式。也就是说，主存任何区的0组只能存到Cache的0组中，1组只能存到Cache的1组中，依此类推。组内的块则采用全相联映像方式，即一组内的块可以任意存放。也就是说，主存一组中的任一块可以存入Cache相应组中任一块中。

这种方式下，通过直接映像方式来决定组号，在一组内再用全相联映像方式来决定Cache中的块号。由主存地址高位决定主存区号与Cache中区号比较可决定是否命中。主存后面的地址即为组号。

3）替换算法

替换算法的目标就是使Cache获得最高的命中率。常用算法有如下几种。

（1）随机替换算法。就是用随机数发生器产生一个要替换的块号，将该块替换出去。

（2）先进先出算法。就是将最先进入Cache的信息块替换出去。

（3）近期最少使用算法。这种方法是将近期最少使用的Cache中的信息块替换出去。

（4）优化替换算法。这种方法必须先执行一次程序，统计Cache的替换情况。有了这样的先验信息，在第二次执行该程序时便可以用最有效的方式来替换。

4）Cache的性能分析

Cache的性能是计算机系统性能的重要方面。命中率是Cache的一个重要指标，但不是最主要的指标。Cache设计的目标是在成本允许的条件下达到较高的命中率，使存储系统具有最短的平均访问时间。设Hc
为Cache的命中率，tc
为Cache的存取时间，tm
为主存的访问时间，则Cache存储器的等效加权平均访问时间ta
为：

这里假设Cache访问和主存访问是同时启动的，其中，tc
为Cache命中时的访问时间，（tm
－tc
）为失效访问时间。如果在Cache不命中时才启动主存，则

在指令流水线中，Cache访问作为流水线中的一个操作阶段，Cache失效将影响指令的流水。因此，降低Cache的失效率是提高Cache性能的一项重要措施。当Cache容量比较小时，容量因素在Cache失效中占有比较大的比例。降低Cache失效率的方法主要有选择恰当的块容量、提高Cache的容量和提高Cache的相联度等。

Cache的命中率与Cache容量的关系是：Cache容量越大，则命中率越高，随着Cache容量的增加，其失效率接近0％（命中率逐渐接近100％）。但是，增加Cache容量意味着增加Cache的成本和增加Cache的命中时间。

5）多级Cache

在多级Cache的计算机中，Cache分为一级（L1 Cache）、二级（L2 Cache）等，CPU访存时首先查找L1 Cache，如果不命中，则访问L2 Cache，直到所有级别的Cache都不命中，才访问主存。目前，CPU内的Cache通常为二级结构。通常要求L1 Cache的速度足够快，以赶上CPU的主频。L1 Cache的容量一般都比较小，为几千字节到几十千字节；L2 Cache则具有较高的容量，一般为几百字节到几兆字节，以具有足够高的命中率。

5．虚拟存储器

虚拟存储（virtual memory）技术的概念是：把很大的程序（数据）分成许多较小的块，全部存储在辅存中。运行时，把要用到的程序（数据）块先调入主存，并且把马上就要用到的程序块从主存调入高速缓存。这样，一边运行程序，一边进行所需程序（数据）块的调进调出。只要及时供应所需处理的程序与数据，程序就能顺利而高速地运行下去。因此，对于应用程序员来说就好像有一个比实际主存大得多且可以放下整个程序的虚拟主存空间。当辅存中的程序块调入主存时，必须使程序在主存中定位，该工作由系统自动完成，应用程序员不用考虑如何把程序地址映像和变换成实际主存的物理地址，因此，虚存技术对于应用程序员来说是透明的。

虚拟存储器管理方式分为如下几种。

（1）页式虚拟存储器。以页为信息传送单位的虚拟存储器。通常一页为几百字节到几千字节。为实现页式管理，需建立虚页与实页间的关系表，称为页表。在页表及变换软件的控制下，可将程序的虚拟地址变换为主存的实地址。页式管理的优点是页表硬件少，查表速度快；主存零头少。其缺点是分页无逻辑意义，不利于存储保护。

（2）段式虚拟存储器。以程序的逻辑结构形成的段（如某一独立程序模块、子程序等）作为主存分配依据的一种段式虚拟存储器的管理方法。为实现段式管理，需建立段表；在段地址变换机构及软件的控制下，可将程序的虚拟地址变换为主存的实地址。段式管理的优点是段的界限分明；支持程序的模块化设计；易于对程序段的编译、修改和保护；便于多道程序的共享。主要缺点是因为段的长度不一，主存利用率不高，产生大量内存碎片，造成浪费；段表庞大，查表速度慢。

（3）段页式虚拟存储器。是页式虚拟存储器和段式虚拟存储器两者相结合的一种管理方式。在这种虚拟存储器中，程序按逻辑结构分段，每一段再分成若干大小固定的页。程序的调入调出是按页进行的，而程序又可按段实现保护。因此，这种管理方式兼有前两者的优点，只是地址变换速度比较慢。

6．外存储器

外存储器用来存放暂时不用的程序和数据，并且以文件的形式存储。CPU不能直接访问外存中的程序和数据，只有将其以文件为单位调入主存方可访问。外存储器由磁表面存储器（如磁盘、磁带）及光盘存储器构成。下面介绍两种常用的外存储器。

1）磁盘存储器

在磁表面存储器中，磁盘的存取速度较快，且具有较大的存储容量，是目前广泛使用的外存储器。磁盘存储器由盘片、驱动器、控制器和接口组成。盘片用来存储信息。驱动器用于驱动磁头沿盘面径向运动以寻找目标磁道位置，驱动盘片以额定速率稳定旋转，并且控制数据的写入和读出。控制器接收主机发来的命令，将它转换成磁盘驱动器的控制命令，并实现主机和驱动器之间数据格式的转换及数据传送，以控制驱动器的读／写操作。一个控制器可以控制一台或多台驱动器。接口是主机和磁盘存储器之间的连接逻辑。

硬盘是最常见的外存储器。一个硬盘驱动器内可装有多个盘片，组成盘片组，每个盘片都配有一个独立的磁头。所有记录面上相同序号的磁道构成一个圆柱面，其编号与磁道编号相同。文件存储在硬盘上时尽可能放在同一圆柱面上，或者放在相邻柱面上，这样可以缩短寻道时间。

为了正确存储信息，将盘片划成许多同心圆，称为磁道，从外到里编号，最外一圈为0道，往内道号依次增加。沿径向的单位距离的磁道数称为道密度，单位为tpi（每英寸磁道数）。将一个磁道沿圆周等分为若干段，每段称为一个扇段或扇区，每个扇区内可存放一个固定长度的数据块，如512B。磁道上单位距离可记录的位数称为位密度，单位为bpi（每英寸位数）。因为每条磁道上的扇区数相同，而每个扇区的大小又一样，所以每条磁道都记录同样多的信息。又因为里圈磁道圆周比外圈磁道的圆周小，所以里圈磁道的位密度要比外圈磁道的位密度高。最内圈的位密度称为最大位密度。

硬盘的寻址信息由硬盘驱动号、圆柱面号、磁头号（记录面号）、数据块号（或扇区号）以及交换量组成。

磁盘容量有两种指标：一种是非格式化容量，它是指一个磁盘所能存储的总位数；另一种是格式化容量，它是指各扇区中数据区容量总和。计算公式分别如下：

非格式化容量＝面数×（磁道数／面）×内圆周长×最大位密度

格式化容量＝面数×（磁道数／面）×（扇区数／道）×（字节数／扇区）

按盘片是否固定、磁头是否移动等指标，硬盘可分为移动磁头固定盘片的磁盘存储器、固定磁头的磁盘存储器、移动磁头可换盘片的磁盘存储器和温彻斯特磁盘存储器（简称温盘）。

2）光盘存储器

光盘存储器是一种采用聚焦激光束在盘式介质上非接触地记录高密度信息的新型存储装置。

根据性能和用途，可分为只读型光盘（CD-ROM）、只写一次型光盘（WORM）和可擦除型光盘，只读型光盘是由生产厂家预先用激光在盘片上蚀刻不能再改写的各种信息，目前这类光盘使用很普遍。只写一次型光盘是指由用户一次写入、可多次读出但不能擦除的光盘，写入方法是利用聚焦激光束的热能，使光盘表面发生永久性变化而实现的。可擦除型光盘是读写性光盘，它是利用激光照射引起介质的可逆性物理变化来记录信息。

光盘存储器由光学、电学和机械部件等组成。其特点是记录密度高；存储容量大；采用非接触式读／写信息（光头距离光盘通常为2mm）；信息可长期保存（其寿命达10年以上）；采用多通道记录时数据传送率可超过200MB/s；制造成本低；对机械结构的精度要求不高；存取时间较长。

7．磁盘阵列技术

磁盘阵列是由多台磁盘存储器组成的一个快速、大容量、高可靠的外存子系统。现在常见的称为廉价冗余磁盘阵列（Redundant Array of Independent Disk, RAID）。目前，常见的RAID如表1-5所示。

表1-5　廉价冗余磁盘阵列

续表

除此之外，上述各种类型的RAID还可以组合起来，构成复合型的RAID，此处不再赘述。

8．存储域网络

在大型服务器系统的背后都有一个网络，把一个或多个服务器与多个存储设备连接起来，每个存储设备可以是RAID、磁带备份系统、磁带库和CD-ROM库等，构成了存储域网络（Storage Area Network, SAN）。这样的网络不仅解决了服务器对存储容量的要求，还可以使多个服务器之间可以共享文件系统和辅助存储空间，避免数据和程序代码的重复存储，提高辅助存储器的利用率。另外，SAN还实现了分布式存储系统的集中管理，降低了大容量存储系统的管理成本，提高了管理效率。存储域网络式连接服务器与存储设备的网络，它能够将多个分布在不同地点的RAID组织成一个逻辑存储设备，供多个服务器共享访问，如图1-11所示。

图1-11　SAN的结构

1.2.3　输入输出技术

1．微型计算机中最常用的内存与接口的编址方式

计算机系统中存在多种内存与接口地址的编址方法，常见的是下面两种：内存与接口地址独立编址和内存与接口地址统一编址。

1）内存与接口地址独立编址方法

内存与接口地址独立编址方式下，内存地址和接口地址是完全独立的两个地址空间，它们是完全独立的并且是相互隔离的。访问数据时所使用的指令也完全不同，用于接口的指令只用于接口读写，其余的指令全都是用于内存的。因此，在编程序或读程序中很易使用和辨认。

这种编址方式的缺点就是用于接口的指令太少、功能太弱。

2）内存与接口地址统一编址方法

在这种编址方法里，内存地址和接口地址统一在一个公共的地址空间里，即内存单元和接口共用地址空间。在这些地址空间里划分出一部分地址分配给接口使用，其余地址归内存单元使用。分配给内存的地址区间只能用于内存单元，接口绝不允许使用。同样，分配给接口的地址区间内存单元也绝不能再用。

这种编址方法的优点是原则上用于内存的指令全都可以用于接口，这就大大地增强了对接口的操作功能，而且在指令上也不再区分内存或接口指令。

该编址方法的缺点就在于整个地址空间被分成两部分，其中一部分分配给接口使用，剩余的为内存所用，这经常会导致内存地址不连续。当用于内存的指令和用于接口的指令是完全一样的，维护程序时就需根据参数定义表仔细加以辨认。

2．直接程序控制

在完成外设数据的输入输出中，整个输入输出过程是在CPU执行程序的控制下完成的。这种方式分为无条件传送和程序查询方式两种情况。

1）无条件传送

在此情况下，外设总是准备好的，它可以无条件地随时接收CPU发来的输出数据，也能够无条件地随时向CPU提供需要输入的数据。

2）程序查询方式

在这种方式下，利用查询方式进行输入输出，就是通过CPU执行程序查询外设的状态，判断外设是否准备好接收数据或准备好了向CPU输入的数据。根据这种状态，CPU有针对性地为外设的输入输出服务。

通常，一个计算机系统中可以存在着多种不同的外设，如果这些外设是用查询方式工作，则CPU应对这些外设逐一进行查询，发现哪个外设准备就绪就对该外设服务。这种工作方式有两大缺点：

（1）降低了CPU的效率。在这种工作方式下，CPU不做别的事，只是不停地对外设的状态进行查询。在实际的工程应用中，对那些慢速的外设在不影响到外设工作时，CPU可以执行其他任务。

（2）对外部的突发事件无法做出实时响应。

3．中断方式

由程序控制I/O的方法，其主要缺点在于CPU必须等待I/O系统完成数据传输任务，在此期间CPU需定期地查询I/O系统的状态，以确认传输是否完成。因此，整个系统的性能严重下降。

利用中断方式完成数据的输入／输出过程为：当I/O系统与外设交换数据时，CPU无须等待也不必去查询I/O的状态，而可以抽身出来处理其他任务。当I/O系统准备好以后，则发出中断请求信号通知CPU，CPU接到中断请求信号后，保存正在执行程序的现场，转入I/O中断服务程序的执行，完成与I/O系统的数据交换，然后再返回被打断的程序继续执行。与程序控制方式相比，中断方式因为CPU无须等待而提高了效率。

1）中断处理方法

在系统中具有多个中断源的情况下，常用的处理方法有多中断信号线法（multiple interrupt lines）、中断软件查询法（software poll）、菊花链法（daisy chain）、总线仲裁法和中断向量表法。

（1）多中断信号线法。每个中断源都有属于自己的一根中断请求信号线向CPU提出中断请求。

（2）中断软件查询法。当CPU检测到一个中断请求信号以后，即转入到中断服务程序去轮询每个中断源以确定是谁发出了中断请求信号。对各个设备的响应优先级由软件设定。

（3）菊花链法。软件查询的缺陷在于花费时间太多。菊花链法实际上是一种硬件查询法。所有的I/O模块共享一根共同的中断请求线，而中断确认信号则以链式在各模块间相连。当CPU检测到中断请求信号，则发出中断确认信号。中断确认信号依次在I/O模块间传递，直到发出请求的模块，该模块则把它的ID送往数据线由CPU读取。

（4）总线仲裁法。一个I/O设备在发出中断请求之前，必须先获得总线控制权，所以可由总线仲裁机制来裁定谁可以发出中断请求信号。当CPU发出中断响应信号后，该设备即把自己ID发往数据线。

（5）中断向量表法。中断向量表用来保存各个中断源的中断服务程序的入口地址。当外设发出中断请求信号（INTR）以后，由中断控制器（INTC）确定其中断号，并根据中断号查找中断向量表来取得其中断服务程序的入口地址，同时INTC把中断请求信号提交给CPU，如图1-12所示。中断源的优先级由INTC来控制。

2）中断优先级控制

在具有多个中断源的计算机系统中，各中断源对服务的要求紧迫程度可能不同。在这样的计算机系统中，就需要按中断源的轻重缓急来安排对它们的服务。

在中断优先级控制系统中，给最紧迫的中断源分配高的优先级，而给那些要求相对不紧迫（例如几百微秒到几毫秒）的中断源分配低一些的优先级。在进行优先级控制时解决以下两种情况。

图1-12　中断向量表法

（1）当不同优先级的多个中断源同时提出中断请求时，CPU应优先响应优先级最高的中断源。

（2）当CPU正在对某一个中断源服务时，又有比它优先级更高的中断源提出中断请求，CPU应能暂时中断正在执行的中断服务程序而转去对优先级更高的中断源服务，服务结束后再回到原先被中断的优先级较低的中断服务程序继续执行。这种情况称为中断嵌套，即一个中断服务程序中嵌套着另一个中断服务程序。

4．直接存储器存取方式

在计算机与外设交换数据的过程中，无论是无条件传送、利用查询方式传送还是利用中断方式传送，都需要由CPU通过执行程序来实现。这就限制了数据的传送速度。

直接内存存取（Direct Memory Access, DMA）是指数据在内存与I/O设备间的直接成块传送，即在内存与I/O设备间传送一个数据块的过程中，不需要CPU的任何干涉，只需要CPU在过程开始启动（即向设备发出“传送一块数据”的命令）与过程结束（CPU通过轮询或中断得知过程是否结束和下次操作是否准备就绪）时的处理，实际操作由DMA硬件直接执行完成，CPU在此传送过程中可做别的事情。

DMA传送的一般过程如图1-13所示。

图1-13　DMA过程示意图

（1）外设向DMA控制器（DMAC）提出DMA传送的请求。

（2）DMA控制器向CPU提出请求，其请求信号通常加到CPU的保持请求输入端HOLD上。

（3）CPU在完成当前的总线周期后立即对此请求作出响应，CPU的响应包括两个方面的内容：一方面，CPU将有效的保持响应信号HLDA输出加到DMAC上，告诉DMAC它的请求已得到响应；同时，另一方面CPU将其输出的总线信号置为高阻，这就意味着CPU放弃了对总线的控制权。

（4）此时DMAC获得了对系统总线的控制权，开始实施对系统总线的控制。同时向提出请求的外设送出DMAC的响应信号，告诉外设其请求已得到响应，现在准备开始进行数据的传送。

（5）DMAC送出地址信号和控制信号，实现数据的高速传送。

（6）当DMAC将规定的字节数传送完时，它就将HOLD信号变为无效并加到CPU上。撤销对CPU的请求。CPU检测到无效的HOLD就知道DMAC己传送结束，CPU就送出无效的HLDA响应信号，同时重新获得系统总线的控制权，接着DMA前的总线周期继续执行下面的总线周期。

在此再强调说明，在DMA传送过程中无须CPU的干预，整个系统总线完全交给了DMAC，由它控制系统总线完成数据传送。在DMA传送数据时要占用系统总线，根据占用总线的方法不同，DMA可以分为中央处理器停止法、总线周期分时法和总线周期挪用法等。无论采用哪种方法，在DMA传送数据期间，CPU不能使用总线。

5．输入输出处理机（IOP）

上面所描述的输入输出方式适合于外设不多，速度不很高的小型或微型机中。在大型计算机中，外设很多，要求计算机的速度很高。采用程序传送、查询、中断或DMA均会因输入输出而造成过大的开销，影响计算机的整体性能。为此，提出采用输入输出处理机。

输入输出处理机是一个专用处理机，接在主计算机上，主机的输入输出操作由它来完成。它根据主机的I/O命令，完成对外设数据的输入输出。既然输入输出由IOP来完成了，主机的工作效率必然提高了。

输入输出处理机的数据传送方式有如下三种：字节多路方式、选择传送方式和数组多路方式。

1.2.4　总线结构

计算机系统的硬部件线以总线方式进行互连，以便于部件和设备的扩充，并制定统一的总线标准。随着微电子技术和计算机技术的发展，总线技术也在不断地发展和完善，因此，计算机总线技术种类繁多，各具特色。

1．总线的定义与分类

广义地讲，任何连接两个以上电子元器件的导线都可以称为总线。通常分为如下三类总线。

（1）内部总线。用于芯片一级的互连，分为芯片内总线和元件级总线。芯片内总线用于在集成电路芯片内部各部分的连接，元件级总线用于一块电路板内各元器件的连接。

（2）系统总线。用于插件板一级的互连，用于构成计算机各组成部分（CPU、内存和接口等）的连接。

（3）外部总线。又称通信总线，用于设备一级的互连，通过该总线和其他设备进行信息与数据交换。

2．系统总线

系统总线有时也称为内总线，其性能直接影响到计算机的性能。常见的内总线标准如下。

（1）ISA总线。ISA是工业标准总线，它向上兼容更早的PC总线，在PC总线62个插座信号的基础上，再扩充另一个具有36个信号的插座构成ISA总线。

ISA总线主要包括24条地址线，16条数据线，控制总线（内存读写、接口读写、中断请求、中断响应、DMA请求和DMA响应等），±5V、±12V电源和地线等。

（2）EISA总线。该总线是在ISA总线的基础上发展起来的32位总线。该总线定义32位地址线、32位数据线以及其他控制信号线、电源线、地线等共196个接点。总线传输速率达33MB/s。该总线利用总线插座与ISA总线相兼容，插板插在上层为ISA总线信号；将插板插在下层便是EISA总线。

（3）PCI总线。PCI总线是目前微型机上广泛采用的内总线。PCI总线有适于32位机的124个信号的标准和适于64位机的188个信号的标准。PCI总线的传输速率至少为133MB/s，64位PCI总线的传输速率为266MB/s，具有很高的传输速率。PCI总线的工作与处理器的工作是相互独立的，也就是说，PCI总线时钟与处理器时钟是独立的、非同步的。PCI总线上的设备是即插即用的。

3．外部总线

外总线的标准有七八十种之多，下面简要介绍几种。

（1）RS-232C。RS-232C是一条串行外总线，其主要特点是所需传输线比较少，最少只需三条线（一条发、一条收、一条地线）即可实现全双工通信。传送距离远，用电平传送为15m，电流环传送可达千米。有多种可供选择的传送速率。采用非归零码负逻辑工作，电平≤－3V为逻辑1，而电平≥＋3V为逻辑0，具有较好的抗干扰性。

（2）SCSI总线。小型计算机系统接口（SCSI）是一条并行外总线，广泛用于连接软硬磁盘、光盘、扫描仪等。该接口总线早期是8位的，后来发展到16位。传输速率由SCSI－1的5MB/s到16位的Ultra2 SCSI的80MB/s。今天的传输速率已高达320MB/s。该总线上最多可接63种外设，传输距离可达20m（差分传送）。

（3）USB。通用串行总线（USB）当前风头正劲，近几年得到十分广泛的应用。USB由条信号线组成，其中两条用于传送数据，另外两条传送＋5V容量为500mA的电源。可以经过集线器（Hub）进行树状连接，最多可达5层。该总线上可接127个设备。USB 1.0有两种传送速率：低速为1.5Mb/s，高速为12Mb/s。USB 2.0的传送速率为480Mb/s。USB总线最大的优点还在于它支持即插即用，并支持热插拔。

（4）IEEE-1394。IEEE-1394是高速串行外总线，近几年得到广泛应用。IEEE-1394也支持外设热插拔，可为外设提供电源，省去了外设自带的电源，能连接多个不同设备，支持同步和异步数据传输。IEEE-1394由6条信号线组成，其中两条用于传送数据，两条传送控制信号，另外两条传送8～40V容为1500mA的电源。资料上介绍，IEEE-1394总线上可接63个设备。IEEE-1394的传送速率从400Mb/s，800Mb/s，1600Mb/s直到3.2Gb/s。

（5）IEEE-488总线。IEEE-488是并行总线接口标准。微计算机、数字电压表、数码显示器等设备及其他仪器仪表均可用IEEE-488总线连接装配，它按照位并行、字节串行双向异步方式传输信号，连接方式为总线方式，仪器设备不需中介单元直接并联于总线上。总线上最多可连接15台设备。最大传输距离为20m，信号传输速度一般为500KB/s，最大传输速度为1MB/s。

1.3　安全性、可靠性与系统性能评测基础知识

1.3.1　计算机安全概述

计算机安全是指计算机资产的安全，是要保证这些计算机资产不受自然和人为的有害因素的威胁和危害。计算机资产是由系统资源和信息资源两大部分组成。系统资源包括硬件、软件、配套设备设施、有关文件资料，还可以包括有关的服务系统和业务工作人员。信息资源包括计算机系统中存储、处理和传输的大量各种各样的信息。

1．信息安全的基本要素

信息安全的5个基本要素为机密性、完整性、可用性、可控性和可审查性。

（1）机密性。确保信息不暴露给未受权的实体或进程。

（2）完整性。只有得到允许的人才能修改数据，并能够判别出数据是否已被篡改。

（3）可用性。得到授权的实体在需要时可访问数据。

（4）可控性。可以控制授权范围内的信息流向及行为方式。

（5）可审查性。对出现的安全问题提供调查的依据和手段。

计算机安全是一个涵盖非常广的课题，既包括硬件、软件和技术，又包括安全规划、安全管理和安全监督。计算机安全可包括安全管理、通信与网络安全、密码学、安全体系及模型、容错与容灾、涉及安全的应用程序及系统开发、法律、犯罪及道德规范等领域。

其中安全管理是非常重要的，作为信息系统的管理部门应根据管理原则和该系统处理数据的保密性，制定相应的管理制度或规范。例如，根据工作的重要程度确定系统的安全等级，根据确定的安全等级确定安全管理的范围，制定相应的机房管理制度、操作规程、系统维护措施以及应急措施等。

2．计算机的安全等级

计算机系统中的三类安全性是指技术安全性、管理安全性和政策法律安全性。但是，一个安全产品的购买者如何知道产品的设计是否符合规范，是否能解决计算机网络的安全问题，不同的组织机构各自都制定了一套安全评估准则。一些重要的安全评估准则如下。

（1）美国国防部和国家标准局推出的《可信计算机系统评估准则》（TCSEC）。

（2）加拿大的《可信计算机产品评估准则》（CTCPEC）。

（3）美国制定的《联邦（最低安全要求）评估准则》（FC）。

（4）欧洲英、法、德、荷四国国防部门信息安全机构联合制定的《信息技术安全评估准则》（ITSEC），该准则事实上已成为欧盟各国使用的共同评估标准。

（5）美国制定的《信息技术安全通用评估准则》（简称CC标准），国际标准组织（ISO）于1996年批准CC标准以ISO/IEC 15408—1999名称正式列入国际标准系列。

其中，美国国防部和国家标准局的《可信计算机系统评测标准》TCSEC/TDI将系统划分为4组7个等级，如表1-6所示。

表1-6　安全性的级别

3．安全威胁

随着信息交换的激增，安全威胁所造成的危害越来越被受到重视，因此对信息保密的需求也从军事、政治和外交等领域迅速扩展到民用和商用领域。所谓安全威胁，是指某个人、物、事件对某一资源的机密性、完整性、可用性或合法性所造成的危害。某种攻击就是威胁的具体实现。安全威胁分为两类：故意（如黑客渗透）和偶然（如信息发往错误的地址）。

典型的安全威胁举例如表1-7所示。

表1-7　典型的安全威胁

4．影响数据安全的因素

影响数据安全的因素有内部和外部两类。

（1）内部因素。可采用多种技术对数据加密；制定数据安全规划；建立安全存储体系，包括容量、容错数据保护和数据备份等；建立事故应急计划和容灾措施；重视安全管理，制定数据安全管理规范。

（2）外部因素。可将数据分成不同的密级，规定外部使用员的权限。设置身份认证、密码、设置口令、设置指纹和声纹笔迹等多种认证。设置防火墙，为计算机建立一道屏障，防止外部入侵破坏数据。建立入侵检测、审计和追踪，对计算机进行防卫。同时，也包括计算机物理环境的保障、防辐射、防水和防火等外部防灾措施。

1.3.2　加密技术和认证技术

1．加密技术

加密技术是最常用的安全保密手段，数据加密技术的关键在于加密／解密算法和密钥管理。数据加密的基本过程就是对原来为明文的文件或数据按某种加密算法进行处理，使其成为不可读的一段代码，通常称为“密文”。“密文”只能在输入相应的密钥之后才能显示出原来的内容，通过这样的途径使数据不被窃取。

数据加密和数据解密是一对逆过程。数据加密是用加密算法E和加密密钥K_1
将明文P变换成密文C，记为

数据解密是数据加密的逆过程，解密算法D和解密密钥K_2
将密文C变换成明文P，记为

在安全保密中，可通过适当的密钥加密技术和管理机制来保证网络信息的通信安全。密钥加密技术的密码体制分为对称密钥体制和非对称密钥体制两种。相应地，对数据加密的技术分为两类，即对称加密（私人密钥加密）和非对称加密（公开密钥加密）。

1）对称加密技术

对称加密采用了对称密码编码技术，其特点是文件加密和解密使用相同的密钥，这种方法在密码学中叫做对称加密算法。

常用的对称加密算法有如下几种。

（1）数据加密标准（Digital Encryption Standard, DES）算法。DES主要采用替换和移位的方法加密。它用56位密钥对64位二进制数据块进行加密，每次加密可对64位的输入数据进行16轮编码，经一系列替换和移位后，输入的64位原始数据转换成完全不同的64位输出数据。DES算法运算速度快，密钥生产容易，适合于在当前大多数计算机上用软件方法实现，同时也适合于在专用芯片上实现。

（2）三重DES（3DES，或称TDEA）。在DES的基础上采用三重DES，即用两个56位的密钥K_1
和K_2
，发送方用K_1
加密，K_2
解密，再使用K_1
加密。接收方则使用K_1
解密，K_2
加密，再使用K_1
解密，其效果相当于将密钥长度加倍。

（3）RC-5（Rivest Cipher 5）。RC-5是由Ron Rivest（公钥算法的创始人之一）在1994年开发出来的。RC-5是在RCF2040中定义的，RSA数据安全公司的很多产品都使用了RC-5。

（4）国际数据加密算法（International Data Encryption Adleman, IDEA）。IDEA是在DES算法的基础上发展起来的，类似于三重DES。IDEA的密钥为128位，这么长的密钥在今后若干年内应该是安全的。类似于DES，IDEA算法也是一种数据块加密算法，它设计了一系列加密轮次，每轮加密都使用从完整的加密密钥中生成的一个子密钥。IDEA加密标准由PGP（Pretty Good Privacy）系统使用。

（5）高级加密标准（Advanced Encryption Standard, AES）算法。AES算法基于排列和置换运算。排列是对数据重新进行安排，置换是将一个数据单元替换为另一个。AES使用几种不同的方法来执行排列和置换运算。

AES是一个迭代的、对称密钥分组的密码，它可以使用128、192和256位密钥，并且用128位（16字节）分组加密和解密数据。

2）非对称加密技术

与对称加密算法不同，非对称加密算法需要两个密钥：公开密钥（publickey）和私有密钥（privatekey）。公开密钥与私有密钥是一对，如果用公开密钥对数据进行加密，只有用对应的私有密钥才能解密；如果用私有密钥对数据进行加密，那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥，所以这种算法叫作非对称加密算法。

非对称加密有两个不同的体制，如图1-14所示。

图1-14　非对称加密体制模型

非对称加密算法实现机密信息交换的基本过程是：甲方生成一对密钥并将其中的一把作为公用密钥向其他方公开；得到该公用密钥的乙方使用该密钥对机密信息进行加密后再发送给甲方；甲方再用自己保存的另一把专用密钥对加密后的信息进行解密。甲方只能用其专用密钥解密由其公用密钥加密后的任何信息。

非对称加密算法的保密性比较好，它消除了最终用户交换密钥的需要，但加密和解密花费时间长、速度慢，不适合于对文件加密，而只适用于对少量数据进行加密。

RSA（Rivest, Shamir and Adleman）算法是一种公钥加密算法，它按照下面的要求选择公钥和密钥。

（1）选择两个大素数p和q（大于10^100
）。

（2）令n＝p×q和z＝（p－1）×（q－1）。

（3）选择d与z互质。

（4）选择e，使e×d＝1（mod z）。

明文P被分成k位的块，k是满足2k
＜n的最大整数，于是有0≤P＜n。加密时计算

这样公钥为（e，n）。解密时计算

即私钥为（d，n）。

例如，设p＝3，q＝11，n＝33，z＝20，d＝7，e＝3，C＝P^3
（mod 33），P＝C^7
（mod 33），则有

RSA算法的安全性是基于大素数分解的困难性。攻击者可以分解已知的n，得到p和q，然后可得到z，最后用Euclid算法，由e和z得到d。但是要分解200位的数，需要40亿年；分解500位的数，则需要10^25
年。

3）密钥管理

密钥是有生命周期的，它包括密钥和证书的有效时间，以及已撤销密钥和证书的维护时间等。密钥既然要求保密，这就涉及到密钥的管理问题，管理不好，密钥同样可能被无意识地泄露，并不是有了密钥就高枕无忧，任何保密也只是相对的，是有时效的。密钥管理主要是指密钥对的安全管理，包括密钥产生、密钥备份、密钥恢复和密钥更新等。

（1）密钥产生。密钥对的产生是证书申请过程中重要的一步，其中产生的私钥由用户保留，公钥和其他信息则交于CA中心进行签名，从而产生证书。根据证书类型和应用的不同，密钥对的产生也有不同的形式和方法。对普通证书和测试证书，一般由浏览器或固定的终端应用来产生，这样产生的密钥强度较小，不适合应用于比较重要的安全网络交易。而对于比较重要的证书，如商家证书和服务器证书等，密钥对一般由专用应用程序或CA中心直接产生，这样产生的密钥强度大，适合于重要的应用场合。

另外，根据密钥的应用不同，也可能会有不同的产生方式。例如，签名密钥可能在客户端或RA中心产生，而加密密钥则需要在CA中心直接产生。

（2）密钥备份和恢复。在一个PKI（Public Key Infrastructure，公开密钥体系）系统中，维护密钥对的备份至关重要，如果没有这种措施，当密钥丢失后，将意味着加密数据的完全丢失，对于一些重要数据，这将是灾难性的。所以，密钥的备份和恢复也是PKI密钥管理中的重要一环。换句话说，即使密钥丢失，使用PKI的企业和组织必须仍能够得到确认，受密钥加密保护的重要信息也必须能够恢复。当然，不能让一个独立的个人完全控制最重要的主密钥，否则将引起严重后果。

企业级的PKI产品至少应该支持用于加密的安全密钥的存储、备份和恢复。密钥一般用口令进行保护，而口令丢失则是管理员最常见的安全疏漏之一。所以，PKI产品应该能够备份密钥，即使口令丢失，它也能够让用户在一定条件下恢复该密钥，并设置新的口令。

（3）密钥更新。如果用户可以一次又一次地使用同样密钥与别人交换信息，那么密钥也同其他任何密码一样存在着一定的安全性，虽然说用户的私钥是不对外公开的，但是也很难保证私钥长期的保密性，很难保证长期以来不被泄露。如果某人偶然地知道了用户的密钥，那么用户曾经和另一个人交换的每一条消息都不再是保密的了。另外，使用一个特定密钥加密的信息越多，提供给窃听者的材料也就越多，从某种意义上来讲也就越不安全了。

对每一个由CA颁发的证书都会有有效期，密钥对生命周期的长短由签发证书的CA中心来确定，各CA系统的证书有效期限有所不同，一般大约为2～3年。当用户的私钥被泄漏或证书的有效期快到时，用户应该更新私钥。这时用户可以废除证书，产生新的密钥对，申请新的证书。

（4）多密钥的管理。假设在某机构中有100个人，如果他们任意两人之间可以进行秘密对话，那么总共需要多少密钥呢？每个人需要知道多少密钥呢？也许很容易得出答案，如果任何两个人之间要不同的密钥，则总共需要4950个密钥，而且每个人应记住99个密钥。如果机构的人数是1000，10000人或更多，这种办法显然就过于愚蠢了，管理密钥将是一件非常困难的事情。为此需要研究并开发用于创建和分发密钥的加密安全的方法。

Kerberos提供了一种解决这个问题的较好方案，它是由MIT发明的，使保密密钥的管理和分发变得十分容易，但这种方法本身还存在一定的缺点。为能在因特网上提供一个实用的解决方案，Kerberos建立了一个安全的、可信任的密钥分发中心（Key Distribution Center, KDC），每个用户只要知道一个和KDC进行会话的密钥就可以了，而不需要知道成百上千个不同的密钥。

2．认证技术

认证技术主要解决网络通信过程中通信双方的身份认可。认证的过程涉及到加密和密钥交换。通常，加密可使用对称加密、不对称加密及两种加密方法的混合方法。认证方一般有账户名／口令认证、使用摘要算法认证和基于PKI的认证。

一个有效的PKI系统必须是安全的和透明的，用户在获得加密和数字签名服务时，不需要详细地了解PKI的内部运作机制。在一个典型、完整和有效的PKI系统中，除了具有证书的创建和发布，特别是证书的撤销功能外，一个可用的PKI产品还必须提供相应的密钥管理服务，包括密钥的备份、恢复和更新等。没有一个好的密钥管理系统，将极大地影响一个PKI系统的规模、可伸缩性和在协同网络中的运行成本。在一个企业中，PKI系统必须有能力为一个用户管理多对密钥和证书；能够提供安全策略编辑和管理工具，如密钥周期和密钥用途等。

PKI是一种遵循既定标准的密钥管理平台，能够为所有网络应用提供加密和数字签名等密码服务及所必需的密钥和证书管理体系。简单来说，PKI就是利用公钥理论和技术建立的提供安全服务的基础设施。PKI技术是信息安全技术的核心，也是电子商务的关键和基础技术。PKI的基础技术包括加密、数字签名、数据完整性机制、数字信封和双重数字签名等。完整的PKI系统必须具有权威认证机构（CA）、数字证书库、密钥备份及恢复系统、证书作废系统、应用接口（Application Programming Interface, API）等基本构成部分。

（1）认证机构。即数字证书的申请及签发机关，CA必须具备权威性的特征。

（2）数字证书库。用于存储已签发的数字证书及公钥，用户可由此获得所需的其他用户的证书及公钥。

（3）密钥备份及恢复系统。如果用户丢失了用于解密数据的密钥，则数据将无法被解密，这将造成合法数据丢失。为避免这种情况，PKI提供备份与恢复密钥的机制。但须注意，密钥的备份与恢复必须由可信的机构来完成。并且，密钥备份与恢复只能针对解密密钥，签名私钥为确保其唯一性而不能够作备份。

（4）证书作废系统。证书作废处理系统是PKI的一个必备组件。与日常生活中的各种身份证件一样，证书有效期以内也可能需要作废，原因可能是密钥介质丢失或用户身份变更等。为实现这一点，PKI必须提供作废证书的一系列机制。

（5）应用接口。PKI的价值在于使用户能够方便地使用加密、数字签名等安全服务，因此一个完整的PKI必须提供良好的应用接口系统，使得各种各样的应用能够以安全、一致、可信的方式与PKI交互，确保安全网络环境的完整性和易用性。

PKI采用证书进行公钥管理，通过第三方的可信任机构（认证中心，即CA）把用户的公钥和用户的其他标识信息捆绑在一起，其中包括用户名和电子邮件地址等信息，以在Internet上验证用户的身份。PKI把公钥密码和对称密码结合起来，在Internet上实现密钥的自动管理，保证网上数据的安全传输。

因此，从大的方面来说，所有提供公钥加密和数字签名服务的系统，都可归结为PKI系统的一部分，PKI的主要目的是通过自动管理密钥和证书，为用户建立起一个安全的网络运行环境，使用户可以在多种应用环境下方便地使用加密和数字签名技术，从而保证网上数据的机密性、完整性和有效性。数据的机密性是指数据在传输过程中不能被非授权者偷看；数据的完整性是指数据在传输过程中不能被非法篡改；数据的有效性是指数据不能被否认。

PKI发展的一个重要方面就是标准化问题，它也是建立互操作性的基础。目前，PKI标准化主要有两个方面：一是RSA公司的公钥加密标准（Public Key Cryptography Standards, PKCS），它定义了许多基本PKI部件，包括数字签名和证书请求格式等；二是由Internet工程任务组（Internet Engineering Task Force, IETF）和PKI工作组（Public Key Infrastructure Working Group, PKIX）所定义的一组具有互操作性的公钥基础设施协议。在今后很长的一段时间内，PKCS和PKIX将会并存，大部分的PKI产品为保持兼容性，也将会对这两种标准进行支持。

1）Hash函数与信息摘要（Message Digest）

Hash（哈希）函数提供了这样一种计算过程：输入一个长度不固定的字符串，返回一串定长度的字符串，又称Hash值。单向Hash函数用于产生信息摘要。Hash函数主要可以解决以下两个问题：在某一特定的时间内，无法查找经Hash操作后生成特定Hash值的原报文；也无法查找两个经Hash操作后生成相同Hash值的不同报文。这样，在数字签名中就可以解决验证签名和用户身份验证、不可抵赖性的问题。

信息摘要简要地描述了一份较长的信息或文件，它可以被看作一份长文件的“数字指纹”。信息摘要用于创建数字签名，对于特定的文件而言，信息摘要是唯一的。信息摘要可以被公开，它不会透露相应文件的任何内容。MD2、MD4和MD5（MD表示信息摘要）是由Ron Rivest设计的专门用于加密处理的，并被广泛使用的Hash函数，它们产生一种128位信息摘要，除彻底地搜寻外，没有更快的方法对其加以攻击，而其搜索时间一般需要1025年之久。

2）数字签名

数字签名主要经过以下几个过程。

（1）信息发送者使用一个单向散列函数（Hash函数）对信息生成信息摘要。

（2）信息发送者使用自己的私钥签名信息摘要。

（3）信息发送者把信息本身和已签名的信息摘要一起发送出去。

（4）信息接收者通过使用与信息发送者使用的同一个单向散列函数（Hash函数）对接收的信息本身生成新的信息摘要，再使用信息发送者的公钥对信息摘要进行验证，以确认信息发送者的身份和信息是否被修改过。

数字加密主要经过以下几个过程。

（1）当信息发送者需要发送信息时，首先生成一个对称密钥，用该对称密钥加密要发送的报文。

（2）信息发送者用信息接收者的公钥加密上述对称密钥。

（3）信息发送者将第（1）步和第（2）步的结果结合在一起传给信息接收者，称为数字信封。

（4）信息接收者使用自己的私钥解密被加密的对称密钥，再用此对称密钥解密被发送方加密的密文，得到真正的原文。

数字签名和数字加密的过程虽然都使用公开密钥体系，但实现的过程正好相反，使用的密钥对也不同。数字签名使用的是发送方的密钥对，发送方用自己的私有密钥进行加密，接收方用发送方的公开密钥进行解密，这是一个一对多的关系，任何拥有发送方公开密钥的人都可以验证数字签名的正确性。数字加密则使用的是接收方的密钥对，这是多对一的关系，任何知道接收方公开密钥的人都可以向接收方发送加密信息，只有唯一拥有接收方私有密钥的人才能对信息解密。另外，数字签名只采用了非对称密钥加密算法，它能保证发送信息的完整性、身份认证和不可否认性；而数字加密采用了对称密钥加密算法和非对称密钥加密算法相结合的方法，它能保证发送信息的保密性。

3）SSL协议

SSL（Secure Sockets Layer，安全套接层）协议最初是由Netscape Communication公司设计开发的，主要用于提高应用程序之间数据的安全系数。SSL协议的整个概念可以被总结为：一个保证任何安装了安全套接字的客户和服务器间事务安全的协议，它涉及所有TC/IP应用程序。SSL协议主要提供如下三方面的服务。

（1）用户和服务器的合法性认证。认证用户和服务器的合法性，使得它们能够确信数据将被发送到正确的客户端和服务器上。客户端和服务器都是有各自的识别号，这些识别号由公开密钥进行编号，为了验证用户是否合法，安全套接层协议要求在握手交换数据时进行数字认证，以此来确保用户的合法性。

（2）加密数据以隐藏被传送的数据。安全套接层协议所采用的加密技术既有对称密钥技术，也有公开密钥技术。在客户端与服务器进行数据交换之前，交换SSL初始握手信息，在SSL握手信息中采用了各种加密技术对其加密，以保证其机密性和数据的完整性，并且用数字证书进行鉴别，这样就可以防止非法用户进行破译。

（3）保护数据的完整性。安全套接层协议采用Hash函数和机密共享的方法来提供信息的完整性服务，建立客户端与服务器之间的安全通道，使所有经过安全套接层协议处理的业务在传输过程中能全部完整准确无误地到达目的地。

安全套接层协议是一个保证计算机通信安全的协议，对通信对话过程进行安全保护，其实现过程主要经过如下几个阶段。

（1）接通阶段。客户端通过网络向服务器打招呼，服务器回应。

（2）密码交换阶段。客户端与服务器之间交换双方认可的密码，一般选用RSA密码算法，也有的选用Diffie-Hellmanf和Fortezza-KEA密码算法。

（3）会谈密码阶段。客户端与服务器间产生彼此交谈的会谈密码。

（4）检验阶段。客户端检验服务器取得的密码。

（5）客户认证阶段。服务器验证客户端的可信度。

（6）结束阶段。客户端与服务器之间相互交换结束的信息。

当上述动作完成之后，两者间的资料传送就会加密，另外一方收到资料后，再将编码资料还原。即使盗窃者在网络上取得编码后的资料，如果没有原先编制的密码算法，也不能获得可读的有用资料。

发送时，信息用对称密钥加密，对称密钥用非对称算法加密，再把两个包绑在一起传送过去。接收的过程与发送正好相反，先打开有对称密钥的加密包，再用对称密钥解密。

在电子商务交易过程中，由于有银行参与，按照SSL协议，客户的购买信息首先发往商家，商家再将信息转发银行，银行验证客户信息的合法性后，通知商家付款成功，商家再通知客户购买成功，并将商品寄送客户。

4）数字时间戳技术

数字时间戳就是数字签名技术的一种变种应用。在电子商务交易文件中，时间是十分重要的信息。在书面合同中，文件签署的日期和签名一样均是十分重要的防止文件被伪造和篡改的关键性内容。数字时间戳服务（Digital Time Stamp Service, DTS）是网上电子商务安全服务项目之一，能提供电子文件的日期和时间信息的安全保护。

时间戳是一个经加密后形成的凭证文档，包括如下三个部分。

（1）需加时间戳的文件的摘要（digest）。

（2）DTS收到文件的日期和时间。

（3）DTS的数字签名。

一般来说，时间戳产生的过程为：用户首先将需要加时间戳的文件用Hash编码加密形成摘要，然后将该摘要发送到DTS，DTS在加入了收到文件摘要的日期和时间信息后再对该文件加密（数字签名），然后送回用户。

书面签署文件的时间是由签署人自己写上的，而数字时间戳则是由认证单位DTS来加入的，以DTS收到文件的时间为依据。

1.3.3　计算机可靠性

1．计算机可靠性概述

计算机系统的硬件故障通常是由元器件的失效引起的。对元器件进行寿命试验并根据实际资料统计得知，元器件的可靠性可分成三个阶段，开始阶段器件工作处于不稳定期，失效率较高；第二阶段器件进入正常工作期，失效率最低，基本保持常数；第三阶段元器件开始老化，失效率又重新提高。这就是所谓的“浴盆曲线”。因此，应保证在计算机中使用的元器件处于第二阶段。在第一阶段应对元器件进行老化筛选，而到了第三个阶段，则淘汰该计算机。

计算机系统的可靠性是指从它开始运行（t＝0）到某时刻t这段时间内能正常运行的概率，用R（t）表示。所谓失效率，是指单位时间内失效的元件数与元件总数的比例，用λ表示，当λ为常数时，可靠性与失效率的关系为

典型的失效率与时间的关系曲线如图1-15所示。

两次故障之间系统能正常工作的时间的平均值称为平均无故障时间（MTBF），即

通常用平均修复时间（MTRF）来表示计算机的可维修性，即计算机的维修效率，指从故障发生到机器修复平均所需要的时间。计算机的可用性是指计算机的使用效率，它以系统在执行任务的任意时刻能正常工作的概率A来表示，即

图1-15　失效率特性

计算机的RAS技术就是指用可靠性R、可用性A和可维修性S三个指标衡量一个计算机系统。但在实际应用中，引起计算机故障的原因除了元器件以外还与组装工艺、逻辑设计等因素有关。因此，不同厂家生产的兼容机，即使采用相同的元器件，其可取性及MTBF也可能会相差很大。

2．计算机可靠性模型

计算机系统是一个复杂的系统，而且影响其可靠性的因素也非常繁复，很难直接对其进行可靠性分析。但通过建立适当的数学模型，把大系统分割成若干子系统，可以简化其分析过程。常见的系统可靠性数学模型有以下三种。

（1）串联系统。假设一个系统由N个子系统组成，当且仅当所有的子系统都能正常工作时，系统才能正常工作，这种系统称为串联系统，如图1-16所示。

图1-16　串联系统的可靠性模型

设系统中各个子系统的可靠性分别用R_1
，R_2
，…，RN
来表示，则系统的可靠性R可由下式求得。

如果系统的各个子系统的失效率分别用λ_1
，λ_2
，…，λN
来表示，则系统的失效率λ可由下式求得。

【例1.7】　设计算机系统由CPU、存储器、I/O三部分组成，其可靠性分别为0.95，0.90和0.85。求计算机系统的可靠性。

解：R＝R_1
·R_2
·R_3
＝0.95×0.90×0.85＝0.73

计算机系统的可靠性为0.73。

（2）并联系统。假如一个系统由N个子系统组成，只要有一个子系统正常工作，系统就能正常工作，这样的系统称为并联系统，如图1-17所示。设每个子系统的可靠性分别以R_1
，R_2
，…，RN
表示，整个系统的可靠性只可由下式来求得。

图1-17　并联系统的可靠性模型

假如所有子系统的失效率均为λ，则系统的失效率μ为

在并联系统中只有一个子系统是真正需要的，其余N－1个子系统称为冗余子系统，随着冗余子系统数量的增加，系统的平均无故障时间也增加了。

【例1.8】　设一个系统由三个相同子系统构成，其可靠性为0.9，平均无故障时间为10000小时，求系统的可靠性和平均无故障时间。

系统可靠性R＝1－（1－R_1
）3
＝0.999

系统平均无故障时间为

（3）N模冗余系统。N模冗余系统由N个（N＝2n＋1）相同的子系统和一个表决器组成，表决器把N个子系统中占多数相同结果的输出作为系统的输出，如图1-18所示。

图1-18　N模冗余系统

在N个子系统中，只要有n＋1个或n＋1个以上子系统能正常工作，系统就能正常工作，输出正确的结果。假设表决器是完全可靠的，每个子系统的可靠性为R_0
，则N模冗余系统的可靠性为

其中，
表示从N个元素中取i个元素的组合数。

提高计算机的可靠性一般采取如下两项措施。

（1）提高元器件质量，改进加工工艺与工艺结构，完善电路设计。

（2）发展容错技术，使得在计算机硬件有故障的情况下，计算机仍能继续运行，得出正确的结果。

1.3.4　计算机系统的性能评价

无论是生产计算机的厂商还是使用计算机的用户，都需要有某种方法来衡量计算机的性能，作为设计、生产、购买和使用的依据。但是，由于计算机系统是一个极复杂的系统，其体系结构、组成和实现都有若干种策略，而且其应用领域也千差万别，所以很难找到统一的规则或标准去评测所有的计算机。

1．性能评测的常用方法

（1）时钟频率。计算机的时钟频率在一定程度上反映了机器速度，一般来讲，主频越高，速度越快。但是，相同频率、不同体系结构的机器，其速度可能会相差很多倍，因此还需要用其他方法来测定机器性能。

（2）指令执行速度。在计算机发展的初期，曾用加法指令的运算速度来衡量计算机的速度，速度是计算机的主要性能指标之一。因为加法指令的运算速度大体上可反映出乘法、除法等其他算术运算的速度，而且逻辑运算、转移指令等简单指令的执行时间往往设计成与加法指令相同，因此加法指令的运算速度有一定代表性。当时表征机器运算速度的单位是KIPS（每秒千条指令），后来随着机器运算速度的提高，计量单位由KIPS发展到MIPS（每秒百万条指令）。

（3）等效指令速度法。随着计算机指令系统的发展，指令的种类大大增加，用单种指令的MIPS值来表征机器的运算速度的局限性日益暴露，因此很快就出现了改进的办法，称之为吉普森（Gibson）混合法或等效指令速度法。

等效指令速度法统计各类指令在程序中所占比例，并进行折算。设某类指令i在程序序中所占比例为wi
，执行时间为ti
，则等效指令的执行时间为

其中n为指令的种类数。

（4）数据处理速率（Processing Data Rate, PDR）法。因为在不同程序中，各类指令的使用频率是不同的，所以固定比例方法存在着很大的局限性，而且数据长度与指令功能的强弱对解题的速度影响极大。同时，这种方法也不能反映现代计算机中高速缓冲存储器、流水线和交叉存储等结构的影响。具有这种结构的计算机的性能不仅与指令的执行频率有关，而且也与指令的执行顺序与地址分布有关。

PDR法采用计算PDR值的方法来衡量机器性能，PDR值越大，机器性能越好。PDR与每条指令和每个操作数的平均位数以及每条指令的平均运算速度有关，其计算方法如下：

其中，L＝0.85G＋0.15H＋0.4J＋0.15K，R＝0.85M＋0.09N＋0.06P。

式中：G——每条定点指令的位数；

M——平均定点加法时间；

H——每条浮点指令的位数；

N——平均浮点加法时间；

J——定点操作数的位数；

P——平均浮点乘法时间；

K——浮点操作数的位数。

此外，还作了如下规定：G＞20位，H＞30位；从主存取一条指令的时间等于取一个字的时间；指令与操作数存放在主存，无变址或间址操作；允许有并行或先行取指令功能，此时选择平均取指令时间。PDR值主要对CPU和主存储器的速度进行度量，但不适合衡量机器的整体速度，因为它没有涉及Cache、多功能部件等技术对性能的影响。

（5）核心程序法。上述性能评价方法主要是针对CPU（有时包括主存），它没有考虑诸如I/O结构、操作系统、编译程序的效率等系统性能的影响，因此难以准确评价计算机的实际工作能力。

核心程序法是研究较多的一种方法，它把应用程序中用得最频繁的那部分核心程序作为评价计算机性能的标准程序，在不同的机器上运行，测得其执行时间，作为各类机器性能评价的依据。机器软硬件结构的特点能在核心程序中得到反映，但是核心程序各部分之间的联系较小。由于程序短，所以访问存储器的局部性特征很明显，以致Cache的命中率比一般程序高。

2．基准测试程序

基准程序法（Benchmark）是目前一致承认的测试性能的较好方法，有多种多样的基准程序，如主要测试整数性能的基准程序、测试浮点性能的基准程序等。

（1）整数测试程序。Dhrystone是一个综合性的基准测试程序，它是为了测试编译器及CPU处理整数指令和控制功能的有效性，人为地选择一些“典型指令”综合起来形成的测试程序。

Dhrystone程序测试的结果由每秒多少个Dhrystones来表示机器的性能，这个数值越大，性能越好。VAX11/780的测试结果为每秒1757Dhrystones，为便于比较，人们假设1VAX MIPS＝每秒1757Dhrystones，将被测机器的结果除以1757，就得到被测机器相对VAX11/780的MIPS值。有些厂家在宣布机器性能时就用Dhrystone MIPS值作为各自机器的MIPS值。

不过不同的厂家在测试MIPS值时，使用的基准程序一般是不一样的，因此不同厂家机器的MIPS值有时虽然是相同的，但其性能却可能差别很大，那是因为各厂家在设计计算机时针对不同的应用领域，如科学和工程应用、商业管理应用、图形处理应用等，而采用了不同的体系结构和实现方法。同一厂家的机器，采用相同的体系结构，用相同的基准程序测试，得到的MIPS值越大，一般说明机器速度越快。

（2）浮点测试程序。在计算机科学和工程应用领域内，浮点计算工作量占很大比例，因此机器的浮点性能对系统的应用有很大的影响。有些机器只标出单个浮点操作性能，如浮点加法、浮点乘法时间，而大部分工作站则标出用Linpack和Whetstone基准程序测得的浮点性能。Linpack主要测试向量性能和高速缓存性能。Whetstone是一个综合性测试程序，除测试浮点操作外，还测试整数计算和功能调用等性能。

①理论峰值浮点速度。巨型机和小巨型机在说明书中经常给出“理论峰值速度”的MFLOPS值，它不是机器实际执行程序时的速度，而是机器在理论上最大能完成的浮点处理速度。它不仅与处理机时钟周期有关，而且还与一个处理机里能并行执行操作的流水线功能部件数目和处理机的数目有关。多个CPU机器的峰值速度是单个CPU的峰值速度与CPU个数的乘积。

②Linpack基准测试程序。Linpack基准程序是一个用FORTRAN语言写成的子程序软件包，称为基本线性代数子程序包，此程序完成的主要操作是浮点加法和浮点乘法操作。测量计算机系统的Linpack性能时，让机器运行Linpack程序，测量运行时间，将结果用MFLOPS表示。

当解n阶线性代数方程组时，n越大，向量化程度越高。其关系如表1-8所示。

表1-8　矩阵的向量化程度

向量化百分比指的是含向量成分的计算量占整个程序计算量的百分比。在同一台机器中，向量化程度越高，机器的运算速度越快，因为不管n的大小，求解方程时花在非向量操作的时间差不多是相等的。

③Whetstone基准测试程序。Whetstone是用FORTRAN语言编写的综合性测试程序，主要由执行浮点运算、整数算术运算、功能调用、数组变址、条件转移和超越函数的程序组成。Whetstone的测试结果用Kwips表示，1Kwips表示机器每秒钟能执行1000条Whetstone指令。

（3）SPEC基准程序（SPEC Benchmark）。SPEC（System Performance Evaluation Cooperation）是由几十家世界知名的计算机大厂商所支持的非盈利的合作组织，旨在开发共同认可的标准基准程序。

SPEC基准程序是由SPEC开发的一组用于计算机性能综合评价的程序。以对VAX11/780机的测试结果作为基数，其他计算机的测试结果以相对于这个基数的比率来表示。SPEC基准程序能较全面地反映机器性能，有很高的参考价值。

SPEC 1.0是1989年10月宣布的，是一套复杂的基准程序集，主要用于测量与工程和科学应用有关的数字密集型的整数和浮点数方面的计算。源程序超过15万行，包含10个测试程序，使用的数据量比较大，分别测试应用的各个方面。

SPEC基准程序测试结果一般以SPECmark（SPEC分数）、SPECint（SPEC整数）和SPECfp（SPEC浮点数）来表示。其中，SPEC分数是10个程序的几何平均值，SPEC整数是4个整数程序的几何平均值，SPEC浮点数是6个浮点程序的几何平均值。

1992年，在原来SPECint89和SPECfp89的基础上又增加了两个整数测试程序和8个浮点数测试程序，因此SPECint92由6个程序组成，SPECfp92由14个程序组成。这20个基准程序是基于不同的应用写成的，主要测32位CPU、主存储器、编译器和操作系统的性能。

已有大约30个计算机软件和硬件公司参加了这个组织，它们都承认这种测试，并同意作为它们生产的机器或系统的测试标准。SPEC基准程序的测试结果获得了普遍的认可。

参加这个组织的主要成员有IBM、AT&T、BULL、CDC、DG、DEC、Fujitusu、HP、Intel、MIPS、Motorola、SGI、SUN和Unisys等。1995年，这些厂商又共同推出了SPECint95和SPECfp95作为最新的测试标准程序。

（4）TPC基准程序。TPC（Transaction Proces5ing Council，事务处理委员会）基准程序是由TPC开发的评价计算机事务处理性能的测试程序，用以评测计算机在事务处理、数据库处理、企业管理与决策支持系统等方面的性能。TPC已经推出了4套基准程序：TPC-A、TPC-B、TPC-C和TPC-D。其中A和B已经过时，不再使用。TPC-C是在线事务处理（On-line Transaction Processing, OLTP）的基准程序，TPC-D是决策支持的基准程序。TPC即将推出TPC-E，作为大型企业信息服务的基准程序。该基准程序的评测结果用每秒完成的事务处理数TPC来表示。TPC基准测试程序在商业界范围内建立了用于衡量机器性能以及性能价格比的标准。但是，任何一种测试程序都有一定的适用范围，TPC也不例外。

第2章　程序语言基础知识

程序设计语言是为了书写计算机程序而人为设计的符号语言，用于对计算过程进行描述、组织和推导。程序设计语言的广泛使用始于1957年出现的FORTRAN，程序语言的发展是一个不断演化的过程，其根本的推动力是更高的抽象机制以及对程序设计思想的更好支持。

2.1　程序语言概述

本节主要介绍程序设计语言的基本概念、基本成分和一些有代表性的程序语言。

2.1.1　程序语言的基本概念

1．低级语言和高级语言

计算机的硬件只能识别由0，1组成的机器指令序列，即机器指令程序，因此机器指令是最基本的计算机语言。由于机器指令是特定的计算机系统所固有的、面向机器的语言，所以用机器语言进行程序设计时，效率低、程序可读性很差、难以理解、难以修改和维护。因此，人们就用容易记忆的符号代替0，1序列来表示机器指令，例如，用ADD表示加法、SUB表示减法等。用符号表示的指令称为汇编指令，汇编指令的集合被称为汇编语言。汇编语言与机器语言十分接近，其书写格式在很大程度上取决于特定计算机的机器指令，因此它仍然是一种面向机器的语言，人们称机器语言和汇编语言为低级语言。在此基础上，人们开发了功能更强、抽象级别更高的语言以支持程序设计，于是就产生了面向各类应用的程序语言，称为高级语言。常见的有Java、C、C++、PHP和Python和Delphi等。这类语言与人们使用的自然语言比较接近，大大提高了程序设计的效率。

2．编译程序和解释程序

计算机只能理解由0，1序列构成的机器语言，因此高级程序语言需要翻译，担负这一任务的程序称为“语言处理程序”。语言之间的翻译形式有多种，基本方式为汇编、解释和编译。

用某种高级语言或汇编语言编写的程序称为源程序，源程序不能直接在计算机上执行。如果源程序是用汇编语言编写的，则需要一个汇编程序将其翻译成目标程序后才能执行。如果源程序是用某种高级语言编写的，则需要对应的解释程序或编译程序对其进行翻译，然后在机器上运行。

解释程序也称为解释器，它或者直接解释执行源程序，或者将源程序翻译成某种中间代码后再加以执行；而编译程序（编译器）则是将源程序翻译成目标语言程序，然后在计算机上运行目标程序。这两种语言处理程序的根本区别是：在编译方式下，机器上运行的是与源程序等价的目标程序，源程序和编译程序都不再参与目标程序的执行过程；而在解释方式下，解释程序和源程序（或其某种等价表示）要参与到程序的运行过程中，运行程序的控制权在解释程序。简单来说，在解释方式下，翻译源程序时不生成独立的目标程序，而编译器则将源程序翻译成独立保存的目标程序。

3．程序设计语言的定义

一般地，程序设计语言的定义都涉及语法、语义和语用等方面。

语法是指由程序语言的基本符号组成程序中的各个语法成分（包括程序）的一组规则，其中由基本字符构成的符号（单词）书写规则称为词法规则，由符号构成语法成分的规则称为语法规则。程序语言的语法可用形式语言进行描述。

语义是程序语言中按语法规则构成的各个语法成分的含义，可分为静态语义和动态语义。静态语义指编译时可以确定的语法成分的含义，而运行时刻才能确定的含义是动态语义。一个程序的执行效果说明了该程序的语义，它取决于构成程序的各个组成部分的语义。

语用表示了构成语言的各个记号和使用者的关系，涉及符号的来源、使用和影响。

语言的实现则有个语境问题。语境是指理解和实现程序设计语言的环境，包括编译环境和运行环境。

4．程序设计语言的分类

程序语言有交流算法和计算机实现的双重目的，现在的程序语言种类繁多，它们在应用上各有不同的侧重面。若一种程序语言不依赖于机器硬件，则称为高级语言；若程序语言能够应用于范围广泛的问题求解过程中，则称为通用的程序设计语言。

1）程序语言的发展概述

FORTRAN是第一个被广泛用来进行科学计算的高级语言。一个FORTRAN程序由一个主程序和若干个子程序组成。主程序及每一个子程序都是独立的程序单位，称为一个程序模块。在FORTRAN中，子程序是实现模块化的有效途径。

ALGOL60主导了20世纪60年代程序语言的发展。它有严格的文法规则，采用巴科斯范式BNF来描述语言的语法。ALGOL是一个分程序结构的语言。一般来说，一个ALGOL程序本身就是一个分程序，每个分程序由begin和end括起来，以说明分程序的范围和它所管辖的名字的作用域。分程序的结构可以是嵌套的，即分程序内可以包含别的分程序。过程也可以称为一个分程序。同一个名字在不同的分程序中可以代表完全不同的实体。如果一个名字在若干层嵌套分程序中多次被说明，则程序中该名字的使用由离使用点最近的内层说明决定，即“最近嵌套原则”。此外，ALGOL还提供了数组的动态说明和过程的递归调用。

COBOL（Common Business Oriented Language）是一种面向事务处理的高级语言，1961年由美国数据系统语言协会公布，经不断修改、丰富、完善和标准化，已发展了多种版本。COBOL语言的语法规则很严格，使用了300多个英语保留字，大量采用普通英语词汇和句型，主要应用于情报检索、商业数据处理等。

PASCAL是一种结构化程序设计语言，由瑞士苏黎世联邦工业大学的沃斯（N. Wirth）教授设计，于1971年正式发表。该语言是从ALGOL60衍生的，但功能更强且容易使用。PASCAL语言在高校计算机软件教学中一直处于主导地位。在PASCAL语言中，分程序和过程这两个概念合二为一，统一为过程。PASCAL过程可以嵌套和递归。

C语言是20世纪70年代初发展起来的一种通用程序设计语言，UNIX操作系统及其上的许多软件都是用C编写的。它兼顾了高级语言和汇编语言的特点，提供了一个丰富的运算符集合以及比较紧凑的语句格式。由于C提供了高效的执行语句并且允许程序员直接访问操作系统和底层硬件，因此在系统级应用和实时处理应用的开发中成为主要语言。

C++是在C语言的基础上于20世纪80年代发展起来的，与C兼容。在C++中，最主要的是增加了类机制，使其成为一种面向对象的程序设计语言。

Java产生于20世纪90年代，其初始用途是开发网络浏览器的小应用程序，但是作为一种通用的程序设计语言，Java也得到了广泛的应用。Java保留了C++的基本语法、类和继承等概念，删掉了C++中一些不好的特征，因此与C++相比，Java更简单，其语法和语义更合理。

PHP（Hypertext Preprocessor）是一种在服务器端执行的、嵌入HTML文档的脚本语言，其语言风格类似于C语言，由网站编程人员广泛运用。PHP可以快速地执行动态网页，其语法混合了C、Java、Perl以及PHP自创的语法。由于在服务器端执行，PHP能充分利用服务器的性能。另外，PHP支持几乎所有流行的数据库以及操作系统。

Paython是一种面向对象的解释型程序设计语言，可以用于编写独立程序、快速脚本和复杂应用的原型。Python也是一种脚本语言，它支持对操作系统的底层访问，也可以将Python源程序翻译成字节码在Python虚拟机上运行。虽然Python的内核很小，但它提供了丰富的基本构建块，还可以用C、C++和Java等进行扩展，因此可以用它开发任何类型的程序。

Delphi是Borland公司研制的可视化开发工具，在Windows环境下使用，其在Linux上的对应产品是Kylix。它采用面向对象的编程语言Object Pascal和基于构件的开发结构框架。其主要特性为基于窗体和面向对象的方法、高速的编译器、强大的数据库支持、与Windows编程紧密结合以及成熟的组件技术。

各种程序语言都在不断地发展之中，也出现了许多新的语言，开发工具在组件化和可视化方面进展迅速。

2）程序语言的分类

程序语言的分类没有统一的标准，这里根据设计程序的方法将程序语言大致分为命令式程序设计语言（结构化程序设计语言）、面向对象的程序设计语言、函数式程序设计语言和逻辑型程序设计语言等范型。

（1）命令式程序设计语言。命令式语言是基于动作的语言，在这种语言中，计算被看成是动作的序列。命令式语言族开始于FORTRAN，PASCAL和C语言体现了命令式程序设计的关键思想。

通常所称的结构化程序设计语言属于命令式语言类，其结构特性主要反映在以下几个方面：一是用自顶向下逐步精化的方法编程，二是按模块组装的方法编程，三是程序只包含顺序、判定（分支）及循环构造，而且每种构造只允许单入口和单出口。结构化程序的结构简单清晰、模块化强，描述方式接近人们习惯的推理式思维方式，因此可读性强，在软件重用性、软件维护等方面都有所进步，在大型软件开发中曾发挥过重要的作用。目前仍有许多应用程序的开发采用结构化程序设计技术和方法。C、PASCAL等都是典型的结构化程序设计语言。

（2）面向对象的程序设计语言。程序设计语言的演化从最开始的机器语言到汇编语言到各种结构化高级语言，最后到支持面向对象技术的面向对象语言，反映的就是一条抽象机制不断提高的演化道路。

面向对象的程序设计在很大程度上应归功于从模拟领域发展起来的Simula，Simula提出了对象和类的概念。C++、Java和smaltalk是面向对象程序设计语言的代表，它们都必须支持新的程序设计技术，如数据隐藏、数据抽象、用户定义类型、继和多态等。

（3）函数式程序设计语言。函数式语言是一类以λ-演算为基础的语言，其基本概念来自于LISP，这是一个在1958年为了人工智能应用而设计的语言。函数是一种对应规则（映射），它使定义域中每个元素和值域中唯一的元素相对应。

函数定义1：Square[x]:＝x*x

函数定义2：Plustwo[x]:＝Plusone[Plusone[x]]

函数定义3：fact[n]:＝if n＝0 then 1 else n*fact[n-1]

在函数定义2中，使用了函数复合，即将一个函数调用嵌套在另一个函数定义中。在函数定义3中，函数被递归定义。由此可见，函数可以看成是一种程序，其输入就是定义中左边括号中的量。它也可将输入组合起来产生一个规则，组合过程中可以使用其他函数或该函数本身。这种用函数和表达式建立程序的方法就是函数式程序设计。函数型程序设计语言的优点之一就是对表达式中出现的任何函数都可以用其他函数来代替，只要这些函数调用产生相同的值。

函数式语言的代表LISP在许多方面与其他语言不同，其中最为显著的是LISP的程序和数据的形式是等价的，这样数据结构就可以作为程序执行，程序也可以作为数据修改。在LISP中，大量地使用递归。

（4）逻辑型程序设计语言。逻辑型语言是一类以形式逻辑为基础的语言，其代表是建立在关系理论和一阶谓词理论基础上的PROLOG。PROLOG代表Programming in Logic。PROLOG程序是一系列事实、数据对象或事实间的具体关系和规则的集合。通过查询操作把事实和规则输入数据库。用户通过输入查询来执行程序。在PROLOG中，关键操作是模式匹配，通过匹配一组变量与一个预先定义的模式并将该组变量赋给该模式来完成操作。以值集合S和T上的二元关系R为例，R实现后，可以询问：

①已知a和b，确定R（a，b）是否成立。

②已知a，求所有使R（a，y）成立的y。

③已知b，求所有使R（x，b）成立的x。

④求所有使R（x，y）成立的x和y。

逻辑型程序设计具有与传统的命令型程序设计完全不同的风格。PROLOG数据库中的事实和规则是形式为P:-P_1
，P_2
，…，Pn
的Hore子句，其中n≥0，Pi
（1≤i≤n）为形如Ri
（…）的断言，Ri
是关系名。该子句表示规则：若P_1
，P_2
，…，Pn
均为真（成立），则P为真。当n＝0时，Hore子句变成P.，这样的子句称为事实。

一旦有了事实与规则后，就可以提出询问。测试用户询问A是否成立时，采用归结方法。

①如果程序中包含事实P，且P和A匹配，则A成立。

②如果程序中包含Hore子句P:-P_1
，P_2
，…，Pn
.，且P和A匹配，则PROLOG转而测试P_1
，P_2
，…，Pn
。只有当P_1
，P_2
，…，Pn
都成立时才能断言P成立。当求解某个Pi
失败时，则返回到前面的某个成功点并尝试另一种选择，也就是进行回溯。

③只有当所有可能情况都已穷尽时，才能推导出P失败。

PROLOG有很强的推理功能，适用于书写自动定理证明、专家系统和自然语言理解等问题的程序。

2.1.2　程序语言的基本成分

程序语言的基本成分包括数据、运算、控制和传输等。

1．程序语言的数据成分

程序语言的数据成分指的是一种程序语言的数据类型。数据对象总是对应着应用系统中某些有意义的东西，数据表示则指示了程序中值的组织形式。数据类型用于代表数据对象，还用于在基础机器中完成对值的布局，同时还可用于检查表达式中对运算的应用是否正确。

数据是程序操作的对象，具有存储类别、类型、名称、作用域和生存期等属性，使用时要为它分配内存空间。数据名称由用户通过标识符命名，标识符是由字母、数字和下划线“_”组成的标记；类型说明数据占用内存的大小和存放形式；存储类别说明数据在内存中的位置和生存期；作用域则说明可以使用数据的代码范围；生存期说明数据占用内存的时间范围。从不同角度可将数据进行不同的划分。

1）常量和变量

按照程序运行时数据的值能否改变，将数据分为常量和变量。程序中的数据对象可以具有左值和（或）右值，左值指存储单元（或地址、容器），右值是值（或内容）。变量具有左值和右值，在程序运行过程中其右值可以改变；常量只有右值，在程序运行过程中其右值不能改变。

2）全局量和局部量

按数据的作用域范围，可分为全局量和局部量。系统为全局变量分配的存储空间在程序运行的过程中一般是不改变的，而为局部变量分配的存储单元是动态改变的。

3）数据类型

按照数据组织形式的不同可将数据分为基本类型、用户定义类型、构造类型及其他类型。C（C++）的数据类型如下。

（1）基本类型：整型（int）、字符型（char）、实型（float、double）和布尔类型（bool）。

（2）特殊类型：空类型（void）。

（3）用户定义类型：枚举类型（enum）。

（4）构造类型：数组、结构、联合。

（5）指针类型：type*。

（6）抽象数据类型：类类型。

其中，布尔类型和类类型由C++语言提供。

2．程序语言的运算成分

程序语言的运算成分指明允许使用的运算符号及运算规则。大多数高级程序语言的基本运算可以分成算术运算、关系运算和逻辑运算，有些语言如C（C++）还提供位运算。运算符号的使用与数据类型密切相关。为了明确运算结果，运算符号要规定优先级和结合性，必要时还要使用圆括号。

3．程序语言的控制成分

控制成分指明语言允许表述的控制结构，程序员使用控制成分来构造程序中的控制逻辑。理论上已经证明，可计算问题的程序都可以用顺序、选择和循环这三种控制结构来描述。

1）顺序结构

顺序结构用来表示一个计算操作序列。计算过程从所描述的第一个操作开始，按顺序依次执行后续的操作，直到序列的最后一个操作，如图2-1所示。顺序结构内也可以包含其他控制结构。

2）选择结构

选择结构提供了在两种或多种分支中选择其中一个的逻辑。基本的选择结构是指定一个条件P，然后根据条件的成立与否决定控制流走计算A还是走计算B，从两个分支中选择一个执行。如图2-2（a）所示。选择结构中的计算A或计算B还可以包含顺序、选择和重复结构。程序语言中通常还提供简化了的选择结构，也就是没有计算B的分支结构，如图2-2（b）所示。

图2-1　顺序结构示意图

图2-2　选择结构示意图

3）循环结构

循环结构描述了重复计算的过程，通常由三部分组成：初始化、循环体和循环条件，其中初始化部分有时在控制的逻辑结构中不进行显式的表示。循环结构主要有两种形式：while型重复结构和do-while型重复结构。while型结构的逻辑含义是先判断条件P，若成立，则执行循环体A，然后再去判断重复条件；否则控制流就退出重复结构，如图2-3（a）所示。do-while型结构的逻辑含义是先执行循环体A，然后再判断条件P，若成立则继续执行A的过程并判断条件；否则控制流就退出循环结构，如图2-3（b）所示。

4）C（C++）语言提供的控制语句

（1）复合语句。复合语句用于描述顺序控制结构。复合语句是一系列用“｛”和“｝”括起来的声明和语句，其主要作用是将多条语句组成一个可执行单元。语法上能出现语句的地方都可以使用复合语句。复合语句是一个整体，要么全部执行，要么一条语句也不执行。

（2）if语句和switch语句。

①if语句实现的是双分支的选择结构，其一般形式为：

if（表达式）语句1；else语句2；

其中，语句1和语句2可以是任何合法的C（C++）语句，当语句2为空语句时，可以简化为：

if（表达式）语句1；

图2-3　循环结构示意图

使用if语句时，需要注意的是if和else的匹配关系。C语言规定，else总是与离它最近的尚没有else的if相匹配。

②switch语句描述了多分支的选择结构，其一般形式为：

执行switch语句时，首先计算表达式的值，然后用所得的值与列举的常量表达式值依次比较，若任一常量表达式都不能与所得的值相匹配，则执行default的“语句序列n＋1”，然后结束switch语句。若此值与常量表达式i（i＝1，2，…，n）的值相同，则执行“语句序列i”，当case i的语句序列i中无break语句时，则执行随后的语句序列i＋1，语句序列i＋2，……直到执行完语句序列n＋1后，才退出switch语句；或者遇到break时跳出switch语句。要使得程序在执行“语句序列i”后结束整个switch语句，则语句序列i中应包含控制流能够到达的break语句。

表达式可以是任何类型的，常用的是字符型或整型表达式。多个常量表达式可以共用一个语句组。语句组可以包括任何可执行语句，且无须用“｛”和“｝”括起来。

（3）循环语句。C（C++）语言提供了三种形式的循环语句用于描述循环计算的控制结构。

①while语句。while语句描述了先判断条件再执行循环体的控制结构，其一般形式是：

while（条件表达式）循环体语句；

其中，循环体语句是内嵌的语句，当循环体语句多于一条时，应使用“｛”和“｝”括起来。执行while语句时，先计算条件表达式的值，当值为非0时，就执行循环体语句，然后重新计算条件表达式的值后再进行判断，否则就结束while语句的执行过程。

②do-while语句。do-while语句描述了先执行循环体再判断条件的控制结构，其一般格式是：

do

循环体语句；

while（条件表达式）

执行do-while语句时，先执行内嵌的循环体语句，然后再计算条件表达式的值，若值为非0，则再一次地执行循环体语句和计算条件表达式并进行判断，直到条件表达式的值为0时，才结束do-while语句的执行过程。

③for语句。for语句的基本格式是：

for（表达式1；表达式2；表达式3）循环体语句；

可用while语句等价地表示为：

表达式1；

for语句的使用是很灵活的，其内部的三个表达式都可以省略，但用于分隔三个表达式的分号“；”不能遗漏。

C语言中还提供了实现控制流跳转的goto语句，由于它会破坏程序的逻辑结构，因此不提倡使用。

程序语言的传输成分指明语言允许的数据传输方式，如赋值处理、数据的输入和输出等。

4．函数

C程序由一个或多个函数组成，每个函数都有一个名字，其中有且仅有一个名字为main的函数，作为程序运行时的起点。函数是程序模块的主要成分，它是一段具有独立功能的程序。函数的使用涉及三个概念：函数定义、函数声明和函数调用。

1）函数定义

函数的定义包括两部分：函数首部和函数体。函数的定义描述了函数做什么和怎么做。函数定义的一般格式是：

返回值的类型　函数名（形式参数表）　//函数首部

函数首部说明了函数返回值的数据类型、函数的名字和函数运行时所需的参数及类型。函数所实现的功能在函数体部分进行描述。

C（C++）程序中所有函数的定义都是独立的。在一个函数的定义中不允许定义另外一个函数，也就是不允许函数的嵌套定义。

2）函数声明

函数应该先声明后引用。如果程序中对一个函数的调用在该函数的定义之前进行，则应该在调用前对被调用函数进行声明。函数原型用于声明函数。函数声明的一般形式为：

返回值类型　函数名（参数类型表）；

使用函数原型的目的在于告诉编译器传递给函数的参数个数、类型以及函数返回值的类型，参数表中仅需要依次列出函数定义时参数的类型。函数原型可以使编译器更彻底地检查源程序中对函数的调用是否正确。

3）函数调用

当在一个函数（称为主调函数）中需要使用另一个函数（称为被调函数）实现的功能时，便以名字进行调用，称为函数调用。在使用一个函数时，只要知道如何调用就可以了，并不需要关心被调用函数的内部实现。因此，主调函数需要知道被调函数的名字、返回值和需要向被调函数传递的参数（个数、类型、顺序）等信息。

函数调用的一般形式为：

函数名（实参表）；

在C程序的执行过程中，通过函数调用实现了函数定义时描述的功能。函数体中若调用自己，则称为递归调用。

C和C++通过传值方式将实参传递给形参。调用函数和被调用函数之间交换信息的方法主要有两种：一种是由被调用函数把返回值返回给主调函数，另一种是通过参数带回信息。函数调用时实参与形参间交换信息的方法有值调用和引用调用两种。

（1）值调用（Call by value）。若实现函数调用时实参向形式参数传递相应类型的值（副本），则称为是传值调用。这种方式下形式参数不能向实际参数传递信息。

在C语言中，要实现被调用函数对实际参数的修改，必须用指针作形参。即调用时需要先对实参进行取地址运算，然后将实参的地址传递给指针形参。本质上仍属于值调用。这种方式实现了间接内存访问。

（2）引用调用（Call by Reference）。引用是C++中增加的数据类型，当形式参数为引用类型时，形参名实际上是实参的别名，函数中对形参的访问和修改实际上就是针对相应实际参数所作的访问和改变。例如：

函数调用：swap（a,b）;。

在实现调用swap（a,b）时，x、y就是a、b的别名，因此，函数调用完成后，交换了a和b的值。

2.2　语言处理程序基础

语言处理程序是一类系统软件的总称，其主要作用是将高级语言或汇编语言编写的程序翻译成某种机器语言程序，使程序可在计算机上运行。语言处理程序主要分为汇编程序、编译程序和解释程序三种基本类型。

2.2.1　汇编程序基本原理

1．汇编语言

汇编语言是为特定的计算机或计算机系统设计的面向机器的符号化的程序设计语言。用汇编语言编写的程序称为汇编语言源程序。因为计算机不能直接识别和运行符号语言程序，所以要用专门的翻译程序——汇编程序进行翻译。用汇编语言编写程序要遵循所用语言的规范和约定。

汇编语言源程序由若干条语句组成，一个程序中可以有三类语句：指令语句、伪指令语句和宏指令语句。

（1）指令语句。指令语句又称为机器指令语句，将其汇编后能产生相应的机器代码，这些代码能被CPU直接识别并执行相应的操作。基本的指令如ADD、SUB和AND等，书写指令语句时必须遵循指令的格式要求。

指令语句可分为传送指令、算术运算指令、逻辑运算指令、移位指令、转移指令和处理机控制指令等类型。

（2）伪指令语句。伪指令语句指示汇编程序在汇编源程序时完成某些工作，例如给变量分配存储单元地址，给某个符号赋一个值等。伪指令语句与指令语句的区别是：伪指令语句经汇编后不产生机器代码。而指令语句经汇编后要产生相应的机器代码。另外，伪指令语句所指示的操作是在源程序被汇编时完成的，而指令语句的操作必须在程序运行时完成。

（3）宏指令语句。在汇编语言中，还允许用户将多次重复使用的程序段定义为宏。宏的定义必须按照相应的规定进行，每个宏都有相应的宏名。在程序的任意位置，若需要使用这段程序，只要在相应的位置使用宏名，即相当于使用了这段程序。因此，宏指令语句就是宏的引用。

2．汇编程序

汇编程序的功能是将汇编语言所编写的源程序翻译成机器指令程序。汇编程序的基本工作包括将每一条可执行汇编语句转换成对应的机器指令；处理源程序中出现的伪指令。由于汇编指令中形成操作数地址的部分可能出现后面才会有定义的符号，所以汇编程序一般需要两次扫描源程序才能完成翻译过程。

第一次扫描的主要工作是定义符号的值并创建一个符号表ST，ST记录了汇编时所遇到的符号的值。另外，有一个固定的机器指令表MOT1，其中记录了每条机器指令的记忆码和指令的长度。在汇编程序翻译源程序的过程中，为了计算各汇编语句标号的地址，需要设立一个位置计数器或单元地址计数器LC（Location Counter），其初值一般为0。在扫描源程序时，每处理完一条机器指令或与存储分配有关的伪指令（如定义常数语句、定义储存语句），LC的值就增加相应的长度。这样，在汇编过程中，LC的内容就是下一条被汇编的指令的偏移地址。若正在汇编的语句是有标号的，则该标号的值就取LC的当前值。

此外，在第一次扫描中，还需要对与定义符号值有关的伪指令进行处理。为了叙述方便，不妨设立伪指令表POT1。POT1表的每一个元素只有两个域：伪指令助记符和相应的子程序入口。下面的步骤（1）～（5）描述了汇编程序第一次扫描源程序的过程。

（1）单元计数器LC置初值0。

（2）打开源程序文件。

（3）从源程序中读入第一条语句。

（4）while（若当前语句不是END语句）　｛

if（当前语句有标号）则将标号和单元计数器LC的当前值填入符号表ST；

if（当前语句是可执行的汇编指令语句）则查找MOT1表获得当前指令的长度K，并令LC＝LC＋K；

if（当前指令是伪指令）则查找POT1表并调用相应的子程序；

if（当前指令的操作码是非法记忆码）则调用出错处理子程序。

从源程序中读入下一条语句；

｝

（5）关闭源程序文件。

第二次扫描的任务是产生目标程序。除了使用前一次扫描所生成的符号表ST外，还要使用机器指令表MOT2，该表中的元素有机器指令助记符、机器指令的二进制操作码（binary-code）、格式（type）和长度（length）。此外，还要设立一个伪指令表POT2，供第二次扫描时使用。POT2的每一元素仍有两个域：伪指令记忆码和相应的子程序入口。与第一次扫描的不同之处是：在第二次扫描中，伪指令有着完全不同的处理。

在第二次扫描中，可执行汇编语句应被翻译成对应的二进制代码机器指令。这一过程涉及两个方面的工作：一是把机器指令助记符转换成二进制机器指令操作码，这可通过查找MOT2表来实现；二是求出操作数区各操作数的值（用二进制表示）。在此基础上，就可以装配出用二进制代码表示的机器指令。从求值的角度看，第二部分工作并不复杂。由于形成操作数地址的各个部分都以表达式的形式出现，只要定义一个过程eval-expr（index,value），其功能是通过index给定一个表达式在汇编语句缓冲区S的开始位置，该过程就用value返回此表达式的值。例如，虚拟计算机COMET的机器指令可归属于X型指令，其汇编语句为：

可以写出下面处理X型指令的程序段（假定index已指向操作数在缓冲区S的首地址）：

类似地，可以写出其他类型指令处理操作数的程序段。设当前可执行汇编语句的操作助记符在MOT2表的索引值为i，则整个可执行汇编语句的处理可以描述如下：

将形成的指令送往输出区；

在第二次扫描中，根据伪指令助记符，调用POT2表相应元素所规定的子程序。DS伪指令的主要目的是预留存储空间。不妨设一个工作单元K（初值为0），用于累计以字节为单位的存储空间大小。从DS伪指令的操作数区求出K的大小后，就向输出区送K个空格以达到保留所规定存储单元的目的。DC伪指令处理的结果是向输出区送出转换得到的常量。开始伪指令工作是输出目标程序开始的标准信息，而结束伪指令则是输出目标程序结束的标准信息，这些信息都是为装配程序提供的。

2.2.2　编译程序基本原理

1．编译过程概述

编译程序的功能是把某高级语言书写的源程序翻译成与之等价的目标程序（汇编语言或机器语言）。编译程序的工作过程可以分为6个阶段，如图2-4所示，实际的编译器中可能会将其中的某些阶段结合在一起进行处理。

下面简要介绍各阶段实现的主要功能。

图2-4　编译器的工作阶段示意图

1）词法分析

源程序可以简单地被看成是一个多行的字符串。词法分析阶段是编译过程的第一阶段，这个阶段的任务是对源程序从前到后（从左到右）逐个字符地扫描，从中识别出一个个“单词”符号。“单词”符号是程序设计语言的基本语法单位，如关键字（或称保留字）、标识符、常数、运算符和分隔符（如标点符号、左右括号）等。词法分析程序输出的“单词”常以二元组的方式输出，即单词种别和单词自身的值。

词法分析过程依据的是语言的词法规则，即描述“单词”结构的规则。例如，对于某PASCAL源程序中的一条声明语句和赋值语句：

词法分析阶段将构成这条语句的字符串分割成如下的单词序列。

对于标识符X、Y、Z，其单词种别都是id（用户标识符），字符串″X″、″Y″和″Z″都是单词的值；而对于单词60，整常数是该单词的种类，60是该单词的值。这里用id1、id2和id3分别代表X、Y和Z，强调标识符的内部标识由于组成该标识符的字符串不同而有所区别。经过词法分析后，声明语句VAR X,Y,Z:real;表示为VAR id1,id2,id3:real，赋值语句X:＝Y＋Z*60;表示为id1:＝id2＋id3*60;。

2）语法分析

语法分析的任务是在词法分析的基础上，根据语言的语法规则将单词符号序列分解成各类语法单位，如“表达式”、“语句”和“程序”等。语法规则就是各类语法单位的构成规则。通过语法分析确定整个输入串是否构成一个语法上正确的程序。如果源程序中没有语法错误，语法分析后就能正确地构造出其语法树；否则就指出语法错误，并给出相应的诊断信息。对id1:＝id2＋id1*60进行语法分析后形成的语法树如图2-5所示。

词法分析和语法分析本质上都是对源程序的结构进行分析。

3）语义分析

语义分析阶段主要检查源程序是否包含静态语义错误，并收集类型信息供后面的代码生成阶段使用。只有语法和语义都正确的源程序才能被翻译成正确的目标代码。

语义分析的一个主要工作是进行类型分析和检查。程序语言中的一个数据类型一般包含两个方面的内容：类型的载体及其上的运算。例如，整除取余运算符只能对整型数据进行运算，若其运算对象中有浮点数就认为是类型不匹配的错误。

图2-5　语法树示意图

在确认源程序的语法和语义之后，就可对其进行翻译并给出源程序的内部表示。对于声明语句，需要记录所遇到的符号的信息，所以应进行符号表的填查工作。在图2-6所示的符号表中，每一行存放一个符号的信息。第一行存放标识符X的信息，其类型为real，为它分配的地址是0；第二行存放y的信息，其类型是real，为它分配的地址是4。因此，在这种语言中，为一个real型数据分配的存储空间是4个存储单元。对于可执行语句，则检查结构合理的表达式是否有意义。对id1:＝id2＋id1*60进行语义分析后的语法树如图2-6所示，其中增加了一个语义处理节点inttoreal，该运算用于将一个整型数转换为浮点数。

符号表部分内容

图2-6　语义分析后的符号表和语法树示意图

4）中间代码生成

中间代码生成阶段的工作是根据语义分析的输出生成中间代码。“中间代码”是一种简单且含义明确的记号系统，可以有若干种形式，它们的共同特征是与具体的机器无关。最常用的一种中间代码是与汇编语言的指令非常相似的三地址码，其实现方式常采用四元式。四元式的形式为：

（运算符，运算对象1，运算对象2，运算结果）

例如，对语句X:＝Y＋Z*60，可生成以下四元式序列：

其中，t1、t2、t3是编译程序生成的临时变量，用于存放临时的运算结果。

语义分析和中间代码生成所依据的是语言的语义规则。

5）代码优化

优化是一个编译器的重要组成部分，由于编译器将源程序翻译成中间代码的工作是机械的、按固定模式进行的，因此，生成的中间代码往往在时间上和空间上有较大的浪费。当需要生成高效的目标代码时，就必须进行优化。优化过程可以在中间代码生成阶段进行，也可以在目标代码生成阶段进行。由于中间代码不依赖于具体机器，此时所作的优化一般建立在对程序的控制流和数据流分析的基础之上，与具体的机器无关。优化所依据的原则是程序的等价变换规则。例如，在生成X:＝Y＋Z*60的四元式后，60是编译时已知的常数，把它转换为60.0的工作可以在编译时完成，没有必要生成一个四元式，同时t3仅仅用来将其值传递给id1，也可以被化简掉，因此上述的中间代码可转优化成下面的等价代码：

这只是优化工作中的一个简单示例，真正的优化工作还要涉及公共子表达式的提取、循环优化等更多的内容和技术。

6）目标代码生成

目标代码生成是编译器工作的最后一个阶段。这一阶段的任务是把中间代码变换成特定机器上的绝对指令代码、可重定位的指令代码或汇编指令代码，这个阶段的工作与具体的机器密切相关。例如，使用两个寄存器R1和R2，可对上述的四元式生成下面的目标代码：

这里用#表明60.0为常数。

7）符号表管理

符号表的作用是记录源程序中各个符号的必要信息，以辅助语义的正确性检查和代码生成，在编译过程中需要对符号表进行快速有效地查找、插入、修改和删除等操作。符号表的建立可以始于词法分析阶段，也可以放到语法分析和语义分析阶段，但符号表的使用有时会延续到目标代码的运行阶段。

8）出错处理

用户编写的源程序不可避免地会有一些错误，这些错误大致可分为静态错误和动态错误。动态错误也称动态语义错误，它们发生在程序运行时，例如变量取零时作除数、引用数组元素下标错误等。静态错误是指编译阶段发现的程序错误，可分为语法错误和静态语义错误，如单词拼写错误、标点符号错、表达式中缺少操作数、括号不匹配等有关语言结构上的错误称为语法错误，而语义分析时发现的运算符与运算对象类型不合法等错误属于静态语义错误。

在编译时发现程序中的错误后，编译程序应采用适当的策略修复它们，使得分析过程能够继续下去，以便在一次编译过程中尽可能多地找出程序中的错误。

对于编译器的各个阶段，在逻辑上可以把它们划分为前端和后端两部分。前端包括从词法分析到中间代码生成各阶段的工作，后端包括中间代码优化和目标代码的生成、优化等阶段。这样，以中间代码为分水岭，把编译器分成了与机器有关的部分和与机器无关的部分。如此一来，对于同一种程序语言的编译器，开发出一个前端之后，就可以针对不同的机器开发相应的后端，前后端有机结合后就形成了该语言的一个编译器。当语言有改动时，只会涉及前端部分的维护。对于不同的程序语言，分别设计出相应的前端，然后将各个语言的前端与同一个后端相结合，就可得到各个语言在某种机器上的编译器。

2．文法和语言的形式描述

1）字母表、字符串、字符串集合及运算

·　字母表∑和字符：字母表是字符的非空有穷集合，字符是字母表∑中的一个元素。例如∑＝{a,b}，a或b是字符。

·　字符串：∑中的字符组成的有穷序列。例如a，ab，aaa都是∑上的字符串。

·　字符串的长度：指字符串中的字符个数。如｜aba｜＝3。

·　空串ε：由零个字符组成的序列，｜ε｜＝0。

·　连接：字符串S和T的连接是指将串T接续在串S之后，表示为S·T，连接符号·可省略。显然，对于字母表∑上的任意字符串S，S·ε＝ε·S＝S。

·　∑*
：是指包括空串ε在内的∑上所有字符串的集合。例如，设∑＝{a,b}，∑*
＝{ε,a,b,aa,bb,ab,ba,aaa,……}。

·　字符串的方幂：把字符串α自身连接n次得到的串，称为字符串α的n次方幂，记为αn
。α^0
＝ε，αn
＝ααn－1
＝αn－1
α（n＞0）。

·　字符串集合的运算：设A，B代表字母表∑上的两个字符串集合。

◇　或（合并）：A∪B＝｛α｜α∈A或α∈B｝。

◇　积（连接）：AB＝｛αβ｜α∈A且β∈B｝。

◇　幂：An
＝A·An－1
＝An－1
·A（n＞0），并规定A^0
＝{ε}。

◇　正则闭包＋：A＋
＝A^1
∪A^2
∪A^3
∪…∪An
∪…。

◇　闭包*：A*
＝A^0
∪A＋
。显然，∑*
＝∑0
∪∑1
∪∑2
∪…∪∑n
∪…。

2）文法和语言的形式描述

语言L是有限字母表∑上有限长度字符串的集合，这个集合中的每个符号串都是按照一定的规则生成的。下面从产生语言的角度出发，给出文法和语言的形式定义。所谓产生语言，是指制定出有限个规则，借助它们就能产生此语言的全部句子。

（1）文法的定义。描述语言语法结构的形式规则称为文法。文法G是一个四元组，可表示为G＝（VN
，VT
，P，S），其中VT
是一个非空有限集，其每个元素称为一个终结符；VN
是一个非空有限集，其每个元素称为非终结符。VN
∩VT
＝Ф，即VN
和VT
不含公共元素。令V＝VN
∪VT
，称V为文法G的词汇表，V中的符号称为文法符号，包括终结符和非终结符。S∈VN
，称为开始符号，它至少要在一条产生式中作为左部出现。P是产生式的有限集合，每个产生式是形如“α→β”的规则，其中，α称为产生式的左部，α∈V＋
且α中至少含有一个非终结符；β称为产生式的右部，且β∈V*
。若干个产生式α→β_1
，α→β_2
，…，α→βn
的左部相同时，可简写为α→β_1
｜β_2
｜…｜βn
，称βi
（1≤i≤n）为α的一个候选式。

（2）文法的分类。乔姆斯基（Chomsky）把文法分成4种类型，即0型、1型、2型和3型。这4类文法之间的差别在于对产生式要施加不同的限制。若文法G＝（VN
，VT
，P，S）的每个产生式α→β，均有α∈（VN
∪VT
）*
，α至少含有一个非终结符，且β∈（VN
∪VT
）*
，则称G为0型文法。对0型文法的每条产生式分别施加以下限制，则可得以下文法。

·　1型文法：G的任何产生式α→β（S→ε除外）均满足｜α｜≤｜β｜（｜x｜表示x中文法符号的个数）。

·　2型文法：G的任何产生式形如A→β，其中A∈VN
，β∈（VN
∪VT
）*
。

·　3型文法：G的任何产生式形如A→a或A→aB（或者A→Ba），其中A，B∈VN
，a∈VT
。

0型文法也称为短语文法，其能力相当于图灵机，任何0型语言都是递归可枚举的；反之，递归可枚举集也必定是一个0型语言。1型文法也称为上下文有关文法，这种文法意味着对非终结符的替换必须考虑上下文，并且一般不允许替换成ε串。例如，若αAB→αγβ是1型文法的产生式，α和β不全为空，则非终结符A只有在左边是α，右边是β的上下文中才能替换成γ。2型文法就是上下文无关文法，非终结符的替换无需考虑上下文。3型文法等价于正规式，因此也被称为正规文法或线性文法。

（3）句子和语言。设有文法G＝（VN
，VT
，P，S）

·　推导与直接推导：推导就是从文法的开始符号S出发，反复使用产生式，将产生式左部的非终结符替换为右部的文法符号序列（展开产生式用⇒表示），直到产生一个终结符的序列时为止。若有产生式α→β∈P，γ，δ∈V*
，则γαδ⇒γβδ称为文法G中的一个直接推导，并称γαδ可直接推导出γβδ。显然，对P中的每一个产生式α→β都有α⇒β。若在文法中存在一个直接推导序列，即α_0
⇒α_1
⇒α_2
⇒…⇒αn
（n＞0），则称α_0
可推导出αn
，αn
是α_0
的一个推导，并记为
用记号
表示α_0
＝αn
或者

·　直接归约和归约（推导的逆过程）：若文法G中有一个直接推导α⇒β则称β可直接归约成α，或α是β的一个直接归约。若文法G中有一个推导
则称δ可归约成γ，或γ是δ的一个归约。

·　句型和句子：若文法G的开始符号为S，那么，从开始符号S能推导出的符号串称为文法的一个句型，即α是文法G的一个句型，当且仅当有如下推导
若X是文法G的一个句型，且
则称X是文法G的一个句子，即仅含终结符的句型是一个句子。

·　语言：从文法G的开始符号出发，能推导出的句子的全体称为文法G产生的语言，记为L（G）。

（4）文法的等价。若文法G_1
与文法G_2
产生的语言相同，即L（G_1
）＝L（G_2
），则称这两个文法是等价的。

3．词法分析

语言中具有独立含义的最小语法单位是符号（单词），如标识符、无符号常数与界限符等。词法分析的任务是把构成源程序的字符串转换成单词符号序列。词法规则可用3型文法（正规文法）或正规表达式描述，它产生的集合是语言基本字符集∑（字母表）上的字符串的一个子集，称为正规集。

1）正规表达式和正规集

对于字母表∑，其上的正规式及其表示的正规集可以递归定义如下。

（1）ε是一个正规式，它表示集合L（ε）＝｛ε｝。

（2）若a是∑上的字符，则a是一个正规式，它所表示的正规集为｛a｝。

（3）若正规式r和s分别表示正规集L（r）和L（s），则：

①r｜s是正规式，表示集合L（r）∪L（s）。

②r·s是正规式，表示集合L（r）L（s）。

③r*
是正规式，表示集合（L（r））*
。

④（r）是正规式，表示集合L（r）。

仅由有限次地使用上述三个步骤定义的表达式才是∑上的正规式，其中运算符“｜”、“·”、“*”分别称为“或”、“连接”和“闭包”。在正规式的书写中，连接运算符“·”可省略。运算符的优先级从高到低顺序排列为“*”、“·”、“｜”。

设∑＝{a,b}，表2-1列出了∑上的一些正规式和相应的正规集。

表2-1　正规式与正规集示例

若两个正规式表示的正规集相同，则认为二者等价。两个等价的正规式U和V记为U＝V。例如，b（ab）*
＝（ba）*
b，（a｜b）*
＝（a*
b*
）*
。设U、V和W均为正规式，正规式的代数性质如表2-2所示。

表2-2　正规式的代数性质

2）有限自动机

有限自动机是一种识别装置的抽象概念，它能准确地识别正规集。有限自动机分为两类：确定的有限自动机和不确定的有限自动机。

（1）确定的有限自动机（Deterministic Finite Automata, DFA）。一个确定的有限自动机是个五元组（S，∑，f，s_0
，Z），其中：

·　S是一个有限集，其每个元素称为一个状态。

·　∑是一个有穷字母表，其每个元素称为一个输入字符。

·　f是S×∑→S上的单值部分映像。f（A,a）＝Q表示当前状态为A、输入为a时，将转换到下一状态Q。称Q为A的一个后继状态。

·　s_0
∈S，是唯一的一个开始状态。

·　Z是非空的终止状态集合，Z⊆S。

一个DFA可以用两种直观的方式表示：状态转换图和状态转换矩阵。状态转换图简称为转换图，是一个有向图。DFA中的每个状态对应转换图中的一个节点，DFA中的每个转换函数对应图中的一条有向弧，若转换函数为f（A,a）＝Q，则该有向弧从节点A出发，进入节点Q，字符a是弧上的标记。

【例2.1】　已知有DFA M1＝（｛s_0
,s_1
,s_2
,s_3
｝，｛a,b｝，f，S，｛s_3
｝），其中f为：

与DFA M1对应的状态图如图2-7（a）所示，其中，双圈表示的节点是终态节点。状态转换矩阵可以用一个二维数组M表示，矩阵元素M[A,a]的行下标表示状态，列下标表示输入字符，M[A,a]的值是当前状态为A、输入为a时，应转换到的下一状态。与DFA M1对应的状态转换矩阵如图2-7（b）所示。在转换矩阵中，一般以第一行的行下标所对应的状态作为初态，而终态则需要特别指明。

图2-7　DFA的状态转换图和转换矩阵

对于∑中的任何字符串ω，若存在一条从初态节点到某一终止状态节点的路径，且这条路径上所有弧的标记符连接成的字符串等于ω，则称ω可由DFA M识别（接受或读出）。若一个DFA M的初态节点同时又是终态节点，则空字ε可由该DFA识别（或接受）。DFA M所能识别的语言L（M）＝｛ω｜ω是从M的初态到终态的路径上的弧上标记所形成的串｝。

∑上的一个字符串集合V是正规的，当且仅当存在∑上的一个DFA M，且V＝L（M）。

（2）不确定的有限自动机（Nondeterministic Finite Automata, NFA）。一个不确定的有限自动机也是一个五元组，它与确定有限自动机的区别如下。

①f是S×∑→2S
上的映像。对于S中的一个给定状态及输入符号，返回一个状态的集合。即当前状态的后继状态不一定是唯一确定的。

②有向弧上的标记可以是ε。

【例2.2】　已知有NFA N＝（｛s_0
,s_1
,s_2
,s_3
｝，｛a,b｝，f，S，｛s_3
｝），其中f为：

f（s_0
,a）＝s_0
，f（s_0
,a）＝s_1
，f（s_0
,b）＝s_0
，f（s_1
,b）＝s_2
，f（s_2
,b）＝s_3

与NFA M2对应的状态转换图和状态转换矩阵如图2-8所示。

图2-8　NFA的状态转换图和转换矩阵

显然，DFA是NFA的特例。实际上，对于每个NFA M，都存在一个DFA N，且L（M）＝L（N）。

对于任何两个有限自动机M1和M2，如果L（M1）＝L（M2），则称M1和M2是等价的。

3）NFA到DFA的转换

任何一个NFA都可以转换为DFA，下面先定义转换过程中需要的计算。

（1）若I是NFA N的状态集合的一个子集，定义ε_CLOSURE（I）如下。

·　状态集I的ε_CLOSURE（I）是一个状态集。

·　状态集I的所有状态属于ε_CLOSURE（I）。

·　若s∈I，那么从s出发经过任意条ε弧到达的状态s′都属于ε_CLOSURE（I）。

状态集ε_CLOSURE（I）称为I的ε_闭包。

由以上的定义可知，I的ε闭包就是从状态集I的状态出发，经ε所能到达的状态的全体。

假定I是NFA N的状态集的一个子集，a是∑中的一个字符，定义：

其中，J是所有那些可从I中的某一状态节点出发经过一条a弧而到达的状态节点的全体。

在定义了ε_CLOSURE后，就可以用子集法将一个NFA转换为一个DFA。

（2）NFA转换为DFA。设NFAN＝（S，∑，f，s_0
，Z），与之等价的DFA M＝（S′，∑，f'，q_0
，Z′），用子集法将非确定的有限自动机确定化的算法步骤如下。

①求出DFA M的初态q_0
，即令q_0
＝ε_CLOSURE（{s_0
}），此时S′仅含初态q_0
，并且没有标记。

②对于S′中尚未标记的状态qi
＝{si1
,si2
,…,sim
},sij
∈S（j＝1,…,m）进行以下处理。

·　标记qi
，以说明该状态已经计算过。

·　对于每个a∈∑，令T＝f（{si1
,si2
,…,sim
},a），qj
＝ε_CLOSURE（T）。

·　若qj
尚不在S′中，则将qj
作为一个未加标记的新状态添加到S′，并把状态转换函数f′（qi
,a）＝qj
添加到DFA M中。

③重复进行步骤②，直到S′中不再有未标记的状态时为止。

④令Z′＝{q｜q∈S′且q∩Z≠φ}。

【例2.3】　已知一个识别正规式ab*a的非确定有限自动机，其状态转换图如图2-9所示，用子集法将其转换为DFA M。

图2-9　ab*a的NFA状态转换图

根据ε_CLOSURE的定义，可以求出ε_CLOSURE（{0}）＝{0}，将｛0｝记为DFA的初态q_0
。然后根据题中所给的状态转换图以及算法步骤②求解DFA M的各个状态的过程如下。

ε_CLOSURE（q_0
,a）＝{1,2,4}，将{1,2,4}记为q_1
；ε_CLOSURE（q_0
,b）＝{}，标记q_0
。

ε_CLOSURE（q_1
,a）＝{5}，将{5}记为q_2
（终态）。

ε_CLOSURE（q_1
,b）＝{2,3,4}，将{2,3,4}记为q_3
，标记q_1
。

ε_CLOSURE（q_2
,a）＝{}，ε_CLOSURE（q_2
,b）＝{}，标记q_2
。

ε_CLOSURE（q_3
,a）＝{5}，即q_2
。

ε_CLOSURE（q_3
,b）＝{2,3,4}，即q_3
，标记q_3
。

当S′中不再有未标记的状态时，算法即可终止，所得的DFA M如图2-10所示。

4）DFA的最小化

从NFA转换得到的DFA不一定是最简化的，可以通过等价变换将DFA进行最小化处理。

对于有限自动机中的任何两个状态t和s，若从其中一个状态出发接受输入字符串ω，而从另一状态出发不接受ω，或者从t和s出发到达不同的接受状态，则称ω对状态s和t是可区分的。若状态s和t不可区分，则称其为可以合并的等价状态。

对图2-10所示的自动机进行化简所得的DFA如图2-11所示。

图2-10　识别ab*a的DFA示意图

图2-11　识别ab*a的最小化DFA示意图

4．正规式与有限自动机之间的转换

1）有限自动机转换为正规式

对于∑上的NFA M，可以构造一个∑上的正规式R，使得L（R）＝L（M）。

拓广状态转换图的概念，令每条弧可用一个正规式作标记。为∑上的NFA M构造相应的正规式R，分为如下两步。

（1）在M的状态转换图中加两个节点，一个x节点，一个y节点。从x节点到NFA M的初始状态节点引一条弧并用ε标记，从NFA M的所有终态节点到y节点引一条弧并用ε标记。形成一个与M等价的M′，M′只有一个初态x和一个终态y。

（2）按下面的方法逐步消去M′中除x和y的所有节点。在消除节点的过程中，用正规式来标记弧，最后节点x和y之间弧上的标记就是所求的正规式。消除节点的规则如图2-12所示。

图2-12　有限自动机到正规式的转换规则示意图

2）正规式转换为有限自动机

同样地，对于∑上的每个正规式R，可以构造一个∑上的NFA M，使得L（M）＝L（R）。

（1）对于正规式R，可用图2-13所示的拓广状态图表示。

（2）通过对正规式R进行分裂并加入新的节点，逐步把图转变成每条弧上的标记是∑上的一个字符或ε，转换规则如图2-14所示。

图2-13　拓广状态图

图2-14　正规式到有限自动机的转换规则示意图

最后所得的图即为一个NFA M，x为初态节点，y为终态节点。显然，L（M）＝L（R）。

5．词法分析器的构造

有了正规式和有限自动机的理论基础后，就可以构造出编译程序的词法分析模块。构造词法分析器的一般步骤如下。

（1）用正规式描述语言中的单词构成规则。

（2）为每个正规式构造一个NFA，它识别正规式所表示的正规集。

（3）将构造出的NFA转换成等价的DFA。

（4）对DFA进行最小化处理，使其最简。

（5）从DFA构造词法分析器。

6．语法分析

语法分析的任务是根据语言的语法规则，分析单词串是否构成短语和句子，即表达式、语句和程序等基本语言结构，同时检查和处理程序中的语法错误。程序设计语言的绝大多数语法规则可以采用上下文无关文法进行描述。语法分析方法有多种，根据产生语法树的方向，可分为自底向上和自顶向下两类。

1）上下文无关文法

上下文无关文法属于乔姆斯基定义的2型文法，被广泛地用于表示各种程序设计语言的语法规则。对于上下文无关文法G[S]＝（VN
，VT
，P，S），其产生式的形式都是A→β，其中A∈VN
，β∈（VN
∪VT
）*
。

若不加特别说明，下面用大写英文字母A、B、C等表示非终结符，小写英文字母a、b、c等表示终结符号，u，v，w等表示终结符号串，小写希腊字母α、β、γ、δ等表示终结符和非终结符混合的文法符号串。由于一个上下文无关文法的核心部分是其产生式集合，所以文法可以简写为其产生式集合的描述形式。

（1）规范推导（最右推导）。如果在推导的任何一步α⇒β（其中α、β是句型），都是对α中的最右（最左）非终结符进行替换，则称这种推导为最右（最左）推导。最右推导常称为规范推导。

（2）短语、直接短语和句柄。设αδβ是文法G的一个句型，即
且满足
和
则称δ是句型αδβ相对于非终结符A的短语。特别地，如果有A⇒δ，则称δ是句型αδβ相对于产生式A→δ的直接短语。一个句型的最左直接短语称为该句型的句柄。

【例2.4】　对于简单算术表达式，可以用下面的文法G[E]进行描述。

G[E]＝（｛E,T,F｝，｛＋,*,（,）,id｝，P，E）

P＝{E→T｜E＋T，T→F｜T*F，F→（E）｜id}

可以证明，id＋id*id是该文法的句子。下面用最右推导的方式从文法的开始符号出发推导出该句子。为了表示推导过程中相同符号的不同出现，给符号加一个下标。

该推导过程可以用树型结构进行描述，如图2-15所示。

图2-15　推导过程示意图

由于
且
所以id_2
是句型
的相对于非终结符T的短语。

由于
且F_3
⇒id_1
，所以id_1
是句型
相对于非终结符F的短语，也是相对于产生式F→id的直接短语。

由于
且
所以
是句型
的相对于非终结符T的短语。

由于
所以
是句型
的相对于非终结符E的短语。

实际上id_1
，id_2
，id_3
，
都是句型
的短语，而且id_1
、id_2
、id_3
均是直接短语，其中id_1
是最左直接短语，即句柄。

2）自顶向下语法分析方法

自顶向下分析法的基本思想是：对于给定的输入串ω，从文法的开始符号S出发进行最左推导，直到得到一个合法的句子或者发现一个非法结构。在推导的过程中试图用一切可能的方法，自上而下、从左到右地为输入串ω建立语法树。整个分析过程是一个试探的过程，是反复使用不同产生式谋求与输入序列匹配的过程。若输入串是给定文法的句子，则必能成功，反之必然出错。

文法中存在下述产生式时，自顶向下分析过程中会出现下面的问题。

（1）若文法中存在形如A→αβ｜αδ的产生式，即A产生式中有多于一个候选项的前缀相同（称为公共左因子，简称左因子），则可能导致分析过程中的回溯处理。

（2）若文法中存在形如A→Aα的产生式，由于采取了最左推导，可能会造成分析过程陷入死循环的情况，产生式的这种形式被称为左递归。

因此，需要对文法进行改造，消除其中的左递归，以避免分析陷入死循环；提取左因子，以避免回溯。

（1）消除文法的左递归。

①消除直接左递归。一般而言，假定非终结符号A的产生式为A→Aα｜β，其中，α不等于ε，β不以A开头，那么就对A的产生式进行如下的改造：

由于
可知改造前后关于A的产生式是等价的。将上述结果推广到更一般的情况：先将非终结符A的产生式整理为如下形式。

其中，αi
（1≤i≤m）都不等于ε，βj
（1≤j≤n）都不以A开头，然后用“A→β_1
A′｜β_2
A′｜…｜βn
A′和A′→α_1
A′｜α_2
A′｜…｜αm
A′｜ε”代替A产生式。

②消除文法中一切左递归。消除文法中所有左递归的方法是：对产生式的右部进行代换，将所有的非直接左递归产生式改造为直接的左递归产生式，然后消除文法中的直接左递归产生式。

【例2.5】　对于文法G[E]＝（｛E,T,F｝，｛＋，*，（，），id｝，P，E）

P＝{E→T｜E＋T，T→F｜T*F，F→（E）｜id}，消除左递归后的文法为：

（2）提取公共左因子。假定关于A的产生式为A→αβ_1
｜αβ_2
｜…｜αβn
，那么称α为A的候选式的公共左因子（简称左因子）。如果A的产生式有左因子，推导过程中会出现无法确定用A产生式的哪个候选式替换A的情况，这时可以重写A产生式来推迟这种决定，直到看见足够的输入，能正确做出选择时为止，因此可将A的产生式改为A→αA′和A′→β｜β_2
｜…｜βn
。

经反复提取，就能使每个非终结符（包括新引进的）的任意两个候选式不含有公共前缀。

（3）LL（1）文法。一个文法G是LL（1）的，当且仅当G的任何两个产生式A→α｜β满足下面的条件。

①对任何终结符a，α和β不能同时推导出以a开始的文法符号序列。

②α和β最多有一个可以推导出ε。

③若
，则α不能推导出以FOLLOW（A）中的终结符开始的任何文法符号序列。

下面说明FIRST（α）和FOLLOW（A）的含义。非形式地讲，文法符号序列α的FIRST集合，就是从α出发可以推导出的所有以终结符号开头的序列中的开头终结符号。而一个非终结符的FOLLOW集合，就是从文法开始符号可以推导出的所有含A句型中紧跟在A之后的终结符号。

①文法符号序列α的FIRST集合定义如下：

约定ε∈FIRST（α）。

求解一个文法符号的FIRST集合时，先将每一个文法符号X（X∈VN
∪VT
）的IRST（X）置空，然后应用以下规则。

·　若X∈VT
，则将FIRST（X）＝{X}。

·　若X∈VN
，则考查其产生式。

·　若有X→ε，则将ε加入FIRST（X）。

·　若有X→a…，且a∈VT
，则将a加入FIRST（X）。

·　若有X→Y_1
Y_2
…Yk
，且Y_1
∈VN
，则将FIRST（Y_1
）中除ε之外的所有元素加入FIRST（X）；

若
则将FIRST（Yi
）中除ε之外的所有元素加入FIRST（X）；特别地，若ε∈FIRST（Yj
），j＝1,2,…,k，则将ε加入FIRST（X）。

·　反复执行以上两步，直到每个文法符号的FIRST集合不再扩大时为止。

②非终结符A的FOLLOW集合定义如下：

且a∈VT
}，若A是某个句型的最右符号，则约定#属于FOLLOW（A）。

求解一个文法中非终结符A的FOLLOW集合时，先将FOLLOW（A）置空，然后应用以下规则。

·　若A是文法的开始符号，则将#加入FOLLOW（A），#是输入结束标记。

·　若有产生式A→αBβ，则将FIRST（β）中除ε之外的所有元素加入FOLLOW（B）中。

·　若有产生式A→αB，或A→αBβ且ε∈FIRST（β），则将FOLLOW（A）中的全体元素加入FOLLOW（B）中。

【例2.6】　对文法G[E]＝（｛E,E′,T,T′,F,｝，｛＋，*，（，），id｝，P，E）

对于终结符号a，显然FIRST（a）＝{a}，所以下面只列出文法中非终结符号的FIRST和FOLLOW集合的计算结果。

（4）递归下降分析法。递归下降分析法要求文法是LL（1）文法，它直接以程序的方式模拟产生式产生语言的过程，其基本思想是：为每一个非终结符构造一个子程序，每个子程序的过程体按该产生式候选项分情况展开，遇到终结符即进行匹配，而遇到非终结符则调用相应的子程序。该分析法从调用文法开始符号的子程序开始，直到所有非终结符都展开为终结符并得到匹配为止。若分析过程可以达到这一步，则表明分析成功，否则表明输入串中有语法错误。递归下降分析法的优点是简单且易于构造，缺点是程序与文法直接相关，对文法的任何改变都需要在程序中进行相应的修改。

（5）预测分析法。预测分析法是另一种自顶向下的分析方法，其基本模型如图2-16所示。

图2-16　预测分析模型示意图

一个LL（1）文法的预测分析表可以用一个二维数组M表示，其元素M[A,a]（A∈VN
，a∈VT
∪#）存放关于A的产生式，表明当遇到输入符号为a且用A进行推导时，所应采用的产生式；若M[A,a]为error，则表明推导时遇到了不该出现的符号，应进行出错处理。构造一个文法的预测分析表的过程如下。

①对文法的每个产生式A→α，执行②和③。

②对FIRST（α）的每个终结符a，加入A→α到M[A,a]。

③若ε∈FIRST（α），则对FOLLOW（A）的每个非终结符b（包括#），加入A→α到M[A,b]中。

④M中其他没有定义的条目置为error。

预测分析法的工作过程是：初始时，将“#”和文法的开始符号依次压入栈中；在分析过程中，根据输入串中的当前输入符号a和当前的栈顶符号X进行处理。

若X＝a＝′#′，则分析成功；若X＝′#′且a≠′#′，则出错。

若X∈VT
且X＝a，则X退栈，并读入下一个符号a；若X∈VT
且X≠a，则出错。

若X∈VN
且M[X,a]＝′A→α′，则X退栈，α中的符号从右到左依次进栈（ε无须进栈）；若M[X,a]＝′error′，则调用出错程序进行处理。

根据文法G[E]＝{E→TE′，E′→＋TE′｜ε，T→FT′，T′→*FT′｜ε，F→（E）｜id}构造的预测分析表为：

3）自底向上语法分析方法

自底向上分析方法也称移进—归约分析法，工作模型如图2-17所示。其基本思想是对输入序列ω自左向右进行扫描，并将输入符号逐个移进一个栈中，边移进边分析，一旦栈顶符号串形成某个句型的可归约串时，就用某个产生式的左部非终结符来替代，这称为一步归约。重复这一过程，直至栈中只剩下文法的开始符号且输入串也被扫描完时为止，确认输入串ω是文法的句子，表明分析成功；否则，进行出错处理。

图2-17　移进-归约分析模型

移进-归约分析法的数学模型是下推自动机。若模型中采用算符优先分析表，用“最左素短语”来刻画“可归约串”，则相应的分析器称为算符优先分析器；若采用LR分析表，用“句柄”来刻画“可归约串”，则相应的分析器称为LR分析器。

LR分析法是一种规范归约分析法。规范归约是规范推导（最右推导）的逆过程，下面举例说明规范归约的过程。

【例2.7】　设文法G[S]＝{S→aAcBe，A→b，A→Ab，B→d}，下面对输入串#abbcde#（#为开始和结束标志符号）进行分析。先设一个符号栈，并把句子左括号“#”放入栈底，其分析过程如下。

说明：在第（3）步中栈顶符号串b是句型abbcde的句柄，用产生式A→b进行归约；第（5）步中Ab是句型aAbcde的句柄，用相应产生式A→Ab进行归约；第（8）步和第（10）步是同样的道理。上述分析过程也可看成是自底向上构造语法树的过程。

LR分析法根据当前分析栈中的符号串（通常以状态表示）和向右顺序查看输入串的k个（k≥0）符号，就可唯一确定分析器的动作是移进还是归约，以及用哪条产生式进行归约，因而也就能唯一地确定句柄。当k＝1时，已能满足当前绝大多数高级语言编译程序的需求。常用的LR分析器有LR（0）、SLR（1）、LALR（1）和LR（1）。

一个LR分析器由如下三个部分组成。

（1）驱动器。或称驱动程序。对所有LR分析器，驱动程序都是相同的。

（2）分析表。不同的文法具有不同的分析表。同一文法采用不同的LR分析器时，分析表也不同。分析表又可分为动作表（ACTION）和状态转换表（GOTO）两个部分，它们都可用二维数组表示。

（3）分析栈。包括文法符号栈和相应的状态栈。

分析器的动作由栈顶状态和当前输入符号决定（LR（0）分析器不需向前查看输入符号），LR分析器的模型如图2-18所示。

图2-18　LR分析器模型示意图

其中SP为栈顶指针，Si
为状态，Xi
为文法符号。ACTION[Si
,a]＝Sj
规定了栈顶状态为Si
且遇到输入符号a时应执行的动作。状态转换表GOTO[Si
,X]＝Sj
表示当状态栈顶为Si
且文法符号栈顶为X时应转向状态Sj
。

LR分析器的工作过程以格局的变化来反映。格局的形式为：（栈，剩余输入，动作）。分析是从某个初始格局开始的，经过一系列的格局变化，最终达到接受格局，表明分析成功；或者达到出错格局，表明发现一个语法错误。因此，开始格局的剩余输入应该是全部的输入序列，而接受格局中的剩余输入应该为空，任何其他格局或者出错格局中的剩余输入应该是全部输入序列的一个后缀。

在LR分析过程中，改变格局的动作有以下4种。

（1）移进（shift）。当ACTION[Si
,a]＝Sj
时，把a移进文法符号栈并转向状态Sj
。

（2）归约（reduce）。当在文法符号栈顶形成句柄β时，把β归约为相应产生式A→β的非终结符A。若β的长度为r（即｜β｜＝r），则弹出文法符号栈顶的r个符号，然后将A压入文法符号栈中。

（3）接受（accept）。当文法符号栈中只剩下文法的开始符号S，并且输入符号串已经结束时（当前输入符是“#”），分析成功。

（4）报错（error）。当输入串中出现不该有的文法符号时，就报错。

LR分析器的核心部分是分析表的构造，这里不再详述。

7．语法制导翻译和中间代码生成

程序语言的语义分为静态语义和动态语义。描述程序语义的形式化方法主要有属性文法、公理语义、操作语义和指称语义等，其中属性文法是对上下文无关文法的扩充。目前应用最广的静态语义分析方法是语法制导翻译，其基本思想是将语言结构的语义以属性的形式赋予代表此结构的文法符号，而属性的计算以语义规则的形式赋予文法的产生式。在语法分析的推导或归约的步骤中，通过语义规则实现对属性的计算，以达到对语义的处理。

1）中间代码

从原理上讲，对源程序进行语义分析之后就可以直接生成目标代码，但由于源程序与目标代码的逻辑结构往往差别很大，特别是考虑到具体机器指令系统的特点，要使翻译一次到位很困难，而且用语法制导方式机械生成的目标代码往往是繁琐和低效的，因此有必要设计一种中间代码，将源程序首先翻译成中间代码表示形式，以利于进行与机器无关的优化处理。由于中间代码实际上也起着编译器前端和后端分水岭的作用，所以使用中间代码也有助于提高编译程序的可移植性。常用的中间代码有后缀式、三元式、四元式和树等形式。

（1）后缀式（逆波兰式）。逆波兰式是波兰逻辑学家卢卡西维奇（Lukasiewicz）发明的一种表示表达式的方法。这种表示方式把运算符写在运算对象的后面，例如，把a＋b写成ab＋，所以也称为后缀式。这种表示法的优点是根据运算对象和算符的出现次序进行计算，不需要使用括号，也便于用栈实现求值。对于表达式x:＝（a＋b）*（c＋d），其后缀式为xab＋cd＋*:＝。

（2）树形表示。例如，表达式x:＝（a＋b）*（c＋d）的树形表示为：

（3）三元式表示。三元式是由运算符OP、第一运算对象ARG1和第二运算对象ARG2组成的。例如，表达式x:＝（a＋b）*（c＋d）的三元式表示为：

（4）四元式表示。四元式是一种普遍采用的中间代码形式，其组成成分为运算符OP、第一运算对象ARG1、第二运算对象ARG2和运算结果RESULT。其中，运算对象和运算结果有时指用户自定义的变量，有时指编译程序引入的临时变量，RESULT总是一个新引进的临时变量，用来存放运算结果。例如，表达式x:＝（a＋b）*（c＋d）的四元式表示为：

2）常见语法单位的翻译

常见的语法单位主要有算术表达式、布尔表达式、赋值语句和控制语句（if、while）等。不同结构需要不同的处理方法，但翻译程序的构造原理是相似的。

对于各种语法单位的语法制导翻译，一般是在相应的语法规则中加入适当的语义处理，下面先说明一些翻译过程中要使用的语义变量和语义过程。

·　Entry（id）：在符号表中查找标识符id以获取它在表中的位置（入口）。

·　S.code：语句S被翻译后形成的代码序列。

·　E.place：与非终结符E相联系的语义变量，表示存放E值的变量名在符号表的入口或整数码（若此变量是临时变量）。

·　E.tc：当表达式E的值为真时控制流转向的语句标号（四元式的地址编号）。

·　E.fc：当表达式E的值为假时控制流转向的语句标号（四元式的地址编号）。

·　GEN（OP,ARG1,ARG2,RESULT）：产生四元式（OP,ARG1,ARG2,RESULT）填进四元式表中。

·　NXQ：表示下一个将要形成但尚未形成的四元式地址（编号）。NXQ的初值为1，每执行一次GEN（），NXQ就自动累增1。

·　Merg（P1,P2）：把以P1和P2为链首的两条链合并为一条链，并返回合并后的链首指针。

·　Backpatch（p,t）：把p所链接的每条四元式的第4项都以t作为值进行填充。

·　Newtemp：生成一个新的临时存储单元。

拉链与回填的基本思想是当一个四元式中存在尚不确定的转向地址时，将所有转向同一地址的四元式链接成一个链表，一旦转向地址被确定，则沿此链向所有的四元式回填该地址。

下面简单说明程序语言中常见结构的语法制导翻译方法。

（1）赋值语句及简单算术表达式的翻译。下面的产生式描述了由简单变量，算术加、乘、取负运算和圆括号构成的简单算术表达式，以及将一个算术表达式赋值给一个简单变量的赋值语句的形式规则。

为该文法编写的语义规则如下：

（2）布尔表达式的翻译。布尔表达式常用于表示控制结构中的条件，其计算过程可采用直接计算和短路计算两种形式。直接计算是指布尔表达式中的每个因子都进行运算，而短路计算是指只要表达式的值能够确定下来，就停止计算。下面介绍布尔表达式作为控制条件时的翻译方法（采用短路计算方式）。

布尔表达式的形式定义（文法）为：

其中，relop代表关系运算符（＜、＞、≤、≥、＝、≠），运算符的优先级和结合律遵照通常的习惯。E→E（1）
and E（2）
，E→E（1）
or E（2）
的代码结构如图2-19（a）和图2-19（b）所示。

图2-19　and与or的代码结构示意图

改写文法并编写语义子程序如下。

对于E→E（1）
and E（2）
，为记住E（2）
的第一条四元式的地址，需改写为：

对于E→E（1）
or E（2）
，为记住E（2）
的第一条四元式的地址，需改写为：

（3）常见语句的翻译。下面简单说明if语句和while语句的翻译方法，其形式定义为：

用语义变量S.chain记录语句结束后的转向地址。回填工作将在处理S的外层环境的某个时刻完成。If语句的代码结构如图2-20所示，while循环语句的代码结构如图2-21所示。

图2-20　if语句的代码结构

图2-21　while E do S（1）
的代码结构

【例2.8】　设NXQ的初值为1，每产生一条四元式，NXQ的值就增1，将语句if a＜b then while a＜b do a:＝a＋b翻译成四元式的主要步骤如下。

最后产生的四元式序列如下所示，其中2，4两条四元式有待于回填。

4）动态存储分配和过程调用的翻译

过程（函数）说明和过程（函数）调用是程序中一种常见的语法结构，绝大多数语言都含有这方面的内容。过程说明和调用语句的翻译，有赖于形式参数与实际参数结合的方式以及数据空间的分配方式。

由于各种语言的不同特点，在目标程序运行时，对存储空间的分配和组织有不同的要求，在编译阶段应产生相应的目标来满足不同的要求。需要分配存储空间的对象有基本数据类型（如整型、实型和布尔型等）、结构化数据类型（如数组和记录等）和连接数据（如返回地址、参数等）。分配的依据是名字的作用域和生存期的定义规则。分配的策略有静态存储分配和动态存储分配两大类。

如果在编译时就能确定目标程序运行时所需的全部数据空间的大小，则在编译时就安排好目标程序运行时的全部数据空间，并确定每个数据对象的存储位置。这种分配策略称为静态存储分配。FORTRAN语言的早期版本可以完全采用静态存储分配策略。

如果一个程序语言允许递归过程和可变数据结构，那么就需采用动态存储分配技术。动态存储分配策略的实现有栈分配方式和堆分配方式两种。在栈式动态存储分配中，将程序的数据空间设计为一个栈，每当调用一个过程时，它所需的数据空间就分配在栈顶；每当过程执行结束时，就释放这部分空间。若空间的使用未必服从“先申请后释放”的原则，那么栈式的动态存储分配方式就不适用了，这种情况下通常使用堆分配技术。下面仅就一个简单的栈式分配为例，说明过程调用和过程说明的翻译。

考虑一种简单的程序语言结构：没有分程序结构，过程定义不嵌套，但允许过程递归调用。

称过程（函数）的一次运行为一个活动。活动是一个动态的概念，除了设计为永不停机的过程（如操作系统等），或者是因为设计错误而出现死循环的情况外，任何过程的活动均有有限的生存期。每个活动在运行时的环境就称为它的活动记录。一般情况下，一个活动记录的内容如图2-22（a）所示，活动记录内容的一种安排方式如图2-22（b）所示。其中，SP指向当前活动记录在控制栈中的起始位置，TOP指向栈顶位置。

图2-22　过程的活动记录

设SP总是指向现行过程活动记录的起点，TOP始终指向栈顶单元，则过程调用语句call P（T_1
,T_2
,…,Tn
）的四元式序列是：

（1）在运行时，para和call应产生传递参数和进行调用的代码。

（2）转入过程P后，就进入过程说明的代码。

（3）过程说明中数组说明的代码，调整TOP的代码。

（4）过程体内执行语句的代码（用SP变址方式访问数据对象）。

（5）退出过程语句时，return（E）的代码序列为。

8．中间代码优化和目标代码生成

优化就是对程序进行等价变换，使得从变换后的程序能生成更有效的目标代码。所谓等价，是指不改变程序的运行结果；所谓有效，是指目标代码运行时间较短，占用的存储空间较少。优化可在编译的各个阶段进行。最主要的优化是在目标代码生成以前对中间代码进行的，这类优化不依赖于具体的计算机。

目标代码的生成由代码生成器实现。代码生成器以经过语义分析或优化后的中间代码为输入，以特定的机器语言或汇编代码为输出。代码生成所需考虑的主要问题如下所述。

（1）中间代码形式。中间代码有多种形式，其中树与后缀表示形式适用于解释器，而编译器多采用与机器指令格式较接近的四元式形式。

（2）目标代码形式。目标代码可以分为两大类：汇编语言形式和机器指令形式。机器指令形式的目标代码又可以根据需求的不同分为绝对机器指令代码和可再定位机器代码。绝对机器代码的优点是可以立即执行，一般应用于一类称为load-and-go形式的编译模式，即编译后立即执行，不形成存在外存上的目标代码文件。可再定位机器代码的优点是目标代码可以被任意链接并装入内存的任意位置，是编译器采用较多的代码形式。汇编语言作为一种中间输出形式，便于进行分析和测试。

（3）寄存器的分配。由于存取寄存器的速度远快于存取内存单元的速度，所以总是希望尽可能多地使用寄存器存储数据，而寄存器的个数是有限的，因此，如何分配及使用寄存器，是目标代码生成时需要着重考虑的。

（4）计算次序的选择。代码执行的效率会随计算次序的不同有较大的差别。在生成正确目标代码的前提下，适当地安排计算次序并优化代码序列，也是生成目标代码时要考虑的重要因素之一。

2.2.3　解释程序基本原理

解释程序是另一种语言处理程序，在词法、语法和语义分析方面与编译程序的工作原理基本相同，但是在运行用户程序时，它直接执行源程序或源程序的内部形式。因此，解释程序不产生源程序的目标程序，这是它和编译程序的主要区别。图2-23显示了解释程序实现高级语言的三种方式。

源程序被直接解释执行的处理方式如图2-23中的标记A所示。这种解释程序对源程序进行逐个字符的检查，然后执行程序语句规定的动作。例如，如果扫描到符号序列：

解释程序就开始搜索源程序中标号L的定义位置（即L后面紧跟冒号“:”的语句位置）。这类解释程序通过反复扫描源程序来实现程序的运行，运行效率很低。

图2-23　解释器类型示意图

解释程序也可以先将源程序翻译成某种中间代码形式，然后对中间代码进行解释来实现用户程序的运行，这种翻译方式如图2-23中的标记B和C所示。通常，在中间代码和高级语言的语句间存在一一对应的关系。APL和SNOBOL4的很多实现就采用这种方法。解释方式B和C的不同之处在于中间代码的级别，在方式C下，解释程序采用的中间代码更接近于机器语言。在这种实现方案中，高级语言和低级中间代码间存在着1-n的对应关系。PASCAL-P解释系统是这类解释程序的一个实例，它在词法分析、语法分析和语义基础上，先将源程序翻译成P-代码，再由一个非常简单的解释程序来解释执行这种p-代码。这类系统具有比较好的可移植性。

1．解释程序的基本结构

解释程序通常可以分成两部分：第一部分是分析部分，包括通常的词法分析、语法分析和语义分析程序，经语义分析后把源程序翻译成中间代码，中间代码常采用逆波兰表示形式。第二部分是解释部分，用来对第一部分产生的中间代码进行解释执行。下面简要介绍第二部分的工作原理。

设用数组MEM模拟计算机的内存，源程序的中间代码和解释部分的各个子程序都存放在数组MEM中。全局变量PC是一个程序计数器，它记录了当前正在执行的中间代码的位置。这种解释部分的常见结构可以由下面两部分组成。

（1）PC:＝PC＋1。

（2）执行位于opcode-table[MEM[PC]]的子程序（解释子程序执行后返回到前面）。

下面用一个简单例子来说明其工作原理。设两个实型变量A和B进行相加的中间代码是：

其中，中间代码Ipush和Iaddreal实际上都是opcode-table表的索引值（即位移），而该表的单元中存放的值就是对应的解释子程序的起始地址，A和B都是MEM中的索引值。解释部分开始执行时，PC的值为start-1。

解释部分可表示如下：

其中，stackreal（）表示把相应值压入栈中，而popreal（）表示取得栈顶元素值并弹出栈顶元素。上面的代码基于栈实现了将两个数值相加并将结果存入栈中的处理。

2．高级语言编译与解释方式的比较

对于高级语言的编译和解释工作方式，可以从以下几个方面进行比较。

（1）效率。编译比解释方式可能取得更高的效率。

一般情况下，在解释方式下运行程序时，解释程序可能需要反复扫描源程序。例如，每一次引用变量都要进行类型检查，甚至需要重新进行存储分配，从而降低了程序的运行速度。在空间上，以解释方式运行程序需要更多的内存，因为系统不但需要为用户程序分配运行空间，而且要为解释程序及其支撑系统分配空间。

在编译方式下，编译程序除了对源程序进行语法和语义分析外，还要生成源程序的目标代码并进行优化，所以这个过程比解释方式需要更多的时间。虽然与仔细写出的机器程序相比，一般由编译程序创建的目标程序运行的时间更长，需要占用的存储空间更多，但源程序只需要被编译程序翻译一次，就可以多次运行。因此总体来讲，编译方式比解释方式可能取得更高的效率。

（2）灵活性。由于解释程序需要反复检查源程序，这也使得解释方式能够比编译方式更灵活。当解释器直接运行源程序时，“在运行中”修改程序就成为可能，例如增加语句或者修改错误等。另外，当解释器直接在源程序上工作时，它可以对错误进行更精确地定位。

（3）可移植性。解释器一般也是用某种程序设计语言编写的，因此只要对解释器进行重新编译，就可以使解释器运行在不同的环境中。

由于编译和解释的方法各有特点，因此现有的一些编译系统既提供编译的方式，也提供解释的方式，甚至将两种方式进行结合。例如，在Java虚拟机上发展的一种compiling-just-in-time新技术，就是在代码第一次运行时进行编译，在其后的运行中就不再进行编译了。

第3章　操作系统知识

3.1　操作系统基础知识

计算机系统中的软件极为丰富，通常分为应用软件和系统软件两大类。应用软件是指计算机用户利用计算机的软件、硬件资源为某一专门的应用目的而开发的软件。例如，科学计算、工程设计、数据处理、事务处理和过程控制等方面的程序，以及文字处理软件、表格处理软件、辅助设计软件（CAD）和实时处理软件等。系统软件是计算机系统的一部分，由它支持应用软件的运行。常用的系统软件有操作系统、语言处理程序、链接程序、诊断程序和数据库管理系统等。操作系统是计算机系统中必不可少的核心系统软件，其他软件是建立在操作系统的基础上，并在操作系统的统一管理和支持下运行，是用户与计算机之间的接口。

3.1.1　操作系统的定义与作用

计算机系统的硬件资源包括中央处理机、存储器（包括主存与外存）和输入输出设备等物理设备；计算机系统的软件资源是以文件形式保存在存储器上的程序和数据等信息。操作系统能有效地组织和管理系统中的各种软、硬件资源，合理地组织计算机系统工作流程，控制程序的执行，并且向用户提供一个良好的工作环境和友好的接口。

操作系统有如下两个重要的作用。

（1）通过资源管理，提高计算机系统的效率。操作系统是计算机系统的资源管理者，它含有对系统软、硬件资源实施管理的一组程序。其首要作用就是通过CPU管理、存储管理、设备管理和文件管理，对各种资源进行合理地分配，改善资源的共享和利用程度，最大限度地发挥计算机系统的工作效率，提高计算机系统在单位时间内处理工作的能力（称为系统的“吞吐量（throughput））”。

（2）改善人机界面，向用户提供友好的工作环境。如果不安装操作系统，用户将要面对的是01代码和一些难懂的机器指令，通过按钮或按键来操作计算机，这样即笨拙又费时。一旦安装操作系统后，用户面对的不再是笨拙的裸机，而是操作便利、服务周到的操作系统，从而明显改善了用户界面，提高了用户的工作效率。

3.1.2　操作系统的特征与功能

操作系统的4个特征是并发行、共享性、虚拟性和不确定性。从资源管理的观点来看，操作系统的功能可分为处理机管理、文件管理、存储管理、设备管理和作业管理5大部分。操作系统的5大部分通过相互配合、协调工作，以实现对计算机系统中资源的管理，控制任务的运行。

（1）处理机管理。实质上是对处理机执行“时间”进行管理，采用多道程序等技术将CPU的时间真正合理地分配给每个任务。主要包括进程控制、进程同步、进程通信和进程调度。

（2）文件管理。又称为信息管理。主要包括文件存储空间管理、目录管理、文件的读写管理和存取控制。

（3）存储管理。是对主存储器“空间”进行管理。主要包括存储分配与回收、存储保护、地址映射（变换）和主存扩充。

（4）设备管理。实质是对硬件设备的管理，其中包括对输入输出设备的分配、启动、完成和回收。

（5）作业管理。包括任务、人机交互和用户界面管理等。

操作系统提供系统命令一级的接口，供用户用于组织和控制自己的作业运行，如命令行、菜单式或GUI“联机”、命令脚本“脱机”。操作系统还提供编程一级接口，供用户程序和系统程序调用操作系统功能，如系统调用和高级语言库函数。

3.1.3　操作系统的类型

操作系统分为批处理操作系统（简称批处理）、分时操作系统、实时操作系统、网络操作系统、分布式操作系统、微机操作系统和嵌入式操作系统。

1．批处理操作系统

批处理操作系统分为单道批处理和多道批处理两种。

（1）单道批处理操作系统。单道批处理操作系统是一种早期的操作系统，该系统可以提交多个作业，“单道”的含义是指一次只有一个作业装入内存执行。作业由用户程序、数据和作业说明书（作业控制语言）三部分组成。当一个作业运行结束后，随即自动调入同批的下一个作业运行，从而节省了作业之间的人工干预时间，提高了资源的利用率。

（2）多道批处理操作系统。多道批处理操作系统允许多个作业装入内存执行，在任意一个时刻，作业都处于开始点和终止点之间。每当运行中的一个作业因输入／输出操作需要调用外部设备时，就把CPU及时交给另一道等待运行的作业，从而将主机与外部设备的工作由串行改变为并行，进一步避免了因主机等待外设完成任务而白白浪费宝贵的CPU时间。多道批处理系统主要有三个特点：多道、宏观上并行运行、微观上串行运行。

2．分时系统

分时操作系统将CPU的工作时间划分为许多很短的时间片，轮流为各个终端的用户服务。分时系统的特点如下。

（1）多路性。允许在一台主机上同时连接多台联机终端，系统按分时原则为每个用户服务。宏观上是多个用户同时工作，共享系统资源，而微观上则是每个用户作业轮流运行一个时间片。多路性即同时性，它提高了资源利用率，从而促进了计算机更广泛的应用。

（2）独立性。每个用户各占一个终端，彼此独立操作，互不干扰。因此，用户会感觉到就像他一人独占主机。

（3）交互性。用户可通过终端与系统进行人机对话。用户可以请求系统提供多方面服务，如文件编辑、数据处理和资源共享等。

（4）及时性。用户的请求能在很短时间内获得响应，此时间间隔是以人们所能接受的等待时间来确定的，通常为1～2s。响应时间是分时系统的重要指标，它是用户发生终端命令到系统做出响应间的时间间隔。系统的响应时间主要是根据用户所能接受的等待时间确定的。分时系统中时间片的选择是一个复杂和关键的任务，如时间片选得过大，造成响应时间不变时用户数减少，或造成响应时间过长，当时间片过小时，在一个时间片内切换开销相对增加，一个进程相对要花费更多的时间片才能运行结束，一个进程在系统中的周转时间大大增长。最佳的时间片值应既能使分时用户得到好的响应时间，同时又要使在一个时间片内切换开销相对较小可忽略。

UNIX系统是典型多用户、多任务的分时操作系统。

3．实时系统

实时是指计算机对于外来信息能够以足够快的速度进行处理，并在被控对象允许的时间范围内做出快速响应。实时系统对交互能力要求不高，但可靠性要求高。为了提高系统的响应时间，对随机发生的外部事件做出及时响应并对其进行处理。

实时系统通常分为如下两类。

（1）实时控制系统。主要用于生产过程的自动控制，实验数据自动采集，武器的控制，包括火炮自动控制、飞机自动驾驶、导弹的制导系统。

（2）实时信息处理系统。主要用于实时信息处理，如飞机订票系统、情报检索系统。

实时系统的主要特点如下。

（1）快速的响应时间。实时系统是为了提高系统响应时间而设计的操作系统，特别是实时控制系统，对外部事件的响应要十分及时迅速。外部事件往往以中断方式通知系统，系统有较强的中断处理能力，实时系统的设计也以“事件驱动”方式来设计。

（2）有限的交互能力。实时系统（如实时信息处息系统）一般是专用系统，它能提供人机交互方式，但用户只能访问系统中某些特定的专用服务程序，不能像分时系统一样向终端用户提供多方面服务。

（3）高可靠性。批处理系统和分时系统虽也要求系统可靠，相比之下，实时系统则要求系统高度可靠。因此，实时系统中往往都采用双机系统，多级容错措施来保证系统和数据的安全。

实时系统与分时系统的主要区别如下。

（1）系统的设计目标不同。分时系统是设计成一个多用户的通用系统，交互能力强；而实时系统大都是专用系统。

（2）交互性的强弱不同。分时系统是多用户的通用系统，交互能力强；实时系统是专用系统，仅允许操作并访问有限量的专用程序，不能随便修改其中的程序，且交互能力差。

（3）响应时间的敏感程度不同。分时系统是以人能接受的等待时间为系统的设计依据，而实时系统是以被测物体所能接受的延迟为系统设计依据，因此实时系统对响应时间的敏感程度强，而分时系统的敏感程度弱。

4．网络操作系统

网络操作系统是使联网计算机能方便而有效地共享网络资源，为网络用户提供所需各种服务的软件和有关协议的集合。因此，网络操作系统的功能主要包括高效、可靠的网络通信；对网络中共享资源（在LAN中有硬盘、打印机等）有效的管理；提供电子邮件、文件传输、共享硬盘和打印机等服务；网络安全管理；提供互操作能力。

5．分布式操作系统

分布式计算机系统是由多个分散的计算机经连接而成的计算机系统，系统中的计算机无主、次之分，任意两台计算机可以通过通信交换信息。为分布式计算机配置的操作系统称为“分布式操作系统”。

分布式操作系统能直接对系统中各类资源进行动态分配和调度、任务划分、信息传输协调工作，并为用户提供一个统一的界面，标准的接口，用户通过这一界面实现所需要的操作和使用系统资源，使系统中若干台计算机相互协作完成共同的任务，有效控制和协调诸任务的并行执行，并向系统提供统一、有效的接口的软件集合。

分布式操作系统是网络操作系统的更高级形式，它保持网络系统所拥有的全部功能，同时又有透明性、可靠性和高性能等。网络操作系统与分布式操作系统虽然都属于管理分布在不同地理位置的计算机，但最大的差别是：网络操作系统的工作，用户必须知道网址，而分布式系统用户则不必知道计算机的确切地址；分布式操作系统负责全系统的资源分配，分布式OS通常很好地隐藏系统内部的实现细节，如对象的物理位置、并发控制和系统故障等对用户都是透明的。

6．微机操作系统

微型计算机（简称微机）的出现犹如一颗重磅炸弹，导致了计算机产业革命，拥有巨大的使用量和广泛的用户。将配置在微机上的操作系统称为微机操作系统。常用的微机操作系统有MS-DOS、MS Windows、OS/2、SCO UNIX和Linux等。其中，单用户单任务操作系统MS-DOS是Microsoft公司开发的首先在IBM-PC上使用的微机OS，现在成了事实上的16位微机单用户单任务操作系统的标准。

多任务操作系统Windows 98/NT/2000/XP是Microsoft公司开发的一个图形用户界面的多任务、多线程、全32位的操作系统。多用户多任务操作系统SCO UNIX，是SCO公司将运行于大、中、小型机上UNIX操作系统移植到微机上的。

Linux操作系统是一个遵循标准操作系统界面的免费操作系统，具有UNIX BSD和UNIX SYS Ⅴ的扩展特性。它的版权所有者是芬兰籍的Linus B. Toroalds和其他开发人员，并且遵循通用公共许可证（General Public License, GPL），保证用户有使用上的自由、获得源程序的自由、可以自己修改的自由、可以复制和推广的自由。

7．嵌入式操作系统

嵌入式操作系统是运行在嵌入式智能芯片环境中，对整个智能芯片以及它所操作、控制的各种部件装置等资源进行统一协调、处理、指挥和控制的系统软件。

3.2　处理机管理

处理机管理也称进程管理。在多道程序批处理系统和分时系统中，有多个并发执行的程序，为了描述系统中程序动态变化的过程引入了进程。进程是资源分配和独立运行的基本单位。处理机管理重点需要研究诸进程之间的并发特性，以及进程之间相互合作与资源竞争产生的问题。

3.2.1　基本概念

1．程序与进程

1）程序顺序执行的特征

前趋图是一个有向无循环图，图由节点和节点间的有向边组成，节点代表各程序段的操作，而节点间的有向边表示两程序段操作之间存在的前趋关系（“→”）。两程序段Pi
和Pj
的前趋关系表示成Pi
→Pj
，其中Pi
是Pj
的前趋，Pj
是Pi
的后继，其含义是Pi
执行完毕才能由Pj
执行。例如，图3-1为三个程序段，第一个程序段输入是第二个程序段计算的前驱，第二个程序段计算是第三个程序段输出的前驱。

图3-1　三个节点的前驱图

程序顺序执行时的特征包括顺序性、封闭性和可再现性。

2）程序并发执行的特征

若在计算机系统中采用多道程序设计技术，则主存中的多道程序可处于并发执行状态。对于上述有三个程序段的作业类，虽然每个作业有前趋关系的各程序段不能在CPU和输入输出各部件并行执行，但是同一个作业内没有前趋关系的程序段或不同作业的程序段可以分别在CPU和各输入输出部件上并行执行。例如，某系统中有一个CPU、一台输入设备和一台输出设备，每个作业具有三个程序段输入Ii
，计算Ci
和输出Pi
（i＝1,2,3）。图3-2为三个作业的各程序段并发执行的前驱图。

图3-2　程序并发执行的前驱图

从图3-2中可以看出，I_2
与C_1
并行执行；I_2
、C_2
与P_1
并行执行；C_3
与P_2
并行执行。其中，I_2
、I_3
受到I_1
的间接制约，C_2
、C_3
受到C_1
的间接制约，P_2
、P_3
受到P_1
的间接制约；而C_1
、P_1
受到I_1
的直接制约，等等。

程序并发执行时的特征如下。

（1）失去了程序的封闭性。

（2）程序和机器的执行程序的活动不再一一对应。

（3）并发程序间的相互制约性。

例如，两个并发执行的程序段完成交通流量的统计，其中“观察者”P_1
识别通过的车辆数，“报告者”P_2
定时将观察者的计数值清0。程序实现如下。

对于上例，由于程序可并发执行，所以可能有以下三种执行序列。

假定COUNT的某个循环的初值为n，那么对这三种执行序列得到的COUNT结果不同，如表3-1所示。

表3-1　程序并发执行的结果

这种不正确结果的发生是因为两个程序P_1
和P_2
共享变量COUNT引起的，即程序并发执行破坏了程序的封闭性和可再现性，使得程序和执行程序的活动不再一一对应。为了解决这一问题，需要研究进程间的同步与互斥问题。

2．进程的组成

进程是程序的一次执行，该程序可以和其他程序并发执行。进程通常是由程序、数据和进程控制块（PCB）组成的。

（1）进程控制块。进程控制块是进程存在的唯一标志，其主要内容如表3-2所示。

表3-2　PCB的内容

（2）程序。程序部分描述了进程需要完成的功能。假如一个程序能被多个进程同时共享执行，那么这一部分就应该以可再入（纯）码的形式编制，它是程序执行时不可修改的部分。

（3）数据。数据部分包括程序执行时所需的数据及工作区。这部分只能为一个进程所专用，是进程的可修改部分。

3．进程的状态及其状态间的切换

1）三态模型

在多道程序系统中，进程在处理器上交替运行，状态也不断地发生变化，因此进程一般有三种基本状态：运行、就绪和阻塞。图3-3显示了进程基本状态及其转换，也称三态模型。

（1）运行。当一个进程在处理机上运行时，则称该进程处于运行状态。显然，对于单处理机系统，处于运行状态的进程只有一个。

（2）就绪。一个进程获得了除处理机外的一切所需资源，一旦得到处理机即可运行，则称此进程处于就绪状态。

（3）阻塞。也称等待或睡眠状态，一个进程正在等待某一事件发生（例如请求I/O而等待I/O完成等）而暂时停止运行，这时即使把处理机分配给进程也无法运行，故称该进程处于阻塞状态。

图3-3　进程的三态模型

2）五态模型

事实上，对于一个实际的系统，进程的状态及其转换将更复杂。例如，引入新建态和终止态构成了进程的五态模型。如图3-4所示。

图3-4　进程的五态模型

其中，新建态对应于进程刚刚被创建时没有被提交的状态，并等待系统完成创建进程的所有必要信息。因为创建进程时分为两个阶段，第一个阶段为一个新进程创建必要的管理信息，第二个阶段让该进程进入就绪状态。由于有了新建态操作系统，往往可以根据系统的性能和主存容量的限制推迟新建态进程的提交。

类似地，进程的终止也可分为两个阶段，第一个阶段等待操作系统进行善后处理，第二个阶段释放主存。

图3-5　细分进程状态及其转换

3）具有挂起状态的进程状态及其转换

由于进程的不断创建，系统资源特别是主存资源已不能满足进程运行的要求。这时，就必须将某些进程挂起，放到磁盘对换区，暂时不参加调度，以平衡系统负载。或者是系统出现故障，或者是用户调试程序，也可能需要将进程挂起检查问题。图3-5是具有挂起状态的进程状态及其转换。

（1）活跃就绪。是指进程在主存并且可被调度的状态。

（2）静止就绪。是指就绪进程被对换到辅存时的状态，是不能被直接调度的状态，只有当主存中没有活跃就绪态进程，或者是挂起态进程具有更高的优先级，系统将把挂起就绪态进程调回主存并转换为活跃就绪。

（3）活跃阻塞。是指进程在主存，一旦等待的事件产生便进入活跃就绪状态。

（4）静止阻塞。是指阻塞进程对换到辅存时的状态，一旦等待的事件产生便进入静止就绪状态。

3.2.2　进程的控制

进程控制就是对系统中所有进程从创建到消亡的全过程实施有效的控制。为此，操作系统设置了一套控制机构，该机构的主要功能包括创建一个新进程，撤销一个已经运行完的进程，改变进程的状态，实现进程间的通信。进程控制是由操作系统内核（Kernel）中的原语实现的。内核是计算机系统硬件的首次延伸，是基于硬件的第一层软件扩充，它为系统对进程进行控制和管理提供了良好的环境。

原语（Primitive）是指由若干条机器指令组成的，用于完成特定功能的程序段。原语的特点是在执行时不能被分割，即原子操作要么都做，要么都不做。内核中所包含的原语主要有进程控制原语、进程通信原语、资源管理原语以及其他方面的原语。属于进程控制方面的原语有进程创建原语、进程撤销原语、进程挂起原语、进程激活原语、进程阻塞原语以及进程唤醒原语等。不同的操作系统内核所包含的功能不同，但大多数操作系统的内核都包含支撑功能和资源管理的功能。

3.2.3　进程间的通信

在多道程序设计的系统中，由于多个进程可以并发执行，故进程间必然存在资源共享和相互合作的问题。进程通信是指各个进程交换信息的过程。

1．同步与互斥

同步是合作进程间的直接制约问题，互斥是申请临界资源进程间的间接制约问题。

1）进程间的同步

在计算机系统中，多个进程可以并发执行，每个进程都以各自独立的、不可预知的速度向前推进，但是需要在某些确定点上协调相互合作进程间的工作。例如，进程A向缓冲区送数据，进程B从缓冲区取数据加工，当进程B要取数据加工时，必须是进程A完成了向缓冲区送数据的操作，否则进程B必须停下来等待进程A的操作结束。可见，进程间的同步是指进程间完成一项任务时直接发生相互作用的关系。

2）进程间的互斥

在多道程序系统环境中，各进程可以共享各类资源，但有些资源一次只能供一个进程使用，称为临界资源（Critical resource, CR），如打印机、公享变量和表格等。进程间的互斥是指系统中各进程互斥使用临界资源。

3）临界区管理的原则

临界区（Critical section, CS）是进程中对临界资源实施操作的那段程序。对互斥临界区管理的4条原则如下。

（1）有空即进。当无进程处于临界区时，允许进程进入临界区，并且只能在临界区运行有限的时间。

（2）无空则等。当有一个进程在临界区时，其他欲进入临界区的进程必须等待，以保证进程互斥地访问临界资源。

（3）有限等待。对要求访问临界资源的进程，应保证进程能在有限时间进入临界区，以免陷入“饥饿”状态。

（4）让权等待。当进程不能进入自己的临界区时，应立即释放处理机，以免进程陷入忙等状态。

2．信号量机制

荷兰学者Dijkstra与1965年提出的信号量机制是一种有效的进程同步与互斥工具。目前信号量机制有了很大的发展，主要有整型信号量、记录型信号量和信号量集机制。

1）整型信号量与PV操作

信号量是一个整型变量，根据控制对象的不同被赋予不同的值。信号量分为如下两类：

（1）公用信号量。实现进程间的互斥，初值为1或资源的数目。

（2）私用信号量。实现进程间的同步，初值为0或某个正整数。

信号量S的物理意义：S≥0表示某资源的可用数，若S＜0，则其绝对值表示阻塞队列中等待该资源的进程数。

对系统中的每个进程，其工作的正确与否不仅取决于它自身的正确性，而且与它在执行中能否与其他相关进程正确地实施同步互斥有关。PV操作是实现进程同步与互斥的常用方法。P操作和V操作是低级通信原语，在执行期间不可分割。其中，P操作表示申请一个资源，V操作表示释放一个资源。

P操作的定义：S:＝S－1，若S≥0，则执行P操作的进程继续执行；若S＜0，则置该进程为阻塞状态（因为无可用资源），并将其插入阻塞队列。P操作可用如下过程表示，其中Semaphore表示所定义的变量是信号量。

V操作定义：S:＝S＋1，若S＞0，则执行V操作的进程继续执行；若S≤0，则从阻塞状态唤醒一个进程，并将其插入就绪队列，然后执行V操作的进程继续。

V操作可用如下过程表示。

2）利用PV操作实现进程的互斥

令信号量mutex的初值为1，当进入临界区时执行P操作，退出临界区时执行V操作。这样，利用PV操作实现进程互斥的代码段如下：

【例3.1】　将交通流量统计程序改写如下。

3）利用PV操作实现进程的同步

进程的同步是由于进程间合作引起的相互制约的问题，要实现进程的同步可用一个信号量与消息联系起来，当信号量的值为0时表示希望的消息未产生，当信号量的值为非0时表示希望的消息已经存在。假定用信号量S表示某条消息，进程可以通过调用P操作测试消息是否到达，调用V操作通知消息已准备好。最典型的是单缓冲区的生产者和消费者的同步问题。

【例3.2】　生产者进程P_1
不断地生产产品送入缓冲区，消费者进程P_2
不断地从缓冲区中取产品消费。为了实现P_1
与P_2
进程间的同步问题，需要设置一个信号量S_1
，且初值为1，表示缓冲区空，可以将产品送入缓冲区；设置另一个信号量S_2
，且初值为0，表示缓冲区有产品。其同步过程如图3-6所示。若信号量S_1
的初值可以设为0，此时同步过程如图3-7所示。

图3-6　单缓冲区的同步举例方法1

图3-7　单缓冲区的同步举例方法2

【例3.3】　一个生产者和一个消费者，缓冲区可存放n件物品。生产者不断地生产产品，消费者不断地消费产品。如何用PV操作实现生产者和消费者的同步。可以通过设置三个信号量S、S_1
和S_2
，其中，S是一个互斥信号量，初值为1，因为缓冲区是一个互斥资源，所以需要进行互斥控制；S_1
表示是否可以将物品放入缓冲区，初值为n；S_2
表示缓冲区是否存有物品，初值为0。同步过程如图3-8所示。

图3-8　n个缓冲区的同步举例

3．高级通信原语

进程间通信是指进程之间的信息交换，少则一个状态，多则成千上万个信息。根据交换信息量的多少和效率的高低，进程通信的方式分为低级方式和高级方式。PV操作属于低级通信方式，若用PV操作实现进程间通信，则存在如下问题。

（1）编程难度大，通信对用户不透明，即要用户利用低级通信工具实现进程间的同步与互斥。但是，PV操作使用不当容易引起死锁。

（2）效率低，生产者每次只能向缓冲区放一个消息，消费者只能从缓冲区取一个消息。

为了提高信号通信的效率，传递大量数据，减轻程序编制的复杂度，系统引入了高级通信方式。高级通信方式主要分为共享存储模式、消息传递模式和管道通信。

（1）共享存储模式。相互通信的进程共享某些数据结构（或存储区）实现进程之间的通信。

（2）消息传递模式。进程间的数据交换以消息为单位，程序员直接利用系统提供的一组通信命令（原语）来实现通信。如Send（A）、Receive（A）。

（3）管道通信。所谓管道，是指用于连接一个读进程和一个写进程，以实现它们之间通信的共享文件（pipe文件）。向管道（共享文件）提供输入的发送进程（即写进程），以字符流的形式将大量的数据送入管道；而接收进程可从管道接收大量的数据。由于它们通信时采用管道，所以叫管道通信。

3.2.4　管程

1．管程的引入

用信号量和P、V操作来解决进程的同步与互斥问题，需在程序中适当位置安排P、V操作，否则会造成死锁错误。为了解决分散编程带来的困难，1974年和1975年汉森（Brinsh Hansen）和霍尔（Hoare）提出了另一种同步机制——管程（monitor）。其基本思路是采用资源集中管理的方法，将系统中的资源用某种数据结构抽象地表示出来。由于临界区是访问共享资源的代码段，建立一个管程管理进程提出的访问请求。

采用这种方式对共享资源的管理就可以借助数据结构及在其上实施操作的若干过程来进行，对共享资源的申请和释放可以通过过程在数据结构上的操作来实现。

管程是由一些共享数据、一组能为并发进程所执行的作用在共享数据上的操作的集合、初始代码以及存取权组成。管程提供了一种可以允许多进程安全有效地共享抽象数据类型的机制，管程实现同步机制由“条件结构（condition construct）”所提供。为实现进程互斥同步，必须定义一些条件变量，例如var notempty、notfull: condition，这些条件变量只能被wait和signal操作所访问。Notfull.wait操作意味着调用该操作的进程将被挂起，使另一个进程执行；而notfull.signal操作仅仅是启动一个被挂起的进程，如无挂起进程则notfull.signal操作相当于空操作，不改变notfull状态，这不同于V操作。

2．管程的结构

每一个管程都要有一个名字以供标识，用语言来写一个管程的形式如下：

3．利用管程解决生产者—消费者问题

【例3.4】　建立一个管程Producer-Consumer，它包括两个过程put（item）和get（item），分别执行将生产的消息放入缓冲池和从缓冲池取出消息的操作，设置变量count表示缓冲池已存消息的数目。管程描述如下：

利用管程解决生产者—消费者问题，其中生产者和消费者程序如下：

3.2.5　进程调度

进程调度方式是指当有更高优先级的进程到来时如何分配CPU。调度方式分为可剥夺和不可剥夺两种。可剥夺式是指当有更高优先级的进程到来时，强行将正在运行进程的CPU分配给高优先级的进程；不可剥夺式是指当有更高优先级的进程到来时，必须等待正在运行进程自动释放占用的CPU，然后将CPU分配给高优先级的进程。

1．三级调度

在某些操作系统中，一个作业从提交到完成需要经历高、中、低三级调度。

（1）高级调度。又称“长调度”或“作业调度”或“接纳调度”，它决定处于输入池中的哪个后备作业可以调入主系统做好运行的准备，成为一个或一组就绪进程。系统中一个作业只需经过一次高级调度。

（2）中级调度。又称“中程调度”或“对换调度”，它决定处于交换区中的就绪进程哪个可以调入内存，以便直接参与对CPU的竞争。在内存资源紧张时，为了将进程调入内存，必须将内存中处于阻塞状态的进程调出至交换区，以便为调入进程腾出空间。这相当于使处于内存的进程和处于盘交换区的进程交换了位置。

（3）低级调度。又称“短程调度”或“进程调度”，它决定处于内存中的就绪进程哪个可以占用CPU。低级调度是操作系统中最活跃、最重要的调度程序，对系统的影响很大。

2．调度算法

常用的进程调度算法有先来先服务、时间片轮转、优先级调度和多级反馈调度算法。

（1）先来先服务（FCFS）。FCFS按照作业提交或进程成为就绪状态的先后次序分配CPU，即进程调度总是将就绪队列队首的进程投入运行。FCFS的特点是比较有利于长作业，而不利于短作业；有利于CPU繁忙的作业，而不利于I/O繁忙的作业。FCFS算法主要用于宏观调度。

（2）时间片轮转。时间片轮转算法主要用于微观调度，其设计目标是提高资源利用率。通过时间片轮转，提高进程并发性和响应时间特性，从而提高资源利用率。时间片的长度可从几个ms到几百ms，选择的方法一般有如下几种。

①固定时间片。分配给每个进程相等的时间片，使所有进程都公平执行，是一种实现简单又有效的方法。

②可变时间片。根据进程不同的要求对时间片的大小实时修改，可以更好地提高效率。

（3）优先级调度。优先级调度算法是让每一个进程都拥有一个优先数，数值大的表示优先级高，系统在调度时总选择优先数大的占用CPU。优先级调度分为静态优先级和动态优先级两种。

·　静态优先级。进程的优先级在创建时确定，直到进程终止都不会改变。根据以下因素确定优先级：进程类型（系统进程优先级较高）、对资源的需求（对CPU和内存需求较少的进程优先级较高）、用户要求（紧迫程度和付费多少）。

·　动态优先级。在创建进程时赋予一个优先级，在进程运行过程中还可以改变，以便获得更好的调度性能。例如，在就绪队列中，随着等待时间增长，优先级将提高。这样，对于优先级较低的进程在等待足够的时间后，其优先级提高到可被调度执行。进程每执行一个时间片，就降低其优先级，从而当一个进程持续执行时，其优先级会降低到让出CPU。

（4）多级反馈调度。多级反馈队列算法是时间片轮转算法和优先级算法的综合与发展。其优点是照顾了短进程以提高系统吞吐量、缩短了平均周转时间；照顾I/O型进程以获得较好的I/O设备利用率和缩短响应时间；不必估计进程的执行时间，动态调节优先级。该算法如图3-9所示，其实现思路如下。

①设置多个就绪队列。队列1，队列2，…，队列n分别赋予不同的优先级，队列1的优先级≥队列2的优先级≥…≥队列n的优先级。每个队列执行时间片的长度也不同，规定优先级越低则时间片越长，如逐级加倍。

②新进程进入内存后，先投入队列1的末尾，按FCFS算法调度；若某进程在队列1的一个时间片内未能执行完，则降低投入到队列2的末尾，同样按FCFS算法调度；如此下去，当进程降低到最后的队列，则按“时间片轮转”算法调度直到完成。

③仅当较高优先级的队列为空，才调度较低优先级队列中的进程执行。如果进程执行时有新进程进入较高优先级的队列，则抢先执行新进程，并把被抢先的进程投入原队列的末尾。

图3-9　多级队列算法

3．进程优先级确定

优先级确定需要考虑如下情况。

（1）对I/O型进程，让其进入最高优先级队列，以及时响应需要I/O交互的进程。通常执行一个小的时间片，在该时间片内要求可处理完一次I/O请求的数据，然后转入到阻塞队列。

（2）对计算型进程，每次都执行完时间片后进入更低级队列。最终采用最大时间片来执行，减少调度次数。

（3）对I/O次数不多，而主要是CPU处理的进程。在I/O完成后，放回优先I/O请求时离开的队列，以免每次都回到最高优先级队列后再逐次下降。

（4）为适应一个进程在不同时间段的运行特点，I/O完成时，提高优先级；时间片用完时，降低优先级。

3.2.6　死锁

在计算机系统中有许多互斥资源（如磁带机、打印机和绘图仪等）或软件资源（如进程表、临界区等），若两个进程同时使用打印机，或同时进入临界区必然会出现问题。所谓死锁，是指两个以上的进程互相都要求对方已经占有的资源导致无法继续运行下去的现象。

1．死锁举例

【例3.5】　进程推进顺序不当引起的死锁。设系统中有一台读卡机A，一台打印机B，它们被进程P_1
和P_2
共享，两个进程并发执行，它们按下列顺序请求和释放资源。

假如按P_1
＜a＞P_2
＜a＞P_1
＜b＞P_2
＜b＞的次序执行，则系统会发生死锁。因为进程P_1
＜a＞时，由于读卡机未被占用，所以请求可以得到满足；进程P_2
＜a＞时，由于打印机未被占用，所以请求也可以得到满足。接着进程P_1
＜b＞时，由于打印机被占用，所以请求得不到满足，P_1
等待；进程P_2
＜b＞时，由于读卡机被占用，所以请求得不到满足，P_2
也等待。导致互相在请求对方已占有的资源，系统发生死锁。

【例3.6】　同类资源分配不当引起死锁。若系统中有m个资源被n个进程共享，当每个进程都要求k个资源，而m＜nk时，即资源数小于进程所要求的总数时，可能会引起死锁。例如，m＝5，n＝3，k＝3，若系统采用的分配策略是轮流地为每个进程分配，则第一轮系统先为每个进程分配1台，还剩下2台；第二轮系统再为两个进程各分配1台，此时，系统中已无可供分配的资源，使得各个进程都处于等待状态导致系统发生死锁。

【例3.7】　PV操作使用不当引起的死锁。对于图3-10，当信号量S_1
＝S_2
＝0时，将发生死锁。

图3-10　PV操作引起的死锁

从图3-10可见，P_2
进程从缓冲区取产品前，先执行P（S_2
），由于S_2
＝-1故P_2
等待；P_1
进程将产品送到缓冲区后，执行P（S_1
），由于S_1
＝-1，故P_1
等待。这样，P_1
、P_2
进程都无法继续运行下去，导致系统死锁。

2．死锁产生的原因及4个必要条件

可以看出，产生死锁的原因为竞争资源及进程推进顺序非法。当系统中有多个进程所共享的资源，不足以同时满足它们的需求时，引起它们对资源的竞争导致死锁。进程推进顺序非法，进程在运行的过程中，请求和释放资源的顺序不当，导致进程死锁。产生死锁的4个必要条件是互斥条件、请求保持条件、不可剥夺条件和环路条件。

当发生死锁时，在进程资源有向图中必构成环路，其中每个进程占有了下一个进程申请的一个或多个资源，如图3-11所示。

进程资源有向图由方框、圆圈和有向边三部分组成。其中方框表示资源，圆圈表示进程。请求资源：○→□，箭头由进程指向资源；分配资源：○←□，箭头由资源指向进程。

图3-11　进程资源有向图

3．死锁的处理

死锁的处理策略主要有4种：鸵鸟策略（即不理睬策略）、预防策略、避免策略和检测与解除死锁。

1）死锁预防

死锁预防是采用某种策略，限制并发进程对资源的请求，破坏死锁产生的4个必要条件之一，使系统在任何时刻都不满足死锁的必要条件。预防死锁的两种策略如下。

（1）预先静态分配法。破坏了“不可剥夺条件”。预先分配所需资源，保证不等待资源。该方法的问题是降低了对资源的利用率，降低进程的并发程度；有时可能无法预先知道所需资源。

（2）资源有序分配法。破坏了“环路条件”。把资源分类按顺序排列，保证不形成环路。该方法存在的问题是限制进程对资源的请求；由于资源的排序占用系统开销。

2）死锁避免

死锁预防是设法破坏产生死锁的4个必要条件之一，严格防止死锁的产生。死锁避免则不那么严格地限制产生死锁的必要条件。最著名的是Dijkstra提出的银行家算法。死锁避免算法需要很大的系统开销。

银行家算法对于进程发出的每一个系统可以满足的资源请求命令加以检测，如果发现分配资源后，系统进入不安全状态，则不予分配；若分配资源后系统仍处于安全状态，则实施分配。与死锁预防策略相比提高了资源的利用率，但增加了系统开销。

所谓安全状态，是指系统能按某种顺序如＜P_1
,P_2
,…,Pn
＞来为每个进程分配其所需资源，直至最大需求，使每个进程都可顺序完成。通常称＜P_1
,P_2
,…,Pn
＞序列为安全序列。若系统不存在这样一个安全序列，则称系统处于不安全状态。

【例3.8】　假设系统中有三类互斥资源R_1
、R_2
和R_3
，可用资源数分别为8，7和4。在T_0
时刻系统中有P_1
、P_2
、P_3
、P_4
和P_5
这5个进程，这些进程对资源的最大需求量和已分配资源数如图3-12所示。

图3-12　进程已分配资源数

若有如下4个执行序列，那么进程按什么序列执行，系统状态是安全的。

解：初始时系统的可用资源数分别为8，7和4，在T_0
时刻已分配资源数分别为7，6和4，因此系统剩余的可用资源数分别为1，1和0。

由于R_3
资源为0，系统不能在分配R_3
资源了，所以不能一开始就运行需要分配R_3
资源的进程P_1
和P_2
，故①和②显然是不安全的。

分析序列③的P_4
→P_2
→P_1
→P_5
→P_3
是否安全。进程P_4
可以设置能完成标志True，如图3-13所示。

图3-13　进程按序列③的P_4
→P_2
→P_1
→P_5
→P_3
执行

因为系统的可用资源数为（1，1，0），而进程P_4
只需要一台R_1
资源；进程P_2
可以设置能完成标志True，因为进程P_4
运行完毕将释放所有资源，此时系统的可用资源数应为（2，3，1），而进程P_2
只需要（0，1，1），进程P_2
运行完毕将释放所有资源，此时系统的可用资源数应为（4，4，2）；进程P_1
不能设置能完成标志True，因为进程P_1
需要R_1
资源为5，系统能提供的R_1
资源为4，所以序列无法进行下去，因此，P_4
→P_2
→P_1
→P_5
→P_3
为不安全序列。

序列④的P_4
→P_2
→P_5
→P_1
→P_3
是安全的，因为所有的进程都能设置完成标志True，如图3-14所示。

图3-14　进程按序列④的P_4
→P_2
→P_5
→P_1
→P_3
执行

3）死锁检测

解决死锁的另一条途径是死锁检测方法，这种方法对资源的分配不加限制，即允许死锁产生。但系统定时地运行一个死锁检测程序，判断系统是否发生死锁，若检测到有死锁，则设法加以解除。

4）死锁解除

死锁解除通常采用如下方法。

（1）资源剥夺法。从一些进程那里强行剥夺足够数量的资源分配给死锁进程。

（2）撤销进程法。根据某种策略逐个地撤销死锁进程，直到解除死锁为止。

3.2.7　线程

传统的进程有两个基本属性：可拥有资源的独立单位；可独立调度和分配的基本单位。由于在进程的创建、撤销和切换中，系统必须为之付出较大的时空开销，因此在系统中设置的进程数目不宜过多，进程切换的频率不宜太高，这就限制了并发程度的提高。引入线程后，将传统进程的两个基本属性分开，线程作为调度和分配的基本单位，进程作为独立分配资源的单位。用户可以通过创建线程来完成任务，以减少程序并发执行时付出的时空开销。

例如，在文件服务进程中可设置多个服务线程，当一个线程受阻时，第二个线程可以继续运行，当第二个线程受阻时，第三个线程可以继续运行……从而显著地提高了文件系统的服务质量及系统的吞吐量。

这样，对拥有资源的基本单位，不用频繁对其切换，进一步提高了系统中各程序的并发程度。需要说明的是，线程是进程中的一个实体，是被系统独立分配和调度的基本单位。线程基本上不拥有资源，只拥有一点运行中必不可少的资源（如程序计数器、一组寄存器和栈），它可与同属一个进程的其他线程共享进程所拥有的全部资源。

线程也具有就绪、运行和阻塞三种基本状态。由于线程具有许多传统进程所具有的特性，故称为“轻型进程（Light-Weight Process）”；传统进程称之为“重型进程（Heavy-Weight Process）”。线程可创建另一个线程，同一个进程中的多个线程可并发执行。

线程分为用户级线程（User-Level Threads）和内核支持线程（Kernel-Supported Threads）两类。用户级线程不依赖于内核，该类线程的创建、撤销和切换都不利用系统调用来实现；内核支持线程依赖于内核，即无论是在用户进程中的线程，还是在系统中的线程，它们的创建、撤销和切换都利用系统调用来实现。某些系统同时实现了两种类型的线程。

与线程不同的是，不论是系统进程还是用户进程，在进行切换时，都要依赖于内核中的进程调度。因此，不论是什么进程都是与内核有关的，是在内核支持下进行切换的。尽管程序和进程表面上看起来相似，但它们在本质上是不同的。

3.3　存储管理

存储器管理的对象是主存，也称内存。存储器是计算机系统中的关键性资源，是存放各种信息的主要场所。特别是近几年来，系统软件、应用软件在功能及其所需存储空间等方面都在急剧膨胀，如何对存储器实施有效的管理，不仅直接影响到存储器的利用率，而且还对系统性能有重大的影响。其主要功能包括分配和回收主存空间、提高主存的利用率、扩充主存、对主存信息实现有效保护。

3.3.1　基本概念

1．存储器的结构

存储器的功能是保存数据，存储器的发展方向是高速、大容量和小体积。一般存储器的结构有“寄存器—主存—外存”结构和“寄存器—缓存—主存—外存”结构（如图3-15所示）。存储组织的功能是在存储技术和CPU寻址技术许可的范围内组织合理的存储结构，使得各层次的存储器都处于均衡的繁忙状态。

图3-15　存储器的层次结构

（1）虚拟地址。对程序员来说，数据的存放地址是由符号决定的，故称符号名地址，或者称为名地址，而把源程序的地址空间叫做符号名地址空间或者名空间。它是从0号单元开始编址，并顺序分配所有的符号名所对应的地址单元，所以它不是主存中的真实地址，故称为相对地址、程序地址、逻辑地址或称虚拟地址。

（2）地址空间。把程序中由符号名组成的空间称为名空间。源程序经过汇编或编译后再经过链接编辑程序加工形成程序的装配模块，即转换为相对地址编址的模块，它是以0为基址顺序进行编址的。相对地址也称为逻辑地址或虚地址，把程序中由相对地址组成的空间叫做逻辑地址空间。相对地址空间通过地址再定位机构转换到绝对地址空间，绝对地址空间也叫物理地址空间。

（3）存储空间。简单来说，逻辑地址空间（简称地址空间）是逻辑地址的集合，物理地址空间（简称存储空间）是物理地址的集合。

2．地址重定位

地址重定位是指将逻辑地址变换成主存物理地址的过程。在可执行文件装入时，需要解决可执行文件中地址（指令和数据）与主存地址的对应关系，由操作系统中的装入程序Loader和地址重定位机构来完成。地址重定位分为静态地址重定位和动态地址重定位。

（1）静态重定位。静态重定位是指在程序装入主存时已经完成了逻辑地址到物理地址的变换，在程序的执行期间将不会再发生变化。静态地址重定位的优点是无须硬件地址变换机构的支持，它只要求程序本身是可重定位的，只对那些要修改的地址部分具有某种标识，由专门设计的程序来完成。在早期的操作系统中多数都采用这种方法。静态重定位的缺点是必须给作业分配一个连续的存储区域，在作业的执行期间不能扩充存储空间，也不能在主存中移动，多个作业也难以共享主存中的同一程序副本和数据。

（2）动态重定位。动态重定位是指在程序运行期间完成逻辑地址到物理地址的变换。其实现机制要依赖硬件地址变化机构，如基地址寄存器（BR）。动态地址重定位的优点是程序在执行期间可以换入和换出主存，以解决主存紧张；可以在主存中移动，把主存中的碎片集中起来，以充分利用空间；不必给程序分配连续的主存空间，可以较好地利用较小的主存块；可以实现共享。

3.3.2　存储管理方案

存储管理的主要目的是解决多个用户使用主存的问题，其存储管理方案主要包括分区存储管理、分页存储管理、分段存储管理、段页式存储管理以及虚拟存储管理。本小节介绍分区存储管理方案，其他存储管理方案在后续章节中介绍。

分区存储管理是早期的存储管理方案，其基本思想是把主存的用户区划分成若干个区域，每个区域分配给一个用户作业使用，并限定它们只能在自己的区域中运行，这种主存分配方案就是分区存储管理方式。按分区的划分方式不同可分为固定分区、可变分区和可重定位分区。

（1）固定分区。固定分区是一种静态分区方式，在系统生成时已将主存划分为若干个分区，每个分区的大小可不等。操作系统通过主存分配情况表管理主存。这种方法的突出问题是已分配区中存在未用空间，原因是程序或作业的大小不可能刚好等于分区的大小，故造成了空间的浪费。通常将已分配分区内的未用空间叫做零头或内碎片。

（2）可变分区。可变分区是一种动态分区方式，存储空间的划分是在作业装入时进行的，故分区的个数是可变的，分区的大小刚好等于作业的大小。可变分区分配需要两种管理表格：已分配表，记录已分配分区的情况；未分配表，记录未分配分区的情况。

对于可变分区的请求和释放分区主要有如下4种算法。

①最佳适应算法。假设系统中有n个空白区（自由区），每当用户申请一个空间时，将从这n个空白区中找到一个最接近用户需求的分区。这种算法能保留较大的空白区。但缺点是空闲区不可能刚好等于用户要求的区，所以必然要将一个分区一分为二，可是随着系统不断地释放空间，可能会使产生的小分区小到无法再继续分配，将这样的无用小分区称之为外碎片。

②最差适应算法。系统总是将用户作业装入最大的空白分区。这种算法将一个最大的分区一分为二，所以剩下的空白区通常也大，不容易产生外碎片。

③首次适应算法。每当用户作业申请一个空间时，系统总是从主存的低地址开始选择一个能装入作业的空白区。当用户释放空间时，该算法更易实现相邻的空白区合并。

④循环首次适应算法。与首次适应算法的不同之处是，每次分配都是从刚分配的空白区开始寻找一个能满足用户要求的空白区。

引入可变分区后虽然主存分配更灵活，也提高了主存利用率，但是由于系统在不断地分配和回收中，必定会出现一些不连续的小的空闲区，尽管这些小的空闲区的总和超过某一个作业要求的空间，但是由于不连续而无法分配，产生了未分配区的无用空间，通常称之为外碎片。解决碎片的方法是拼接（或称紧凑），即向一个方向（例如向低地址端）移动已分配的作业，使那些零散的小空闲区在另一个方向连成一片。

（3）可重定位分区。可重定位分区是解决碎片问题的简单而又行之有效的方法。基本思想是移动所有已分配好的分区，使之成为连续区域。如同队列有一个队员出列，指挥员叫大家“靠拢”一样。分区“靠拢”的时机是当用户请求空间得不到满足时或某个作业执行完毕时。由于靠拢是要代价的，所以通常是在用户请求空间得不到满足时进行。需要注意的是当进行分区“靠拢”时会导致地址发生变化，所以有地址重定位问题。

分区保护的目的是防止未经核准的用户访问分区，常用如下两种方式。

（1）采用上界／下界寄存器保护。上界寄存器中存放的是作业的装入地址，下界寄存器装入的是作业的结束地址，形成的物理地址必须满足如下条件：

上界寄存器≤物理地址≤下界寄存器

（2）采用基址／限长寄存器保护。基址寄存器中存放的是作业的装入地址，限长寄存器装入的是作业的长度，形成的物理地址必须满足如下条件：

基址寄存器≤物理地址＜基址寄存器＋限长寄存器

3.3.3　分页存储管理

尽管分区管理方案是解决多道程序共享主存的可行方案，但是该方案的主要问题是用户程序必须装入连续的地址空间中，若无满足用户要求的连续空间时，需要进行分区靠拢操作，这是以耗费系统时间为代价的。为此，引入了分页存储管理方案。

1．纯分页存储管理

1）分页原理

将一个进程的地址空间划分成若干个大小相等的区域，称为页。相应地，将主存空间划分成与页相同大小的若干个物理块，称为块或页框。在为进程分配主存时，将进程中若干页分别装入多个不相邻接的块中。

2）地址结构

分页系统的地址结构如图3-16所示，它由两部分组成：前一部分为页号P；后一部分为偏移量W，即页内地址。图中的地址长度为32位，其中0～11位为页内地址（每页的大小为4KB），12～31位为页号，所以允许地址空间的大小最多为1MB个页。

图3-16　分页地址结构

3）页表

在将进程的每一页离散地分配到主存的多个物理块中后，系统应能保证在主存中找到每个页面所对应的物理块。为此，系统为每个进程建立了一张页面映射表，简称页表（如图3-17所示）。每个页在页表中占一个表项，记录该页在主存中对应的物理块号。进程在执行时，通过查找页表就可以找到每页所对应的物理块号。图中逻辑页号为4，查找的物理块号为15，与页内地址256拼接得到物理地址。可见，页表的作用是实现从页号到物理块号的地址映射。

图3-17　页式存储管理的地址映射

地址变换机构的基本任务是利用页表把用户程序中的逻辑地址变换成主存中的物理地址，实际上就是将用户程序中的页号变换成主存中的物理块号。为了实现地址变换功能，在系统中设置页表寄存器，用来存放页表的始址和页表的长度。在进程未执行时，每个进程对应的页表的始址和长度存放在进程的PCB中，当该进程被调度时，就将它们装入页表寄存器。在进行地址变换时，系统将页号与页表长度进行比较，如果页号大于等于页表寄存器中的页表长度L（页号从0开始），则访问越界，产生越界中断。若未出现越界，则根据页表寄存器中的页表始址和页号计算出该页在页表项中的位置，得到该页的物理块号，将此物理块号装入物理地址寄存器中。与此同时，将有效地址（逻辑地址）寄存器中页内地址直接装入物理地址寄存器的块内地址字段中，这样便完成了从p逻辑地址到物理地址的变换。

2．快表

从地址映射的过程可以发现，页式存储管理至少需要两次访问主存。例如，第一次是访问页表，得到的是数据的物理地址；第二次是存取数据。若数据是间接地址，还需要再进行地址变换。为了提高访问主存的速度，可以在地址映射机构中增加一组高速寄存器，用来保存页表。这种方法需要大量的硬件开销，经济上是不可行的。另一种方法是在地址映射机构中增加一个小容量的联想存储器，联想存储器由一组高速存储器组成，称之为快表，用来保存当前访问频率高的少数活动页的页号及相关信息。

联想存储器存放的只是当前进程最活跃的少数几页，因此，用户程序要访问数据时，根据该数据所在逻辑页号在联想存储器中找出对应的物理页号，然后与页内地址拼接形成物理地址；若找不到相应的逻辑页号，则地址映射仍通过主存的页表进行，得到物理地址后，须将物理块号填入联想存储器的空闲单元中，若无空闲单元，则根据淘汰算法淘汰某一行，再填入新得到的页号。事实上，查找联想存储器和查找主存页表是并行进行的，一旦在联想存储器中找到相符的逻辑页号时，就停止查找主存页表。

3．两级页表机制

大家知道，80386的逻辑地址有2^32
个，若页面大小为4KB（2^12
B），则页表项达1MB个，每个页表项占用4B，故每个进程的页表占用4MB主存空间，并且还要求是连续的，显然这是不现实的。为了减少页表所占用的连续的主存空间，在80386中采用了两级页表机制。基本方法是将页表进行分页，每个页面的大小与主存物理块的大小相同，并为它们进行编号，可以离散地将各个页面分别存放在不同的物理块中。为此需要建立一张页表，称为外层页表（页表目录），即第一级页表，其中的每个表目是存放某个页表的物理地址。第二级是页表，其中的每个表目所存放的是页的物理块号。

两级页表的逻辑地址结构和两级页表的地址变换机构如图3-18所示。

图3-18　两级页表的地址变换机构

3.3.4　分段存储管理

在分段存储管理方式中，作业的地址空间被划分为若干个段，每个段是一组完整的逻辑信息，如有主程序段、子程序段、数据段及堆栈段等，每个段都有自己的名字，都是从0开始编址的一段连续的地址空间，各段长度是不等的。分段系统的地址结构如图3-19所示，逻辑地址由段号（名）和段内地址两部分组成。在该地址结构中，允许一个作业最多有64KB个段，每个段的最大长度为64KB。

图3-19　分段的地址结构

在分段式存储管理系统中，为每个段分配一个连续的分区，而进程中的各个段可以离散地分配到主存的不同分区中。在系统中为每个进程建立一张段映射表，简称为“段表”。每个段在表中占有一个表项，在其中记录了该段在主存中的起始地址（又称为“基址”）和段的长度，如图3-20所示。进程在执行时，通过查段表来找到每个段所对应的主存区。可见，段表实现了从逻辑段到物理主存区的映射。

图3-20　段式存储管理的地址变换机构

为了实现从逻辑地址到物理地址的变换功能，系统中设置了段表寄存器，用于存放段表始址和段表长度。在进行地址变换时，系统将逻辑地址中的段号S与段表长度L进行比较。若S≥L，表示段号太大，访问越界，于是产生越界中断信号；若未越界，则根据段表的始址和该段的段号，计算出该段对应段表项的位置，从中读出该段在主存中的起始地址，然后再检查段内地址d是否超过该段的段长SL。若超过，即d≥SL，同样发出越界中断信号；若未越界，则将该段的基址S′与段内地址d相加，得到要访问的主存物理地址。

【例3.9】　设某作业的段表如下：

（1）逻辑地址（0，128）、（1，30）、（2，88）、（3，290）和（4，100）能否转换为对应的物理地址？为什么？

（2）将问题（1）的逻辑地址分别转换成对应的物理地址。

解：

（1）逻辑地址（0，128）、（2，88）和（3，290）可以转换成对应的物理地址，而逻辑地址（1，30）和（4，100）不能转换为对应的物理地址，因为地址越界。

（2）逻辑地址（0，128）对应的物理地址是219＋128＝347；

逻辑地址（2，88）对应的物理地址是90＋88＝178；

逻辑地址（3，290）对应的物理地址是1327＋290＝1617。

3.3.5　段页式存储管理

分页和分段存储管理方式都各有其优缺点。因为分页的过程是由操作系统完成的，对用户是透明的，所以用户不必关心分页的过程，其缺点是不易实现共享；段是信息的逻辑单位，其优点是易于实现段的共享，即允许若干个进程共享一个或多个段，而且对段的保护也十分简单。如果对两种存储管理方式“各取所长”，则可以形成一种新的存储管理方式的系统——段页式系统。这种新系统既具有分页系统能有效地提高主存利用率的优点，又具有分段系统能很好地满足用户需要的长处，显然是一种比较有效的存储管理方式。

段页式系统的基本原理是先将整个主存划分成大小相等的存储块（页架），将用户程序按程序的逻辑关系分为若干个段，并为每个段赋予一个段名，再将每个段划分成若干页，以页架为单位离散分配。在段页式系统中，其地址结构由段号、段内页号和页内地址三部分组成。作业地址空间的结构如图3-21所示。

图3-21　段页式管理的地址结构

在段页式系统中，为了实现从逻辑地址到物理地址的变换，系统中必须同时配置段表和页表。由于将段中的页进行离散地分配，段表中的内容不再是段的主存始址和段长，而是页表始址和页表长度。在段页式系统中，有一个段表寄存器，存放段表始址和段表长度TL，其地址变换机构如图3-22所示。

图3-22　段页式存储管理的地址变换机构

3.3.6　虚拟存储管理

前面介绍的存储管理方案中，必须为每个作业分配足够的空间，以便装入全部信息。当主存空间不能满足作业要求时，作业便无法装入主存执行。

如果一个作业只部分装入主存便可开始启动运行，其余部分暂时留在磁盘上，需要时再装入主存。这样可以有效地利用主存空间，从用户角度看，该系统所具有的主存容量将比实际主存容量大得多，人们把这样的存储器称为虚拟存储器。虚拟存储器是为了扩大主存容量而采用的一种设计方法，其容量是由计算机的地址结构决定的。

1．程序局部性原理

早在1968年P. Denning就指出过，程序在执行时将呈现出局部性规律，即在一段时间内，程序的执行仅局限于某个部分。相应地，它所访问的存储空间也局限于某个区域内。程序的局限性表现在时间局限性和空间局限性两个方面。

（1）时间局限性。如果程序中的某条指令一旦执行，则不久的将来该指令可能再次被执行；如果某个存储单元被访问，则不久以后该存储单元可能再次被访问。产生时间局限性的典型原因是在程序中存在着大量的循环操作。

（2）空间局限性。一旦程序访问了某个存储单元，则在不久的将来，其附近的存储单元也最有可能被访问。即程序在一段时间内所访问的地址，可能集中在一定的范围内，其典型原因是程序是顺序执行的。

2．虚拟存储器的实现

虚拟存储器是具有请求调入功能和置换功能，能仅把作业的一部分装入主存便可运行作业的存储器系统，是能从逻辑上对主存容量进行扩充的一种虚拟的存储器系统。其逻辑容量由主存和外存容量之和以及CPU可寻址的范围来决定，其运行速度接近于主存速度，成本也下降。可见，虚拟存储技术是一种性能非常优越的存储器管理技术，故被广泛地应用于大、中、小型机器和微型机中。虚拟存储器实现主要有如下三种方式。

（1）请求分页系统。它是在分页系统的基础上，增加了请求调页功能和页面置换功能所形成的页式虚拟存储系统。它允许只装入若干页的用户程序和数据（而非全部程序）就可以启动运行，以后再通过调页功能和页面置换功能陆续把将要使用的页面调入主存，同时把暂不运行的页面置换到外存上，置换时以页面为单位。

（2）请求分段系统。它是在分段系统的基础上，增加了请求调段和分段置换功能所形成的段式虚拟存储系统。它允许只装入若干段（而非全部段）的用户程序和数据就可以启动运行，以后再通过调段功能和置换功能将不运行的段调出，同时调入将要运行的段，置换是以段为单位。

（3）请求段页式系统。它是在段页式系统的基础上，增加了请求调页和页面置换功能所形成的段页式虚拟存储系统。

3．请求分页管理的实现

请求分页是在纯分页系统的基础上，增加了请求调页功能、页面置换功能所形成的页式虚拟存储系统，是目前常用的一种虚拟存储器的方式。

请求分页的页表机制是在纯分页的页表机制上形成的，由于只将应用程序的一部分调入主存，还有一部分仍在磁盘上，故需在页表中再增加若干项，如状态位、访问字段和辅存地址等供程序（数据）在换进、换出时参考。

请求分页系统中的地址变换机构，是在分页系统的地址变换机构的基础上增加了某些功能，如产生和处理缺页中断、从主存中换出一页实现虚拟存储。

在请求分页系统中，每当所要访问的页面不在主存时，便要产生一个缺页中断，请求OS将所缺的页调入主存，这是由缺页中断机构完成的。缺页中断与一般中断的主要区别如下。

（1）缺页中断在指令执行期间产生和处理中断信号，而一般中断在一条指令执行完，下一条指令开始执行前检查和处理中断信号。

（2）发生缺页中断时，返回到被中断指令的开始重新执行该指令，而一般中断返回到下一条指令执行。

（3）一条指令在执行期间，可能会产生多次缺页中断。

【例3.10】　在某计算机中，假设某程序的COPY指令跨两个页面，且源地址A和目标地址B所涉及的区域也跨两个页面，如下图所示。

若地址为A和B操作数均不在内存，计算机执行COPY指令时，系统将产生（1）
缺页中断；若系统产生三次缺页中断，那么该程序有（2）
个页面在内存。

例题分析

从例题的图中可见，程序的COPY指令跨两个页面，且源地址A和目标地址B所涉及的区域也跨两个页面页内地址，这时，如果2，3，4和5号页面不在内存，系统执行“COPY A TO B”指令时，取地址为A的操作数。由于该操作数不在内存且跨两个页面2，3，需要将2，3页面装入内存，所以产生两次缺页中断。同理，取地址为B的操作数，由于该操作数不在内存且跨两个页面4和5，需要将4和5页面装入内存，所以产生两次缺页中断，共产生4次缺页中断。故例题空（1）的正确答案为C。

同理，如果1，2，3号页面不在内存，系统执行“COPY A TO B”指令时，由于程序的COPY指令跨两个页面，当取出指令分析是多字节的，那么系统将产生一次缺页中断取指令的后半部分；当取地址为A的操作数，由于该操作数不在内存，且跨两个页面2和3，需要将2和3页面装入内存，所以产生两次缺页中断，共产生3次缺页中断。故例题空（2）的正确答案为B。

4．页面置换算法

请求分页是在纯分页系统的基础上，增加了请求调页功能、页面置换功能所形成的页式虚拟存储系统，是目前常用的一种虚拟存储器的方式。在进程运行过程中，如果发生缺页，此时主存中又无空闲块时，为了保证进程能正常运行，就必须从主存中调出一页程序或数据送磁盘的对换区。但究竟将哪个页面调出，需要根据一定的页面置换算法来确定。置换算法的好坏将直接影响系统的性能，不适当的算法可能会导致系统发生“抖动”（thrashing）。即刚被换出的页很快又被访问，需重新调入，导致系统频繁地更换页面，以致一个进程在运行中把大部分时间花费在完成页面置换的工作上，这种现象称之为系统发生了“抖动”（也称颠簸）。请求分页系统的核心问题是选择合适的页面置换算法。常用的页面置换算法如下所述。

1）最佳（Optimal）置换算法

这是一种理想化的算法，即选择哪些是永不使用的，或者是在最长时间内不再被访问的页面置换出去。这种方法性能最好，但实际上难于实现。要确定哪一个页面是未来最长时间内不再被访问的是很难的，所以该算法通常用来评价其他算法。

【例3.11】　假定系统为某进程分配了三个物理块，进程访问页面的顺序为0，7，6，5，7，4，7，3，5，4，7，4，5，6，5，7，6，0，7，6。如图3-23所示，进程运行时先将0，7，6三个页面装入主存。当进程访问页面5时，产生缺页中断，根据最佳置换算法，页面0将在第18次才被访问，是三页中将最久不被访问的页面，所以被淘汰。接着访问页面7时，发现已在主存中，而不会产生缺页中断，依此类推。从图中可以看出，采用最佳置换算法，产生了9次缺页中断，发生了6次页面置换。

图3-23　最佳置换算法

2）先进先出（FIFO）置换算法

该算法总是淘汰最先进入主存的页面，即选择在主存中驻留时间最久的页面予以淘汰。该算法实现简单，只须把一个进程调入主存的页面，按先后次序链接成一个队列，并设置一个指针即可。它是一种最直观，性能最差的算法，有BELADY异常现象：对页面访问序列ABCDABEABCDE，当分配的物理块从3块增加到4块时，缺页次数反而增加。

【例3.12】　假定系统中某进程访问页面的顺序为0，7，6，5，7，4，7，3，5，4，7，4，5，6，5，7，6，0，7，6。利用FIFO算法对上例进行页面置换的结果如图3-24所示。

图3-24　先进先出置换算法

从图中可以看出，共发生了11次页面置换，缺页次数为14次。

3）最近最少未使用（Least Recently Used, LRU）置换算法

该算法是选择最近最少未使用的页面予以淘汰，系统在每个页面设置一个访问字段，用以记录这个页面自上次被访问以来所经历的时间T，当要淘汰一个页面时，选择T最大的页面。但在实现时需要硬件的支持（寄存器或栈）。

【例3.13】　假定系统中某进程访问页面的顺序为0，7，6，5，7，4，7，3，5，4，7，4，5，6，5，7，6，0，7，6。利用LRU算法对上例进行页面置换的结果如图3-25所示。

图3-25　最近最少未使用置换算法

从图中可以看出，共发生了10次面置换，缺页次数13次。

4）最近未用（Not Used Recently, NUR）置换算法

将最近一段时间未引用过的页面换出。这是一种LRU的近似算法。该算法为每个页面设置一位访问位，将主存中的所有页面都通过链接指针链成一个循环队列。当某页被访问时，其访问位置1。在选择一页淘汰时，检查其访问位，如果是0，则选择该页换出；若为1，则重新置为0，暂不换出该页，在循环队列中检查下一个页面，直到访问位为0的页面为止。由于该算法只有一位访问位，只能用它表示该页是否已经使用过，而置换时是将未使用过的页面换出去，所以把该算法称为最近未用算法。

5．工作集

程序在运行中所产生的缺页情况，会影响程序的运行速度及系统性能，而缺页率的高低又与每个进程所占用的物理块数目有关。究竟应该为每个进程分配多少个物理块，才能把缺页率保持在一个合理的水平上？否则会因为进程频繁地从辅存请求页面，而出现“颠簸”（也称抖动）现象。所谓工作集，是指在某段时间间隔（Δ）里，进程实际要访问的页面的集合。工作集的理论是在1968年由Denning提出来的，他认为，虽然程序只需有少量的几页在主存就可以运行，但为了使程序能够有效地运行，较少地产生缺页，就必须使程序的工作集驻留在主存中。把某进程在时间t的工作集记为w（t，Δ），把变量Δ称为工作集“窗口尺寸（Windows Size）”。正确选择工作集窗口（Δ）的大小，对存储器的有效利用和系统吞吐量的提高，都将产生重要的影响。

程序在运行时对页面的访问是不均匀的，即往往在某段时间内的访问仅局限于较少的若干个页面，如果能够预知程序在某段时间间隔内要访问哪些页面，并能将它们提前调入主存，将会大大地降低缺页率，从而减少置换工作，提高CPU的利用率。当每个工作集都已达到最小值时，虚存管理程序跟踪进程的缺页数量，根据主存中自由页面数量可以适当增加其工作集的大小。

3.4　设备管理

设备管理是操作系统中最繁杂而且与硬件紧密相关的部分。设备管理不但要管理实际I/O操作的设备（如磁盘机、打印机），还要管理诸如设备控制器、DMA控制器、中断控制器和I/O处理机（通道）等支持设备。设备管理包括各种设备分配、缓冲区管理和实际物理I/O设备操作，通过管理达到提高设备利用率和方便用户的目的。

3.4.1　设备管理概述

设备是计算机系统与外界交互的工具，具体负责计算机与外部的输入输出工作，所以常称为外部设备（简称外设）。在计算机系统中，将负责管理和输入输出的机构称为I/O系统。因此，I/O系统由设备、控制器、通道（具有通道的计算机系统）、总线和I/O软件组成。

1．设备的分类

现代计算机系统都配有各种各样的设备，如打印机、显示器、绘图仪、扫描仪、键盘和鼠标等。设备可以有各种不同的分类方式。

（1）按数据组织分类。分为块设备（Block Device）和字符设备（Character Device）。块设备是指以数据块为单位来组织和传送数据信息的设备，如磁盘。字符设备是指以单个字符为单位来传送数据信息的设备，如交互式终端、打印机等。

（2）从资源分配角度分类。分为独占设备、共享设备和虚拟设备。独占设备是指在一段时间内只允许一个用户（进程）访问的设备，大多数低速的I/O设备，如用户终端、打印机等属于这类设备。共享设备是指在一段时间内允许多个进程同时访问的设备。显然，共享设备必须是可寻址的和可随机访问的设备。典型的共享设备是磁盘。虚拟设备是指通过虚拟技术将一台独占设备变换为若干台供多个用户（进程）共享的逻辑设备。一般可以利用假脱机技术（Spooling技术）实现虚拟设备。

（3）按数据传输率分类。分为低速设备、中速设备和高速设备。低速设备是指传输速率为每秒钟几个字节到数百个字节的设备，典型的设备有键盘、鼠标、语音的输入等。中速设备是指传输速率在每秒钟数千个字节至数十千个字节的设备，典型的设备有行式打印机、激光打印机等。高速设备是指传输速率在数百千个字节至数兆字节的设备，典型的设备有磁带机、磁盘机和光盘机等。

2．设备管理的目标与任务

设备管理的目标主要是如何提高设备的利用率，为用户提供方便统一的界面。提高设备的利用率，就是提高CPU与I/O设备之间的并行操作程度，主要利用的技术有中断技术、DMA技术、通道技术和缓冲技术。

设备管理的任务是保证在多道程序环境下，当多个进程竞争使用设备时，按一定策略分配和管理各种设备，控制设备的各种操作，完成I/O设备与主存之间的数据交换。

设备管理的主要功能是动态地掌握并记录设备的状态、设备分配和释放、缓冲区管理、实现物理I/O设备的操作、提供设备使用的用户接口及设备的访问和控制。

3.4.2　I/O软件

设备管理软件的设计水平决定了设备管理的效率。从事I/O设备管理软件的结构，其基本思想是分层构造，也就是说把设备管理软件组织成为一系列的层次。其中低层与硬件相关，它把硬件与较高层次的软件隔离开来。而最高层的软件则向应用提供一个友好的、清晰而统一的接口。

设计I/O软件的主要目标是设备独立性和统一命名。I/O软件独立于设备，就可以提高设备管理软件的设计效率。当输入输出设备更新时，没有必要重新编写全部设备驱动程序。在实际应用中也可以看到，在常用操作系统中，只要安装了相对应的设备驱动程序，就可以很方便地安装好新的输入输出设备，甚至不必重新编译就能将设备管理程序移到他处执行。

I/O设备管理软件一般分为4层：中断处理程序、设备驱动程序、与设备无关的系统软件和用户级软件。至于一些具体分层时细节上的处理，是依赖于系统的，没有严格的划分，只要有利于设备独立这一目标，可以为了提高效率而设计不同的层次结构。

I/O软件的所有层次及每一层的主要功能如图3-26所示。

图3-26　I/O系统的层次结构与每层的主要功能

图中的箭头给出了I/O部分的控制流。这里举一个读硬盘文件的例子。当用户程序试图读一个硬盘文件时，需要通过操作系统实现这一操作。与设备无关软件检查高速缓存中有无要读的数据块。若没有，则调用设备驱动程序，向I/O硬件发出一个请求。然后，用户进程阻塞并等待磁盘操作的完成。当磁盘操作完成时，硬件产生一个中断，转入中断处理程序。中断处理程序检查中断的原因，认识到这时磁盘读取操作已经完成，于是唤醒用户进程取回从磁盘读取的信息，从而结束此次I/O请求。用户进程在得到了所需的硬盘文件内容之后，继续运行。

3.4.3　设备管理采用的相关缓冲技术

1．通道技术

引入通道的目的是使数据的传输独立于CPU，使CPU从繁琐的I/O工作中解脱出来。设置通道后，CPU只需向通道发出I/O命令，通道收到命令后，从主存中取出本次I/O要执行的通道程序并执行，仅当通道完成了I/O任务后，才向CPU发出中断信号。

根据信息交换方式的不同，将通道分为字节多路通道、数组选择通道和数组多路通道三类。由于通道价格昂贵，导致计算机系统中的通道数是有限的，这往往会成为输入输出的“瓶颈”问题。一个单通路的I/O系统中主存和设备之间只有一条通路。一旦某通道被设备占用，即使另一通道空闲，连接该通道的其他设备也只有等待。解决“瓶颈”问题的最有效方法是增加设备到主机之间的通路，使得主存和设备之间有两条以上的通路。

2．DMA技术

直接主存存取（Direct Memory Access, DMA）是指数据在主存与I/O设备间的直接成块传送，即在主存与I/O设备间传送一个数据块的过程中，不需要CPU的任何干涉，只需要CPU在过程开始启动（即向设备发出“传送一块数据”的命令）与过程结束（CPU通过轮询或中断得知过程是否结束和下次操作是否准备就绪）时的处理，实际操作由DMA硬件直接执行完成，CPU在此传送过程中可做别的事情。例如，在非DMA时，打印2048字节至少需要执行2048次输出指令，加上2048次中断处理的代价。而在DMA情况下，若一次DMA可传送512个字节，则只需要执行4次输出指令和处理4次打印机中断。若一次DMA可传送字节数大于等于2048个字节，则只需要执行一次输出指令和处理一次打印机中断。

3．缓冲技术

缓冲技术可提高外设利用率，尽可能使外设处于忙状态。缓冲技术可以采用硬件缓冲和软件缓冲。硬件缓冲是利用专门的硬件寄存器作为缓冲，软件缓冲是通过操作系统来管理的。引入缓冲的主要原因有以下几个方面。

（1）缓和CPU与I/O设备间速度不匹配的矛盾。

（2）减少对CPU的中断频率，放宽对中断响应时间的限制。

（3）提高CPU和I/O设备之间的并行性。

在所有的I/O设备与处理机（主存）之间都使用了缓冲区来交换数据，所以操作系统必须组织和管理好这些缓冲区。缓冲可分为单缓冲、双缓冲、多缓冲和环形缓冲。

4．Spooling技术

Spooling（Simultaneous Peripheral Operations On Line，外围设备联机操作），常简称为Spooling系统或假脱机系统。所谓Spooling技术，实际上是用一类物理设备模拟另一类物理设备的技术，是使独占使用的设备变成多台虚拟设备的一种技术，也是一种速度匹配技术。

Spooling系统是由“预输入程序”、“缓输出程序”和“井管理程序”以及输入和输出井组成的。其中，输入井和输出井是为了存放从输入设备输入的信息以及作业执行的结果，系统在辅助存储器上开辟的存储区域。Spooling系统的组成和结构如图3-27所示。

图3-27　Spooling系统的组成和结构

Spooling系统的工作过程是操作系统初启后激活Spooling预输入程序使它处于捕获输入请求的状态，一旦有输入请求消息，Spooling输入程序立即得到执行，把装在输入设备上的作业输入到硬盘的输入井中，并填写好作业表以便在作业执行中要求输入信息时，可以随时找到它们的存放位置。当作业需要输出数据时，可以先将数据送到输出井，当输出设备空闲时，由Spooling输出程序把硬盘上输出井的数据送到慢速的输出设备上。

Spooling系统中拥有一张作业表用来登记进入系统的所有作业的作业名、状态和预输入表位置等信息。每个用户作业拥有一张预输入表来登记该作业的各个文件的情况，包括设备类、信息长度及存放位置等。输入井中的作业有如下4种状态。

（1）提交状态。作业的信息正从输入设备上预输入。

（2）后备状态。作业预输入结束但未被选中执行。

（3）执行状态。作业已被选中运行，运行过程中，它可从输入井中读取数据信息，也可向输出井写信息。

（4）完成状态。作业已经撤离，该作业的执行结果等待缓输出。

3.4.4　磁盘调度

磁盘是可被多个进程共享的设备。当有多个进程请求访问磁盘时，为了保证信息的安全，系统每一时刻只允许一个进程启动磁盘进行I/O操作，其余的进程只能等待。因此，操作系统应采用一种适当的调度算法，使各进程对磁盘的平均访问（主要是寻道）时间最小。磁盘调度分为移臂调度和旋转调度两类，并且是先进行移臂调度，然后进行旋转调度。由于访问磁盘最耗时的是寻道时间，因此，磁盘调度的目标是使磁盘的平均寻道时间最少。

1．磁盘驱动调度

常用的磁盘调度算法如下。

（1）先来先服务（First-Come First-Served, FCFS）。这是最简单的磁盘调度算法。它根据进程请求访问磁盘的先后次序进行调度。此算法的优点是公平、简单，且每个进程的请求都能依次得到处理，不会出现某进程的请求长期得不到满足的情况。但此算法由于未对寻道进行优化，致使平均寻道时间可能较长。

（2）最短寻道时间优先（Shortest Seek Time First, SSTF）。该算法选择这样的进程。其要求访问的磁道与当前磁头所在的磁道距离最近，使得每次的寻道时间最短。但这种调度算法却不能保证平均寻道时间最短。

（3）扫描算法（SCAN）。扫描算法不仅考虑到欲访问的磁道与当前磁道的距离，更优先考虑的是磁头的当前移动方向。例如，当磁头正在由里向外移动时，SCAN算法所选择的下一个访问对象应是其欲访问的磁道既在当前磁道之外，又是距离最近的。这样由里向外地访问，直至再无更外的磁道需要访问时，才将磁臂换向，由外向里移动。这时，同样也是每次选择在当前磁道之内，且距离最近的进程来调度。这样，磁头逐步地向里移动，直至再无更里面的磁道需要访问。显然，这种方式避免了饥饿现象的出现。这种算法中，磁头移动的规律颇似电梯的运行，故又常称为电梯调度算法。

（4）单向扫描调度算法（CSCAN）。SCAN存在这样的问题：当磁头刚从里向外移动过某一磁道时，恰有一进程请求访问此磁道，这时该进程必须等待，待磁头从里向外，然后再从外向里扫描完所有要访问的磁道后，才处理该进程的请求，致使该进程的请求被严重地推迟。为了减少这种延迟，算法规定磁头作单向移动。

2．旋转调度算法

当移动臂定位后，有多个进程等待访问该柱面时，应当如何决定这些进程的访问顺序？这就是旋转调度要考虑的问题。显然，系统应该选择延迟时间最短的进程对磁盘的扇区进行访问。当有若干等待进程请求访问磁盘上的信息时，旋转调度应考虑如下情况。

（1）进程请求访问的是同一磁道上不同编号的扇区。

（2）进程请求访问的是不同磁道上不同编号的扇区。

（3）进程请求访问的是不同磁道上具有相同编号的扇区。

对于（1）与（2），旋转调度总是让首先到达读写磁头位置下的扇区先进行传送操作；对于（3），旋转调度可以任选一个读写磁头位置下的扇区进行传送操作。

【例3.14】　当进程请求读磁盘时，操作系统（1）
。假设磁盘每磁道有10个扇区，移动臂位于18号柱面上，且进程的请求序列如表3-3所示。

表3-3　进程的请求序列

那么，按照最短寻道时间优先的响应序列为（2）
。

（1）A．只需要进行旋转调度，无须进行移臂调度

B．旋转、移臂调度同时进行

C．先进行移臂调度，再进行旋转调度

D．先进行旋转调度，再进行移臂调度

（2）

分析：空（1）的正确答案为C；空（2）的正确答案为A。

当进程请求读磁盘时，操作系统先进行移臂调度，再进行旋转调度。由于移动臂位于18号柱面上，按照最短寻道时间优先的响应柱面序列为20→15→8→6→40。按照旋转调度的原则：进程在20号柱面上的响应序列为②→③，因为进程访问的是不同磁道上的不同编号的扇区，旋转调度总是让首先到达读写磁头位置下的扇区先进行传送操作。进程在15号柱面上的响应序列为⑤→①→⑧或⑤→⑧→①。对于①和⑧可以任选一个进行读写，因为进程访问的是不同磁道上具有相同编号的扇区，旋转调度可以任选一个读写磁头位置下的扇区进行传送操作。④在40号柱面上。⑥在6号柱面上。⑦在8号柱面上。

【例3.15】　数据存储在磁盘上的排列方式会影响I/O服务的总时间。假设每磁道划分成10个物理块，每块存放1个逻辑记录。逻辑记录R1，R2，…，R10存放在同一个磁道上，记录的安排顺序如下表所示。

假定磁盘的旋转速度为每周20ms，磁头当前处在R1的开始处。若系统顺序处理这些记录，使用单缓冲区，每个记录处理时间为4ms，则处理这10个记录的最长时间为（1）
；对信息存储进行优化分布后，处理10个记录的最少时间为（2）
。

空（1）分析：系统读记录的时间为20/10＝2ms，对第一种情况，系统读出并处理记录R1之后，将转到记录R4的开始处，所以为了读出记录R2，磁盘必须再转一圈，需要2ms（读记录）加20ms（转一圈）的时间。这样，处理10个记录的总时间应为处理前9个记录（即R1，R2，…，R9）的总时间再加上读R10和处理时间，即9×22ms＋6ms＝204ms。

空（2）分析：对于第二种情况，若对信息进行分布优化的结果如下表所示。

可以看出，当读出记录R1并处理结束后，磁头刚好转至R2记录的开始处，立即就可以读出并处理，因此处理10个记录的总时间为10×（2ms（读记录）＋4ms（处理记录））＝10×6ms＝60ms。

3.5　文件管理

随着计算机应用需求不断增长，快速、高效地处理大量的信息是计算机的首要任务之一，而这些信息通常是存储在大容量的外存储器上。然而在早期，用户要访问外存储器上的信息是很麻烦的，不仅要考虑信息在外存储器上的存放位置，而且要记住信息在外存储器的分布情况，构造I/O程序。稍不注意，就会破坏已存放的信息。特别是多道程序技术出现后，多个用户之间根本无法预料各个不同程序间的信息在外存储器上是如何分配的。鉴于这些原因，引入文件系统专门负责管理外存储器上的信息，使用户可以“按名”高效、快速和方便地存储信息。

3.5.1　文件与文件系统

1．文件

文件（file）是具有符号名的、在逻辑上具有完整意义的一组相关信息项的集合。例如，一个源程序、一个目标程序、编译程序、一批待加工的数据和各种文档等都可以各自组成一个文件。

信息项是构成文件内容的基本单位，可以是一个字符，也可以是一个记录，记录可以等长，也可以不等长。一个文件包括文件体和文件说明。文件体是文件真实的内容；文件说明是操作系统为了管理文件所用到的信息，包括文件名、文件内部标识、文件的类型、文件存储地址、文件的长度、访问权限、建立时间和访问时间等。

文件是一种抽象机制，它隐蔽了硬件和实现细节，提供了将信息保存在磁盘上而且便于以后读取的手段，使用户不必了解信息存储的方法、位置以及存储设备实际运作方式便可存取信息。因此，在文件管理中的一个非常关键的问题在于文件的命名。文件名是在进程创建文件时确定的，以后这个文件将独立于进程存在直到它被显式删除。其他进程要使用文件时必须显式指出该文件名，操作系统根据文件名对其进行控制和管理。不同的操作系统文件命名规则有所不同，即文件名字的格式和长度因系统而异。

2．文件系统

由于计算机系统处理的信息量越来越大，所以不可能将所有的信息保存到主存中。特别是在多用户系统中，既要保证各用户文件存放的位置不冲突，又要防止任一用户对外存储器（简称外存）空间占而不用；既要保证各用户文件在未经许可的情况下不被窃取和破坏，又要允许在特定的条件下多个用户共享某些文件。因此，需要设立一个公共的信息管理机制来负责统一管理外存和外存上的文件。

所谓文件管理系统，就是操作系统中实现文件统一管理的一组软件和相关数据的集合，专门负责管理和存取文件信息的软件机构，简称文件系统。文件系统的功能包括按名存取，即用户可以“按名存取”，而不是“按地址存取”；统一的用户接口，在不同设备上提供同样的接口，方便用户操作和编程；并发访问和控制，在多道程序系统中支持对文件的并发访问和控制；安全性控制，在多用户系统中的不同用户对同一文件可有不同的访问权限；优化性能，采用相关技术提高系统对文件的存储效率、检索和读写性能；差错恢复，能够验证文件的正确性，并具有一定的差错恢复能力。

3．文件的类型

（1）按文件性质和用途可将文件分为系统文件、库文件和用户文件。

（2）按信息保存期限分类可将文件分为临时文件、档案文件和永久文件。

（3）按文件的保护方式分类可将文件分为只读文件、读写文件、可执行文件和不保护文件。

（4）UNIX系统将文件分为普通文件、目录文件和设备文件（特殊文件）。

（5）目前常用的文件系统类型有FAT、Vfat、NTFS、Ext2和HPFS等。

文件分类的目的是对不同文件进行管理，提高系统效率；提高用户界面友好性。当然，根据文件的存取方法和物理结构不同还可以将文件分为不同的类型，这在文件的逻辑结构和文件的物理结构中介绍。

3.5.2　文件的结构和组织

文件的结构是指文件的组织形式。从用户角度看到的文件组织形式称为文件的逻辑结构，文件系统的用户只要知道所需文件的文件名，就可存取文件中的信息，而无须知道这些文件究竟存放在什么地方。从实现的角度看文件在文件存储器上的存放方式，称为文件的物理结构。

1．文件的逻辑结构

文件的逻辑结构可分为两大类：一是有结构的记录式文件，它是由一个以上的记录构成的文件，故又称为记录式文件；二是无结构的流式文件，它是由一串顺序字符流构成的文件。

1）有结构的记录式文件

在记录式文件中，所有的记录通常都是描述一个实体集的，有着相同或不同数目的数据项，记录的长度可分为定长和不定长两类。

（1）定长记录。指文件中所有记录的长度相同。所有记录中的各个数据项都处在记录中相同的位置，具有相同的顺序及相同的长度，文件的长度用记录数目表示。定长记录的特点是处理方便，开销小，是目前较常用的一种记录格式，被广泛用于数据处理中。

（2）变长记录。指文件中各记录的长度不相同。这是因为：一个记录中所包含的数据项数目可能不同，如书的著作者、论文中的关键词；数据项本身的长度不定，如病历记录中的病因、病史，科技情报记录中的摘要等。但是，不论是哪一种结构，在处理前每个记录的长度是可知的。

2）无结构的流式文件

文件体为字节流，不划分记录。无结构的流式文件通常采用顺序访问方式，并且每次读写访问可以指定任意数据长度，其长度以字节为单位。对流式文件访问，是利用读写指针指出下一个要访问的字符。可以把流式文件看作是记录式文件的一个特例。在UNIX系统中，所有的文件都被看作是流式文件，即使是有结构的文件，也被视为流式文件，系统不对文件进行格式处理。

2．文件的物理结构

文件的物理结构是指文件的内部组织形式，即文件在物理存储设备上的存放方法。由于文件的物理结构决定了文件在存储设备上的存放位置，所以文件的逻辑块号到物理块号的转换也是由文件的物理结构决定的。根据用户和系统管理上的需要，可采用多种方法来组织文件，下面介绍几种常见的文件物理结构。

（1）连续结构。连续结构也称顺序结构，它将逻辑上连续的文件信息（如记录）依次连续存放在连续编号的物理块上。只要知道文件的起始物理块号和文件的长度，就可以很方便地进行文件的存取。

对文件诸记录进行批量存取时，连续结构在所有逻辑文件中的存取效率是最高的。但在交互应用的场合，如果用户（程序）要求随机地查找或修改单个记录，此时系统需要逐个地查找各个记录，这样采用连续结构所表现出来的性能就可能很差，尤其是当文件较大时情况更为严重。连续结构的另一个缺点是不便于记录的增加或删除操作。为了解决这个问题，可以为采用连续结构的文件配置一个运行记录文件（Log File）或称为事务文件（Transactor File），规定每隔一定时间，例如4小时，将运行记录文件与原来的主文件进行合并，产生一个新文件。这样，不必每次对记录进行增加或删除操作，物理移动磁盘信息，使其成为连续结构。

（2）链接结构。链接结构也称串联结构，它是将逻辑上连续的文件信息（如记录）存放在不连续的物理块上，每个物理块设有一个指针指向下一个物理块。因此，只要知道文件的第一个物理块号，就可以按链指针查找整个文件。

（3）索引结构。采用索引结构时，将逻辑上连续的文件信息（如记录）存放在不连续的物理块中，系统为每个文件建立一张索引表。索引表记录了文件信息所在的逻辑块号对应的物理块号，并将索引表的起始地址放在与文件对应的文件目录项中。

（4）多个物理块的索引表。索引表是在文件建立时由系统自动建立的，并与文件一起存放在同一文件卷上。根据一个文件大小的不同，其索引表占用物理块的个数不等，一般占一个或几个物理块。多个物理块的索引表可有两种组织方式：链接文件和多重索引方式。

在UNIX文件系统中采用的是三级索引结构，文件系统中inode是基本的构件，它表示文件系统树型结构的节点。UNIX文件索引表项分4种寻址方式：直接寻址、一级间接寻址、二级间接寻址和三级间接寻址。

3.5.3　文件目录

为了实现“按名存取”，系统必须为每个文件设置用于描述和控制文件的数据结构，它至少要包括文件名和存放文件的物理地址，这个数据结构称为文件控制块（FCB），文件控制块的有序集合称为文件目录。换句话说，文件目录是由文件控制块组成的，专门用于文件的检索。文件控制块也称为文件的说明或文件目录项（简称目录项）。

1．文件控制块

文件控制块中包含以下三类信息：基本信息类、存取控制信息类和使用信息类。

（1）基本信息类。例如文件名、文件的物理地址、文件长度和文件块数等。

（2）存取控制信息类。文件的存取权限，像UNIX用户分成文件主、同组用户和一般用户三类，这三类用户的读写执行（RWX）权限。

（3）使用信息类。文件建立日期、最后一次修改日期、最后一次访问的日期；当前使用的信息如打开文件的进程数，在文件上的等待队列等。

2．目录结构

文件目录结构的组织方式直接影响到文件的存取速度，关系到文件共享性和安全性，因此组织好文件的目录是设计文件系统的重要环节。常见的目录结构有三种：一级目录结构、二级目录结构和多级目录结构。

（1）一级目录结构。一级目录的整个目录组织是一个线性结构，在整个系统中只需建立一张目录表，系统为每个文件分配一个目录项。一级目录结构简单，但缺点是查找速度慢，不允许重名和不便于实现文件共享等，因此它主要用在单用户环境中。

（2）二级目录结构。为了克服一级目录结构存在的缺点，引入了二级目录结构。二级目录结构是由主文件目录（Master File Directory, MFD）和用户目录（User File Directory, UFD）组成的。在主文件目录中，每个用户文件目录都占有一个目录项，其目录项中包括用户名和指向该用户目录文件的指针。用户目录是由用户所有文件的目录项组成。

二级目录结构基本上克服了单级目录的缺点，其优点是提高了检索目录的速度，较好地解决了重名问题。采用二级目录结构也存在一些问题。该结构虽然能有效地将多个用户隔离开，这种隔离在各个用户之间完全无关时是一个优点；但当多个用户之间要相互合作去共同完成一个大任务，且一个用户又需去访问其他用户的文件时，这种隔离便成为一个缺点，因为这种隔离使诸用户之间不便于共享文件。

（3）多级目录结构。为了解决以上问题，在多道程序设计系统中常采用多级目录结构，这种目录结构像一棵倒置的有根树，所以也称为树型目录结构。从树根向下，每一个节点是一个目录，叶节点是文件。MS-DOS和UNIX等操作系统均采用多级目录结构。

采用多级目录结构的文件系统中，用户要访问一个文件，必须指出文件所在的路径名，路径名是从根目录开始到该文件的通路上所有各级目录名拼起来得到的。各目录名之间，目录名与文件名之间需要用分隔符隔开。例如，在MS-DOS中分隔符为“＼”，在UNIX中分隔符为“／”。绝对路径名（absolute path name）是指从根目录“／”开始的完整文件名，即它是由从根目录开始的所有目录名以及文件名构成的。

【例3.16】　在下图所示的树型文件系统中，方框表示目录，圆圈表示文件，“／”表示路径中的分隔符，“／”在路径之首时表示根目录。

假设当前目录是D2，进程A以如下两种方式打开文件f2。

方式①

方式②

其中，方式①的工作效率比方式②的工作效率高，因为采用方式①，文件系统是从（2）
。

（1）A．/D2/W2

B．D2/W2

C．W2

D．/W2

（2）A．根目录开始查找文件f2，系统查找时间少，读取f2文件次数不变

B．当前路径开始查找文件f2，系统查找时间少，读取f2文件次数少

C．根目录开始查找文件f2，系统查找时间少，读取f2文件次数少

D．当前路径开始查找文件f2，系统查找时间少，读取f2文件次数不变

分析：空（1）的正确答案是C。因为在树型目录结构中，树的根节点为根目录，数据文件作为树叶，其他所有目录均作为树的节点。在树型目录结构中，从根目录到任何数据文件之间只有唯一的一条通路。从树根开始，把全部目录文件名与数据文件名依次用“／”连接起来，构成该数据文件的路径名，且每个数据文件的路径名是唯一的。这样，可以解决文件重名问题。所以，虽然数据文件名均为f2，但不一定是相同的文件。从树根开始的路径名为绝对路径名，如果文件系统有很多级时，使用绝对路径即不方便又费时间，所以引入相对路径名，即从当前目录开始，再逐级通过中间的目录文件，最后到达所要访问的数据文件。根据题意，方式①的工作效率比方式②的工作效率高，正确答案为C。同样，从当前目录开始，采用相对路径名较之采用绝对路径名可以减少系统访问目录文件的次数，但是访问文件f2的次数是不变的，所以对于空（2）的正确答案为D。

3.5.4　存取方法和存储空间的管理

1．文件的存取方法

文件的存取方法是指读写文件存储器上的一个物理块的方法，通常有顺序存取和随机存取两种方法。顺序存取是指对文件中的信息按顺序依次读写的方式；随机存取是指对文件中的信息可以按任意的次序随机地读写文件中的信息。

（1）顺序存取法。在提供记录式文件结构的系统中，顺序存取法就是严格按物理记录排列的顺序依次读取。如果当前读取的是Ri
记录，下一次要读取的记录自动地确定为Ri＋1
。在只提供无结构的流式文件中，顺序存取法是按读写的位移（offset）从当前位置开始读写，每读完一段信息，读写位移自动加上这段信息的长度，以便读下一段信息。下面着重讨论记录式文件的存取法。

（2）直接存取法。直接存取法允许用户随意存取文件中任意一个物理记录。对于无结构的流式文件，采用直接存取法，必须事先将读写偏移移动到待读写信息的位置上，然后再进行读写。

（3）按键存取法。按键存取法是直接存取法的一种，它不是根据记录的编号或地址来存取文件中的记录，而是根据文件中各记录的某个数据项内容来存取记录的，这种数据项称之为“键”。因此，将这种存取法称之为按键存取法。

2．文件存储空间的管理

外存具有大容量的存储空间，被多用户共享，用户执行程序经常要在磁盘上存储文件和删除文件，因此，文件系统必须对磁盘空间进行管理。外存空闲空间管理的数据结构通常称为磁盘分配表（disk allocation table）。常用的空闲空间的管理方法有位示图、空闲区表和空闲块链三种。

（1）空闲区表。将外存空间上一个连续未分配区域称为“空闲区”。操作系统为磁盘外存上所有空闲区建立一张空闲表，每个表项对应一个空闲区，空闲表中包含序号、空闲区的第一块号、空闲块的块数和状态等信息，如表3-4所示。它适用于连续文件结构。

表3-4　空闲区表

（2）位示图。这种方法是在外存上建立一张位示图（bitmap），记录文件存储器的使用情况。每一位对应文件存储器上的一个物理块，取值0和1分别表示空闲和占用。文件存储器上的物理块依次编号为0、1、2、…。假如系统中字长为32位，那么在位示图中的第一个字对应文件存储器上的0、1、2、…、31号物理块；第二个字对应文件存储器上的32、33、34、…、63号物理块，依此类推。

这种方法的主要特点是位示图的大小由磁盘空间的大小（物理块总数）决定，位示图的描述能力强，适合各种物理结构。

（3）空闲块链。每个空闲物理块中有指向下一个空闲物理块的指针，所有空闲物理块构成一个链表，链表的头指针放在文件存储器的特定位置上（如管理块中）。不需要磁盘分配表，节省空间。每次申请空闲物理块只需根据链表的头指针取出第一个空闲物理块，根据第一个空闲物理块的指针可找到第二个空闲物理块，依此类推即可。

（4）成组链接法。在UNIX系统中，将空闲块分成若干组，每100个空闲块为一组，每组的第一个空闲块登记了下一组空闲块的物理盘块号和空闲块总数。假如一个组的第一个空闲块号等于0的话，意味着该组是最后一组，无下一组空闲块。

3.5.5　文件的使用

文件系统将用户的逻辑文件按一定的组织方式转换成物理文件存放到文件存储器上，也就是说，文件系统为每个文件与该文件在磁盘上的存放位置建立了对应关系。当用户使用文件时，文件系统通过用户给出的文件名，查出对应文件的存放位置，读出文件的内容。在多用户环境下，为了文件安全和保护起见，操作系统为每个文件建立和维护关于文件主、访问权限等方面的信息。为此，操作系统在操作级（命令级）和编程级（系统调用和函数）向用户提供文件的服务。

操作系统在操作级向用户提供的命令有目录管理类命令、文件操作类命令（如复制、删除和修改）和文件管理类命令（如设置文件权限）等。

3.5.6　文件的共享和保护

1．文件的共享

文件共享是指不同用户进程使用同一文件，它不仅是不同用户完成同一任务所必须的功能，而且还可以节省大量的主存空间，减少由于文件复制而增加的访问外存的次数。文件共享有多种形式，采用文件名和文件说明分离的目录结构有利于实现文件共享。

常见的文件链接有硬链接和符号链接两种。

1）硬链接

文件的硬链接是指两个文件目录表目指向同一个索引节点的链接，该链接也称基于索引节点的链接。换句话说，硬链接是指不同文件名与同一个文件实体的链接。文件硬链接不利于文件主删除它拥有的文件，因为文件主要删除它拥有的共享文件，必须首先删除（关闭）所有的硬链接，否则就会造成共享该文件的用户的目录表目指针悬空。

例如，在UNIX系统中的ln命令，可以将多个文件名与一个文件体建立链接，其格式为：

in 文件名　新文件名

或

in 文件名　目录名

ls命令放在/bin子目录下，可在/usr/bin子目录下设置一个DOS兼容的命令dir，执行该命令相当于执行ls命令。使用命令ln可给一个已存在文件增加一个新文件名，即文件链接数增加1，此种链接是不能跨越文件系统的。为了共享文件，只是在两个不同子目录下取了不同的文件名ls和dir，但它们具有相同的索引节点。UNIX这种文件的结构称为树型带勾链的目录结构。在文件的索引节点中，di_nlink变量表示链接到该索引节点上的链接数；在用命令ls-l长列表显示时，文件的第2项数据项表示链接数。

2）符号链接

符号链接在建立的新文件或目录并与原来文件或目录的路径名进行映射，当访问一个符号链接时，系统通过该映射找到原文件的路径，并对其进行访问。

例如，在UNIX系统中的ln-s命令建立符号链接。此时，系统为共享的用户创建一个link类型的新文件，将这新文件登录在该用户共享目录项中，这个link型文件包含链接文件的路径名。该类文件在用ls命令长列表显示时，文件链接数为1。

采用符号连接可以跨越文件系统，甚至可以通过计算机网络连接到世界上任何地方的机器中的文件，此时只需提供该文件所在的地址，以及在该机器中的文件路径。

符号链接的缺点是其他用户读取符号链接的共享文件比读取硬链接的共享文件需要增加读盘操作的次数。因为其他用户去读符号链接的共享文件时，系统中根据给定的文件路径名，逐个分量地去查找目录，通过多次读盘操作才能找到该文件的索引节点，而用硬链接的共享文件的目录文件表目中已包括了共享文件的索引节点号。

2．文件的保护

文件系统对文件的保护常采用存取控制方式进行。所谓存取控制，就是不同的用户对文件的访问规定不同的权限，以防止文件被未经文件主同意的用户访问。

（1）存取控制矩阵。理论上，存取控制方法可用存取控制矩阵，它是一个二维矩阵，一维列出计算机的全部用户，另一维列出系统中的全部文件，矩阵中每个元素Aij
是表示第i个用户对第j个文件的存取权限。通常存取权限有可读、可写、可执行以及它们的组合，如表3-5所示。

表3-5　存取控制矩阵

续表

存取控制矩阵在概念上是简单清楚的，但实现上却有困难。当一个系统用户数和文件数很大时，二维矩阵要占很大的存储空间，验证过程也将耗费许多系统时间。

（2）存取控制表。存取控制矩阵由于太大而往往无法实现。一个改进的办法是按用户对文件的访问权力的差别对用户进行分类，由于某一文件往往只与少数几个用户有关，所以这种分类方法可使存取控制表大为简化。UNIX系统就是使用这种存取控制表方法。它把用户分成三类：文件主、同组用户和其他用户，每类用户的存取权限为可读、可写、可执行以及它们的组合。在用ls长列表显示时，每组存取权限用三个字母RWX表示，如读、写和执行中哪一样存取不允许，则用“-”字符表示。用ls-l长列表显示ls文件如下：

显示前2～10共9个字符表示文件的存取权限，每3个字符为一组，分别表示文件主、同组用户和其他用户的存取权限。由于存取控制表对每个文件按用户分类，所以该存取控制表可存放在每个文件的文件控制块中，对UNIX只需9位二进制来表示三类用户对文件的存取权限，该权限存在文件索引节点的di_mode中。

（3）用户权限表。改进存取控制矩阵的另一种方法是以用户或用户组为单位将用户可存取的文件集中起来存入表中，这称为用户权限表。表中每个表目表示该用户对应文件的存取权限，这相当于存取控制矩阵一行的简化。

（4）密码。在创建文件时，由用户提供一个密码，在文件存入磁盘时用该密码对文件内容加密。进行读取操作时，要对文件进行解密，只有知道密码的用户才能读取文件。

3.5.7　系统的安全与可靠性

1．系统的安全

系统的安全涉及两类不同的问题，一类涉及到技术、管理、法律、道德和政治等问题，另一类涉及操作系统的安全机制。随着计算机应用范围扩大，在所有稍具规模的系统中，都从多个级别上来保证系统的安全性。一般从4个级别上对文件进行安全性管理：系统级、用户级、目录级和文件级。

系统级安全管理的主要任务是不允许未经授权的用户进入系统，从而也防止了他人非法使用系统中各类资源（包括文件）。系统级管理的主要措施有注册与登录。

用户级安全管理是通过对所有用户分类和对指定用户分配访问权，不同的用户对不同文件设置不同的存取权限来实现。例如，在UNIX系统中将用户分为文件主、组用户和其他用户。有的系统将用户分为超级用户、系统操作员和一般用户。

目录级安全管理是为了保护系统中各种目录而设计的，它与用户权限无关。为保证目录的安全，规定只有系统核心才具有写目录的权利。

文件级安全管理是通过系统管理员或文件主对文件属性的设置来控制用户对文件的访问。通常可设置以下几种属性：只执行、隐含、只读、读／写、共享、系统。用户对文件的访问，将由用户访问权、目录访问权限及文件属性三者的权限所确定，或者说是有效权限和文件属性的交集。例如对于只读文件，尽管用户的有效权限是读／写，但都不能对只读文件进行修改、更名和删除。对于一个非共享文件，将禁止在同一时间内由多个用户对它们进行访问。

通过上述4级文件保护措施，可有效地对文件进行保护。

2．文件系统的可靠性

文件系统的可靠性是指系统抵抗和预防各种物理性破坏和人为性破坏的能力。比起计算机的损坏，文件系统破坏往往后果更加严重。例如，将开水撒在键盘上引起的故障，尽管伤脑筋但毕竟可以修复；但如果文件系统破坏了，在很多情况下是无法恢复的。特别是对于那些程序文件、客户档案、市场计划或其他数据文件丢失的客户来说，这不亚于一场大的灾难。尽管文件系统无法防止设备和存储介质的物理损坏，但至少应能保护信息。

（1）转储和恢复。文件系统中无论是硬件或软件，都会发生损坏和错误，例如自然界的闪电、电压的突变、火灾和水灾等均可能引起软、硬件的破坏。为了使文件系统万无一失，应当采用相应的措施。最简单和常用的措施是通过转储操作，形成文件或文件系统的多个副本。这样，一旦系统出现故障，利用转储的数据使得系统恢复成为可能。常用的转储方法有静态转储和动态转储、海量转储和增量转储。

（2）日志文件。在计算机系统的工作过程中，操作系统把用户对文件的插入、删除和修改的操作写入日志文件。一旦发生故障，操作系统恢复子系统利用日志文件来进行系统故障恢复，并可协助后备副本进行介质故障恢复。

（3）文件系统的一致性。影响文件系统可靠性的因素之一是文件系统的一致性问题。很多文件系统是先读取磁盘块到主存，在主存进行修改，修改完毕在写回磁盘。但如读取某磁盘块，修改后在将信息写回磁盘前系统崩溃，则文件系统就可能会出现不一致性状态。如果这些未被写回的磁盘块是索引节点块、目录块或空闲块，那么后果是不堪设想的。通常解决方案是采用文件系统的一致性检查，一致性检查包括块的一致性检查和文件的一致性检查。

3.6　作业管理

作业是系统为完成一个用户的计算任务（或一次事务处理）所做的工作总和。例如，对用户编写的源程序，需要经过编译、连接装入以及执行等步骤得到结果，这其中的每一个步骤称为作业步。操作系统中用来控制作业进入、执行和撤销的一组程序称为作业管理程序。操作系统可以进一步为每个作业创建作业步进程，完成用户的工作。

3.6.1　作业与作业控制

1．作业控制

可以采用脱机和联机两种控制方式控制用户作业的运行。在脱机控制方式中，作业运行的过程是无须人工干预的，因此，用户必须将自己想让计算机干什么的意图用作业控制语言（JCL）编写成作业说明书，连同作业一起提交给计算机系统。在联机控制方式中，操作系统向用户提供了一组联机命令，用户可以通过终端输入命令将自己想让计算机干什么的意图告诉计算机，以控制作业的运行过程，因此整个作业的运行过程需要人工干预。

作业由程序、数据和作业说明书三部分组成。作业说明书包括作业基本情况、作业控制、作业资源要求的描述，它体现用户的控制意图。其中，作业基本情况包括用户名、作业名、编程语言和最大处理时间等；作业控制描述包括作业控制方式、作业步的操作顺序、作业执行出错处理；作业资源要求描述包括处理时间、优先级、主存空间、外设类型和数量、实用程序要求等。

2．作业状态及转换

作业状态分为4种：提交、后备、执行和完成。

（1）提交。作业提交给计算机中心，通过输入设备送入计算机系统的过程状态称为提交状态。

（2）后备。通过Spooling系统将作业输入到计算机系统的后备存储器（磁盘）中，随时等待作业调度程序调度时的状态。

（3）执行。一旦作业被作业调度程序选中，为其分配了必要的资源，并为其建立相应的进程后，该作业便进入了执行状态。

（4）完成。当作业正常结束或异常终止时，作业进入完成状态。此时有作业调度程序对该作业进行善后处理。如撤销作业的作业控制块，收回作业所占的系统资源，将作业的执行结果形成输出文件放到输出井中，由Spooling系统控制输出。

作业的状态及转换如图3-28所示。

图3-28　作业的状态及其转换

3．作业控制块和作业后备队列

所谓作业控制块（JCB），是记录与该作业有关的各种信息的登记表。JCB是作业存在的唯一标志，包括用户名、作业名和状态标志等信息。

由于在输入井中有较多的后备作业，为了便于作业调度程序调度，通常将作业控制块排成一个或多个队列，而这些队列称为作业后备队列。也就是说，作业后备队列是由若干个JCB组成的。

3.6.2　作业调度

选择调度算法需要考虑的因素：与系统的整个设计目标一致，均衡使用系统资源，以及平衡系统和用户的要求。对用户来说，作业能“立即执行”往往难以做到，但是应保证进入系统的作业在规定的截止时间内完成，而且系统应设法缩短作业的平均周转时间。

1．作业调度算法

常用的作业调度算法如下。

（1）先来先服务。按作业到达先后进行调度，即启动等待时间最长的作业。

（2）短作业优先。以要求运行时间长短进行调度，即启动要求运行时间最短的作业。

（3）响应比高优先。响应比高的作业优先启动。

定义响应比如下：

其中作业响应时间为作业进入系统后的等候时间与作业的执行时间之和，即

响应比高者优先算法，在每次调度前都要计算所有被选作业（在作业后备队列中）的响应比，然后选择响应比最高的作业执行。该算法比较复杂，系统开销大。

（4）优先级调度算法。可由用户指定作业优先级，优先级高的作业先启动。也可由系统根据作业要求的紧迫程度，或者照顾“I/O繁忙”的作业，以便充分发挥外设的效率等。

（5）均衡调度算法。这种算法的基本思想是根据系统的运行情况和作业本身的特性对作业进行分类。作业调度程序轮流地从这些不同类别的作业中挑选作业执行。这种算法力求均衡地使用系统的各种资源，即注意发挥系统效率，又使用户满意。

2．作业调度算法性能的衡量指标

在一个以批量处理为主的系统中，通常用平均周转时间或平均带权周转时间来衡量调度性能的优劣。假设作业Ji
（i=1，2，…，n）的提交时间为tsi
，执行时间为tri
，作业完成时间为toi
，则作业Ji
的周转时间Ti
和带权周转时间Wi
分别定义为：

n个作业的平均周转时间T和平均带权周转时间W分别定义为：

站在用户的角度来说，总是希望自己的作业在提交后能立即执行，这意味着当等待时间为0时作业的周转时间最短，即Ti
＝tri
。但是，作业的执行时间tri
并不能直观地衡量出系统的性能，而带权周转时间Wi
却能直观反应系统的调度性能。站在整个系统的角度来说，不可能满足每个用户的这种要求，而只能是系统的平均周转时间或平均带权周转时间最小。

3.6.3　用户界面

用户界面（user interface）是计算机中实现用户与计算机通信的软件、硬件部分总称。用户界面也称用户接口，或人机界面。

用户界面的硬件部分包括用户向计算机输入数据或命令的输入装置及由计算机输出供用户观察或处理的输出装置。用户界面的软件部分包括用户与计算机相互通信的协议、约定、操纵命令及其处理软件。目前常用的输入输出装置有键盘、鼠标、显示器和打印机等。常用的人机通信方法有命令语言、选项、表格填充及直接操纵等。从计算机用户界面的发展过程来看，可分为如下阶段。

（1）控制面板式用户界面。这是计算机发展早期，用户通过控制台开关、板键或穿孔纸带向计算机送入命令或数据，而计算机通过指示灯及打印机输出运行情况或结果。这种界面的特点是人去适应现在看来十分笨拙的计算机。

（2）字符用户界面。字符用户界面是基于字符型的。用户通过键盘或其他输入设备输入字符，由显示器或打印机输出字符。字符用户界面的优点是功能强、灵活性好、屏幕开销少；缺点是操作步骤繁琐，学会操作也较费时。

（3）图形用户界面。随着文字、图形、声音和图像等多媒体技术的出现，使各种图形用户界面应运而生，用户既可使用传统的字符，也可使用图形、图像和声音同计算机进行交互，操作将更为自然，更加方便。现代界面的关键技术是超文本。超文本的“超”体现在它不仅包括文本，还包括图像、音频和视频等多媒体信息，即将文本的概念扩充到超文本，超文本的最大特点是具有指向性。

（4）新一代用户界面。虚拟现实技术将用户界面发展推向一个新阶段：人将作为参与者，以自然的方式与计算机生成的虚拟环境进行通信。以用户为中心、自然、高效、高带宽、非精确、无地点限制等是新一代用户界面的特征。多媒体、多通道及智能化是新一代用户界面的技术支持。语音、自然语言、手势、头部跟踪、表情和视线跟踪等新的、更加自然的交互技术，将为用户提供更方便的输入技术。计算机将通过多种感知通道来理解用户的意图，实现用户的要求。计算机不仅以二维屏幕向用户输出，而且以真实感（立体视觉、听觉、嗅觉和触觉等）的计算机仿真环境向用户提供真实的体验。

3.7　网络与嵌入式操作系统基础知识

3.7.1　网络操作系统

计算机网络系统除了硬件外，还需要有系统软件，二者结合构成计算机网络的基础平台。操作系统是最重要的系统软件。网络操作系统是网络用户和计算机网络之间的一个接口，它除了应具备通常操作系统应具备的基本功能外，还应有联网功能，支持网络体系结构和各种网络通信协议，提供网络互连功能，支持有效、可靠安全的数据传送。

一个典型的网络操作系统的特征包括硬件独立性，网络操作系统可以运行在不同的网络硬件上，可以通过网桥或路由器与别的网络连接；多用户支持，应能同时支持多个用户对网络的访问，应对信息资源提供完全的安全和保护功能；支持网络实用程序及其管理功能，如系统备份、安全管理、容错和性能控制；多种客户端支持，如Windows NT网络操作系统包括OS/2、Windows 98和UNIX等多种客户端，极大地方便了网络用户；提供目录服务，以单一逻辑的方式让用户访问位于世界范围内的所有网络服务和资源的技术；支持多种增值服务，如文件服务、打印服务、通信服务和数据库服务等。

网络操作系统可分为如下三类。

（1）集中模式。集中式网络操作系统是由分时操作系统加上网络功能演变而来的，系统的基本单元是由一台主机和若干台与主机相连的终端构成，将多台主机连接起来形成了网络，信息的处理和控制是集中的。UNIX就是这类系统的典型例子。

（2）客户端／服务器模式。这是流行的网络工作模式。该种模式网络可分为服务器和客户端。服务器是网络的控制中心，其任务是向客户端提供一种或多种服务，服务器可有多种类型，如提供文件／打印服务的文件服务器等。客户端是用于本地处理和访问服务器的站点，在客户端中包含了本地处理软件和访问服务器上服务程序的软件接口。

（3）对等模式（peer-to-peer）模式。采用这种模式的操作系统网络中，各个站点是对等的。它既可作为客户端去访问其他站点，又可作为服务器向其他站点提供服务，在网络中既无服务处理中心，也无控制中心，或者说，网络的服务和控制功能分布在各个站点上。可见，该模式具有分布处理及分布控制的特征。

现代操作系统已把网络功能包含到操作系统的内核中，作为操作系统核心功能的一个组成部分。微软公司的Windows NT、AT & T公司的UNIX System V、Sun公司的SunOS、HP公司的HP/OX、IBM公司的AIX和Linux等都已把TCP/IP网络功能包含在内核中。

网络操作系统是整个网络的灵魂，它决定了网络的功能，并由此决定了不同网络的应用领域及方向。目前，网络操作系统主要有三大阵营：UNIX、Windows NT和NetWare。各种网络操作系统具有不同的特点，随着网络技术的发展，新的网络操作系统还会不断出现，用户可根据自己的需要进行选择，而不要仅局限于其技术水平的高低。

3.7.2　嵌入式操作系统

在嵌入式系统中的操作系统，称为嵌入式操作系统。嵌入式操作系统是运行在嵌入式智能芯片环境中，对整个智能芯片以及它所操作、控制的各种部件装置等资源进行统一协调、调度、指挥和控制的系统软件。嵌入式系统广泛应用于各种工业控制系统、计算机外设、微波炉、洗衣机和冰箱等低端设备；也用在信息化家电、掌上电脑、机顶盒、WAP手机和路由器等高端设备中。

1．嵌入式操作系统的特点

一般而言，嵌入式操作系统不同于一般意义的计算机操作系统，它有占用空间小、执行效率高、方便进行个性化定制和软件要求固化存储等特点。嵌入式操作系统和其他嵌入式软件都具有如下特点。

（1）微型化。由于硬件平台的局限性，如主存少、字长短、运行速度有限、能源少（用微小型电池）、外部设备和控制对象千变万化，因此，不论从性能还是从成本角度考虑，都不允许它占用很多资源，系统代码量少，应保证应用功能的前提下，以微型化作为特点来设计嵌入式操作系统的结构与功能。

（2）可定制。嵌入式操作系统的运行平台多种多样，应用更是五花八门，所以表现出专业化的特点。从减少成本和缩短研发周期考虑，要求它能运行在不同的微处理器平台上，能针对硬件变化进行结构与功能上的配置，以满足不同应用需要。

（3）实时性。嵌入式操作系统广泛应用于过程控制、数据采集、传输通信、多媒体信息及关键要害领域需要迅速响应的场合，实时响应要求严格，因此实时性是其主要特点之一。

（4）可靠性。系统构件、模块和体系结构必须达到应有的可靠性，对关键要害应用还要提供容错和防故障措施，以进一步提高可靠性。

（5）易移植性。为了提高系统的易移植性，通常采用硬件抽象层（Hardware Abstraction Level, HAL）和板级支撑包（Board Support Package, BSP）的底层设计技术。HAL提供了与设备无关的特性，屏蔽硬件平台的细节和差异，向操作系统上层提供统一接口，保证了系统的可移植性。而一般由硬件厂家提供，按给定的编程规范完成BSP，保证了嵌入式操作系统可在新推出的微处理器件平台上运行。

2．嵌入式系统开发环境

嵌入式系统开发环境通常配有源码级可配置的系统模块设计，丰富的同步原语，可选择的调度算法，可选择主存分配策略，定时器与计数器，多方式中断处理支持，多种异常处理选择，多种通信方式支持，标准C语言库，数学运算库和开放式应用程序接口。较著名的嵌入式操作系统有Windows CE、VxWorks、pSOS、Palm OS和μC/OS-Ⅱ。

3.8　UNIX操作系统实例

3.8.1　UNIX操作系统

UNIX操作系统是由美国贝尔实验室发明的一种多用户、多任务的分时操作系统。现已发展成为当前使用普遍、影响深远的工业界主流的操作系统，成为重要的企业级操作平台。UNIX广泛运行于PC、小型机等各种环境，用于大型信息系统的关键业务服务，如数据库和Internt主机。UNIX结构如图3-29所示。UNIX最内层硬件提供基本服务，内核提供全部应用程序所需的各种服务。

3.8.2　UNIX文件系统

UNIX文件系统的目录结构是树型带交叉勾连的，根目录记为“/”，非叶节点为目录文件，叶节点可以是目录文件，也可以是文件或特殊文件。目录是一个包含目录项的文件，在逻辑上，可以认为每个目录项都包含一个文件名，同时还包含说明该文件属性的信息。文件属性包括文件类型、文件长度、文件主、文件的许可权（例如，其他用户能否访问该文件）和文件最后的修改时间等。当创建一个新目录时，系统自动创建了两个文件名：“.（称为点）”和“..（称为点-点）”。点表示当前目录，点-点表示父目录。在最高层次的根目录中，点-点与点相同。某些UNIX文件系统限制文件名的最大长度为14个字符，BSD版本则将这种限制扩展为255个字符。

图3-29　UNIX系统结构

UNIX文件系统具有图3-30所示的结构。

图3-30　文件系统的布局

其中各参数的含义如下。

·　引导块：占据文件系统的开头，占一个物理块，包含引导代码段。

·　超级块：描述文件系统的状态，如容量、空闲块号和空闲索引节点号（i_node）等。

·　索引节点区：第一个索引节点是文件系统的根索引节点，当执行了mount命令之后，该文件系统的目录结构就可以从这个根索引节点开始进行存取了。

·　数据存储区：存放数据的区域。

进程可以通过系统调用访问文件，如Open（打开文件）、Close（关闭文件）、Write（写文件）、Read（读文件）、Stat（查询文件属性）、Chmod（改变文件的许可权）、Chown（改变文件所有者）、Creat（创建一个文件）、Mkdir（创建一个目录文件）、Cd（改变当前目录）、Link（建立连接）和Unlink（删除文件连接）等。

3.8.3　UNIX进程与存储管理

1．进程管理

在UNIX中，进程由控制块（PCB）、正文段和数据段组成。其中，进程控制块由常驻主存的基本进程控制块（proc）和非常驻主存的进程扩充控制块（user）构成；正文段是可供多个进程使用，为了管理可共享的正文段，UNIX设置了一张正文表text[]，每个正文段都占据一个表项，用来指明正文段在主存和磁盘的位置；数据段是进程执行时用到的数据段，若进程执行的程序是非共享的，则也构成数据段的一部分。

1）进程控制

在UNIX中的进程控制子系统负责进程同步、进程间通信、存储管理及进程调度。控制进程的系统调用有Fork，创建一个子进程；exec，改变执行程序的映像；exit，结束一个进程的执行；wait，暂停进程的执行，用于进程之间的同步，例如父进程等待子进程执行结束；signal，控制进程对特别事件的响应；kill，发送软中断信号；msgsnd，发送消息；msgrcv，接受消息等。

2）进程调度

UNIX系统对进程的调度采用动态优先数调度算法，进程的优先数随进程的执行情况而变化。就绪进程是否能占用处理机的优先权取决于进程的优先数，优先数越小优先权越大。UNIX系统中优先数确定方法有两种：设置方法和计算方法。设置方法用于要进入睡眠的进程，当进程正在或即将转入用户态运行时，用计算方法确定优先数。UNIX计算优先数的公式为：

其中，p-pri表示进程的优先数；p-cpu为处理器的占用时间；PUSER和NZERO表示偏置常数；p-nice允许用户使用shell命令nice或系统调用nice修改，用户有权将其值设置为0～39之间的数。

2．存储管理

UNIX早期的版本采用“对换技术”扩充主存容量，进程可以被换出到对换区，也可以从对换区换进到主存。高版本的UNIX主存管理采用分页式虚拟存储机制，对换技术作为一种辅助手段。采用二次机会页面替换算法。

3.8.4　UNIX设备管理

1．设备管理

在UNIX系统中，文件等于系统中可用的任何资源。UNIX的设计者们遵循一条这样的规则：UNIX系统中可以使用的任何计算机资源都用一种统一的方法表示。他们选择用“文件”这个概念作为一切资源的抽象表示方法。

UNIX系统包括两类设备：块设备和字符设备。用户可以通过文件系统与设备接口，因为每个设备有一个文件名，可以像文件那样存取。设备文件有一个索引节点，在文件系统目录中占据一个节点，但其索引节点上的文件类型与其他文件不同，是“块”或者是“字符”特殊文件。文件系统与设备驱动程序的接口是通过设备开关表。硬件与驱动程序之间的接口控制寄存器、I/O指令，一旦出现设备中断，根据中断矢量转相应的中断处理程序，完成用户所要求的I/O任务。UNIX设备管理的主要特点如下。

（1）块设备与字符设备具有相似的层次结构。这是指对它们的控制方法和所采用的数据结构、层次结构几乎相同。

（2）将设备作为一个特殊文件，并赋予一个文件名。这样，对设备的使用类似于对文件的存取，具有统一的接口。

（3）采用完善的缓冲区管理技术。引入“预先读”、“异步写”和“延迟写”方式，进一步提高系统效率。

2．输入输出转向

shell即是一种命令语言，又是一种程序设计语言。在UNIX中，任何一个存放一条或多条命令的文件称为shell程序或shell过程。

UNIX系统的shell向用户提供了输入输出转向命令，可以在不改变应用程序本身的情况下自由地改变其数据的输入源和输出目的地。其中，“>”、“>>”表示输出转向，“<”表示输入转向。例如，cat命令用来将输入文件的数据显示在屏幕上：

上述cat命令将input.txt文件中的内容输出到屏幕（标准输出设备）上。但是，如果将命令写成：

那么cat命令就会将原本输出到屏幕上的内容输入到文件output.txt中去并覆盖output.txt的内容。如果使用cat input.txt>>output.txt，则将input.txt文件的内容添加到output.txt文件的末尾。

如果希望使用cat命令时没有指定输入文件，cat就会要求用户在键盘（标准输入设备）上输入数据，直到用户输入Ctrl＋D为止。但是，如果使用输入重定向符，就可以写成：

这样，cat就不再从键盘上而是从input.txt文件中读取内容了。该条命令与cat input.txt在效果上是等价的，但它们代表的意义不同。没有使用重定向符的cat命令知道自己是在从文件中读取数据；而使用了重定向符的cat命令实际上并不知道自己得到的数据来自input.txt，它只知道自己是从标准输入设备上读入信息的，shell通过重定向耍了个花招，“骗”过了cat命令，使得它接收到的数据被调了包。输出重定向也一样，cat命令在输出时也是只知道自己是向标准输出设备输出，而shell也同样用output.txt文件掉换了cat命令的标准输出设备，使得cat命令的输出转到了文件当中。

3．管道

在UNIX中“｜”表示管道。一个管道总是连接两个命令，将左边命令的标准输出与右边命令的标准输入相连，于是左边命令的输出结果就直接成了右边命令的输入。这个功能使得用户可以在不改动程序本身的前提下使多个程序通过标准输入输出设备进行数据传递。利用管道命令可以简化命令行的写法，例如下面的三条命令：

可以用管道命令表示为：

例如，如果要统计当前目录中所有文件和目录中的数目，并将其记录在文件output.txt中。利用管道命令如下：

其中，ls -a用来列出当前目录下的所有文件和目录；wc -l负责统计ls输出中的行数；最后将输出重定向到output.txt中。命令十分简单，完成的工作却相当复杂。

3.8.5　shell程序

shell不但负责管理命令行界面，而且shell自己也是一个编程的环境。实际上，可以将命令按照命令行的格式写入一个文件，再将其权限设置为可执行，就可以像普通命令一样执行它了。这个文件通常称为脚本（script）。熟悉DOS的用户自然想到shell脚本相当于DOS的批处理文件，而且shell脚本中也同样支持如if、for和case等程序控制流程，甚至还支持变量和函数定义。shell实际上是一种编程语言。利用shell语言可以编写出功能很强的shell程序，将程序段组合起来。

1．正则表达式

在UNIX中，正则表达式不仅用在vi中，还用在shell中。正则表达式用来确定字符串模式的一个规则集，是对文本字符串的一种描述，该描述能简洁而又完整地刻画文本字符串的关键特性。因此，正则表达式通常被用作字符串的匹配操作。正则表达式符号如表3-6所示。

表3-6　正则表达式符号

【例3.17】　匹配字符串32787、188567、1234567890的正则表达式。

解：这些字符串的共同特性都是数字，若要匹配任意长度的数字序列可用如下正则表达式：

而不能使用[0-9]*。

【例3.18】　分别写出字符串what出现在行首和行尾的正则表达式。

解：/^what　当what出现在行首时才会被找到

/what$　当what出现在行尾时才会被找到

若匹配文件的任意一行，应用^.*$。

2．shell变量

shell变量可分为三种类型：用户定义变量、系统定义变量和shell定义变量。

（1）用户定义变量。用户定义变量必须以字母或下划线开始，可以包含字母、下划线和数字的字符序列。用户定义的shell变量能用赋值语句置初值或重置值。例如ux＝UNIX。

（2）系统定义变量。系统定义变量如表3-7所示。

表3-7　常用的系统定义变量

（3）shell定义变量。shell定义变量如表3-8所示。

表3-8　shell定义变量

3．shell程序

shell向用户提供了许多用于简化输入的符号，这些符号包括各种通配符、字符串定义符、转义符和变量定义符等。这些符号可以被看做是shell的保留字，通常称为“元字符”。元字符的种类和作用非常多，它们无论在shell的命令行输入还是在shell程序设计中都起着非常重要的作用。

【例3.19】　编写显示用户登录名、用户主目录以及当前命令的进程标识符的shell程序。

解：Shell程序如下。

shell命令行本身也是一个交互式的脚本执行环境，也就是说，在命令行上同样可以使用脚本中的控制语句，也可以定义变量（实际上就是环境变量），甚至可以定义函数。这都与脚本文件中的命令一样。但是有一点必须注意，shell程序有许多种，不同的shell有不同的编程命令和语法。虽然它们基本上大同小异，但还是有许多差别。

【例3.20】　在UNIX操作系统中，若用户输入的命令参数的个数为1时，执行cat$1命令；若用户输入的命令参数的个数为2时，执行。at>>$2<$1命令。将下面所示的shell程序的空缺部分补齐。

分析：空（1）处应填写$#。因为在UNIX操作系统中，shell定义变量$$、$＠、$#和$*的含义如下。

·　$$：表示当前命令的进程标识数。

·　$＠：与$*基本相同，但当用双引号转义时，"$＠"还是能分解成多个参数，而"$*"则合并成一个参数。

·　$#：表示位置参数的个数，不包括命令名。

·　$*：表示所有位置参量，即相当于$1，$2，$3，...。

【例3.21】　将下面shell程序段中的空缺部分补齐，使得它可以将指定的一个或多个输入文件的内容依次添加到输出文件的末尾。如果指定的输出文件不存在，则程序应自动产生一个输出文件。

（1）A．$#

B．$i

C．$!

D．$＠

（2）A．"$i">$output

B．"$i">>$output

C．$i>$output

D．$i>>$output

分析：程序的功能是指定一个或多个输入文件，将它们的内容依次添加到输出文件的末尾。如果指定的输出文件不存在，则程序应自动产生一个。本题命令行的格式应为：

第一个if条件语句的条件段"$#"-lt2的含义是位置参量的个数小于2，则显示提示信息并退出。

output＝"$1"语句的含义是将第一个参数作为输出文件。shift语句的含义是将位置参量左移。空（1）应填$＠，表示所有位置参量，即相当于$1，$2，$3…。

由于试题要求将它们的内容依次添加到输出文件的末尾。对于UNIX系统的shell向用户提供了输入输出转向命令，可以在不改变应用程序本身的情况下自由地改变其数据的输入源和输出目的地。其中，“>”、“>>”表示输出转向，“<”表示输入转向。例如，cat命令用来将输入文件的数据显示在屏幕上。

上述cat命令将input.txt文件中的内容输出到屏幕（标准输出设备）上。但是，如果将命令写成：

那么cat命令就会将原本输出到屏幕上的内容输入到文件output.txt中去并覆盖output.txt的内容。

如果使用cat input.txt>>output.txt，则将input.txt文件的内容添加到output.txt文件的末尾。可见，空（2）应填"$i">>$output。

第4章　系统开发和运行知识

本章的主要内容包括软件工程基础知识、软件项目管理基础知识、软件过程改进、系统分析与设计基础知识，以及系统运行维护和系统评价基础知识等。

在系统分析与系统设计知识部分，主要以结构化分析设计方法为主，面向对象分析与设计方法参见本书第10章。

4.1　软件工程基础知识

自从1968年首次提出“软件工程”这个名词以来，软件工程已经成为计算机软件的一个重要分支和研究方向。软件工程是指应用计算机科学、数学及管理科学等原理，以工程化的原则和方法来解决软件问题的工程，其目的是提高软件生产率、提高软件质量、降低软件成本。

软件工程涉及到软件开发、维护、管理等多方面的原理、方法、工具与环境，限于篇幅，本章不能对软件工程做全面的介绍。根据软件设计师级考试大纲的要求，本章着重介绍软件开发过程中的原理，其他内容只作简单的介绍。

4.1.1　软件工程概述

早期的软件主要指程序，程序的开发采用个体工作方式，开发工作主要依赖于开发人员的个人技能和程序设计技巧。当时的软件通常缺少与程序有关的文档，软件开发的实际成本和进度往往与预计的相差甚远，软件的质量得不到保证，开发出来的软件常常不能使用户满意。随着计算机应用的需求不断增长，软件的规模也越来越大，然而软件开发的生产率远远跟不上计算机应用的迅速增长。此外，由于软件开发时缺少好的方法指导和工具辅助，同时又缺少相关文档，使得大量已有的软件难以维护。上述这些问题严重地阻碍了软件的发展，20世纪60年代中期，人们把上述软件开发和维护过程中所遇到的各种问题称为“软件危机”。

1968年，在德国召开的NATO（North Atlantic Treaty Organization，北大西洋公约组织）会议上，首次提出了“软件工程”这个名词，希望用工程化的原则和方法来克服软件危机。在此以后，人们开展了软件开发模型、开发方法、工具与环境的研究，提出了瀑布模型、演化模型、螺旋模型和喷泉模型等开发模型，出现了面向数据流方法、面向数据结构的方法、面向对象方法等开发方法，以及一批CASE（Computer Aided Software Engineering，计算机辅助的软件工程）工具和环境。

1．软件生存周期

同任何事物一样，一个软件产品或软件系统也要经历孕育、诞生、成长、成熟、衰亡的许多阶段，一般称为软件生存周期。把整个软件生存周期划分为若干阶段，使得每个阶段有明确的任务，使规模大、结构复杂和管理复杂的软件开发变得容易控制和管理。通常，软件生存周期包括可行性分析与项目开发计划、需求分析、设计（概要设计和详细设计）、编码、测试、维护等活动，可以将这些活动以适当的方式分配到不同的阶段去完成。

1）可行性分析与项目开发计划

这个阶段主要确定软件的开发目标及其可行性。必须要回答的问题是：要解决的问题是什么？该问题有可行的解决办法吗？若有解决的办法，则需要多少费用？需要多少资源？需要多少时间？要回答这些问题，就要进行问题定义、可行性分析，制订项目开发计划。

可行性分析与项目计划阶段的参加人员有用户、项目负责人和系统分析师。该阶段所产生的文档有可行性分析报告和项目开发计划。

2）需求分析

需求分析阶段的任务不是具体地解决问题，而是准确地确定软件系统必须做什么，确定软件系统的功能、性能、数据和界面等要求，从而确定系统的逻辑模型。该阶段的参加人员有用户、项目负责人和系统分析师。产生的文档有软件需求说明书。

3）概要设计

在概要设计阶段，开发人员要把确定的各项功能需求转换成需要的体系结构。在该体系结构中，每个成分都是意义明确的模块，即每个模块都和某些功能需求相对应，因此，概要设计就是设计软件的结构，明确软件由哪些模块组成，这些模块的层次结构是怎样的，这些模块的调用关系是怎样的，每个模块的功能是什么。同时，还要设计该项目的应用系统的总体数据结构和数据库结构，即应用系统要存储什么数据，这些数据是什么样的结构，它们之间有什么关系。

概要设计阶段参加的人员有系统分析师和软件设计师。该阶段的主要文档有概要设计说明书。

4）详细设计

详细设计阶段的主要任务就是对每个模块完成的功能进行具体描述，要把功能描述转变为精确的、结构化的过程描述。即该模块的控制结构是怎样的，先做什么，后做什么，有什么样的条件判定，有些什么重复处理等，并用相应的表示工具把这些控制结构表示出来。

详细设计阶段参加的人员有软件设计师和程序员。该阶段的主要文档有详细设计文档。

5）编码

编码阶段就是把每个模块的控制结构转换成计算机可接受的程序代码，即写成某种特定程序设计语言表示的源程序清单。

6）测试

测试是保证软件质量的重要手段，其主要方式是在设计测试用例的基础上检查软件的各个组成部分。测试阶段的参加人员通常由另一部门（或单位）的软件设计师或系统分析师承担。该阶段产生的文档有软件测试计划、测试用例和软件测试报告。

7）维护

软件维护是软件生存周期中时间最长的阶段。已交付的软件投入正式使用后，便进入软件维护阶段，它可以持续几年甚至几十年。软件运行过程中可能由于各方面的原因，需要对它进行修改。其原因可能是运行中发现了软件隐含的错误而需要修改；也可能是为了适应变化了的软件工作环境而需要做适当变更；也可能是因为用户业务发生变化而需要扩充和增强软件的功能；还可能是为将来的软件维护活动预先做准备等。

2．软件生存周期模型

软件生存周期模型是描述软件开发过程中各种活动如何执行的模型。软件生存周期模型确立了软件开发和演绎中各阶段的次序限制以及各阶段或机动的准则，确立开发过程所遵守的规定和限制，便于各种活动的协调，便于各种人员的有效通信，有利于活动重用，有利于活动管理。常见的软件生存周期模型有瀑布模型、演化模型、螺旋模型和喷泉模型等。

1）瀑布模型（waterfall model）

瀑布模型是将软件生存周期各个活动规定为依线性顺序连接的若干阶段的模型。它包括可行性分析、项目开发计划、需求分析、概要设计、详细设计、编码、测试和维护。它规定了由前至后、相互衔接的固定次序，如同瀑布流水，逐级下落。

瀑布模型为软件的开发和维护提供了一种有效的管理模式，根据这一模式制定开发计划，进行成本预算，组织开发力量，以项目的阶段评审和文档控制为手段有效地对整个开发过程进行指导，所以它是以文档作为驱动、适合于软件需求很明确的软件项目的模型。

但是，瀑布模型在大量的软件开发实践中也逐渐暴露出它的严重缺点。它是一种理想的线性开发模式，缺乏灵活性，特别是无法解决软件需求不明确或不准确的问题。

2）演化模型（evolutionary model）

大量的软件开发实践表明，许多开发项目在开始时对软件需求的认识是模糊的，因此很难一次开发成功。为了减少因对软件需求的了解不够确切而给开发工作带来的风险，可以在获取了一组基本的需求后，通过快速分析构造出该软件的一个初始可运行版本，这个初始的软件通常称为原型（Prototype），然后根据用户在使用原型的过程中提出的意见和建议对原型进行改进，获得原型的新版本。重复这一过程，最终可得到令用户满意的软件产品。采用演化模型的开发过程，实际上就是从初始的原型逐步演化成最终软件产品的过程。演化模型特别适用于对软件需求缺乏准确认识的情况。

3）螺旋模型（spiral model）

对于复杂的大型软件，开发一个原型往往达不到要求。螺旋模型将瀑布模型和演化模型结合起来，加入了两种模型均忽略的风险分析，弥补了这两种模型的不足。

螺旋模型将开发过程分为几个螺旋周期，每个螺旋周期大致和瀑布模型相符合。在每个螺旋周期分为如下4个工作步。

（1）制定计划。确定软件的目标，选定实施方案，明确项目开发的限制条件。

（2）风险分析。分析所选的方案，识别风险，消除风险。

（3）实施工程。实施软件开发，验证阶段性产品。

（4）用户评估。评价开发工作，提出修正建议，建立下一个周期的开发计划。

4）喷泉模型（water fountain model）

喷泉模型是一种以用户需求为动力，以对象作为驱动的模型，适合于面向对象的开发方法。它克服了瀑布模型不支持软件重用和多项开发活动集成的局限性。喷泉模型使开发过程具有迭代性和无间隙性。迭代意味着模型中的开发活动常常需要重复多次，在迭代过程中不断地完善软件系统。无间隙是指在开发活动（如分析、设计、编码）之间不存在明显的边界，也就是说，它不像瀑布模型那样，需求分析活动结束后才开始设计活动，设计活动结束后才开始编码活动，而是允许各开发活动交叉、迭代地进行。

3．软件开发方法

软件开发方法是一种使用早已定义好的技术集及符号表示习惯来组织软件生产的过程。

1）结构化方法

结构化方法由结构化分析、结构化设计、结构化程序设计构成，它是一种面向数据流的开发方法。结构化分析是根据分解与抽象的原则，按照系统中数据处理的流程，用数据流图来建立系统的功能模型，从而完成需求分析工作。结构化设计是根据模块独立性准则、软件结构优化准则将数据流图转换为软件的体系结构，用软件结构图来建立系统的物理模型，实现系统的概要设计。结构化程序设计是根据结构程序设计原理，将每个模块的功能用相应的标准控制结构表示出来，从而实现详细设计。

结构化方法总的指导思想是自顶向下、逐层分解，它的基本原则是功能的分解与抽象。它是软件工程中最早出现的开发方法，特别适合于数据处理领域的问题，但是不适合解决大规模的、特别复杂的项目，且难以适应需求的变化。

2）Jackson方法

Jackson方法是一种面向数据结构的开发方法。因为一个问题的数据结构与处理该数据结构的控制结构有着惊人的相似之处，该方法就是根据这一思想而形成了最初的JSP（Jackson Structure Programming）方法。首先描述问题的输入、输出数据结构，分析其对应性，然后推出相应的程序结构，从而给出问题的软件过程描述。

JSP方法是以数据结构为驱动的，适合于小规模的项目。当输入数据结构与输出数据结构之间没有对应关系时，难以应用此方法。基于JSP方法的局限性，又发展了JSD（Jackson System Development）方法，它是JSP方法的扩充。

JSD方法是一个完整的系统开发方法。首先建立现实世界的模型，再确定系统的功能需求，对需求的描述特别强调操作之间的时序性。它是以事件作为驱动的，是一种基于进程的开发方法，所以应用于时序特点较强的系统，包括数据处理系统和一些实时控制系统。

3）原型化方法

并非所有的需求都能够预先定义，而且反复修改是不可避免的。当然，能够采用原型化方法是因为开发工具的快速发展，使得可以迅速地开发出一个可以让用户看得见、摸得着的系统框架，这样，对于计算机不是很熟悉的用户就可以根据这个样板提出自己的需求。开发原型化系统首先确定用户需求，开发原始模型，然后征求用户对初始原型的改进意见，并根据意见修改原型。

原型化开发比较适合于用户需求不清、业务理论不确定、需求经常变化的情况。当系统规模不是很大也不太复杂时，采用该方法是比较好的。

4）面向对象开发方法

面向对象开发方法的基本出发点是尽可能按照人类认识世界的方法和思维方法来分析和解决问题。客观世界是由许多具体的事物、事件、概念和规则组成，这些均可被看成对象，面向对象方法正是以对象作为最基本的元素，它也是分析问题、解决问题的核心。

面向对象开发方法包括面向对象分析、面向对象设计和面向对象实现。面向对象开发方法有Booch方法、Coad方法和OMT方法等。为了统一各种面向对象方法的术语、概念和模型，1997年推出了统一建模语言（Unified Modeling Language, UML）。它是面向对象的标准建模语言，通过统一的语义和符号表示，使各种方法的建模过程和表示统一起来，已成为面向对象建模的工业标准。本节对面向对象方法不做详细讨论，详细内容参见本书第10章的面向对象方法。

4.1.2　软件需求分析

需求分析是软件生存周期中相当重要的一个阶段。由于开发人员熟悉计算机但不熟悉应用领域的业务，用户熟悉应用领域的业务但不熟悉计算机，因此对于同一个问题，开发人员和用户之间可能存在认识上的差异。在需求分析阶段，通过开发人员与用户之间的广泛交流，不断澄清一些模糊的概念，最终形成一个完整的、清晰的、一致的需求说明。可以说，需求分析的好坏将直接影响到所开发的软件的成败。

1．需求分析的任务

需求分析主要是确定待开发软件的功能、性能、数据和界面等要求。具体来说，可有以下几点。

（1）确定软件系统的综合要求。

①　系统界面要求。描述软件系统的外部特性，即系统从外部输入哪些数据，系统向外部输出哪些数据。

②　系统的功能要求。列出软件系统必须完成的所有功能。

③　系统的性能要求。如响应时间、吞吐量、处理时间、对主存和外存的限制等。

④　安全性、保密性和可靠性方面的要求。

⑤　系统的运行要求。如对硬件、支撑软件和数据通信接口等的要求。

⑥　异常处理要求。在运行过程中出现异常情况（如临时性或永久性的资源故障，不合法或超出范围的输入数据、非法操作和数组越界等）时应采取的行动以及希望显示的信息。

⑦　将来可能提出的要求。主要是为将来可能的扩充和修改作准备。

（2）分析软件系统的数据要求。包括基本数据元素、数据元素之间的逻辑关系、数据量和峰值等。常用的数据描述手段是实体—关系模型。

（3）导出系统的逻辑模型。在结构化分析方法中可用数据流图来描述；在面向对象分析方法中可用类模型来描述。

（4）修正项目开发计划。在明确了用户的真正需求后，可以更准确地估算软件的成本和进度，从而修正项目开发计划。

（5）如有必要，可开发一个原型系统。对一些需求不够明确的软件，可以先开发一个原型系统，以验证用户的需求。

需要再次强调的是，需求分析阶段主要解决“做什么”的问题，而“怎么做”则由设计阶段来完成。

2．需求的分类

软件需求就是系统必须完成的事以及必须具备的品质。软件需求包括功能需求、非功能需求和设计约束三方面的内容。

（1）功能需求。所开发的软件必须具备什么样的功能。

（2）非功能需求。是指产品必须具备的属性或品质，如可靠性、性能、响应时间、容错性和扩展性等。

（3）设计约束。也称为限制条件、补充规约，这通常是对解决方案的一些约束说明。

3．软件需求分析方法

需求分析方法由对软件的数据域和功能域的系统分析过程及其表示方法组成。它定义了表示系统逻辑视图和物理视图的方式。大多数的需求分析方法是由数据驱动的，也就是说，这些方法提供了一种表示数据域的机制，开发人员根据这种表示，确定软件功能及其他特性，最终建立一个待开发软件的抽象模型，即目标系统的逻辑模型。数据域具有三种属性：数据流、数据内容和数据结构。通常，一种需求分析方法总要利用其中的一种或几种属性。

目前已经出现了许多需求分析方法，每一种分析方法都引入了不同的记号和分析策略，但是它们都具有以下的共性。

（1）支持数据域分析的机制。

（2）功能表示的方法。

（3）接口的定义。

（4）问题分解的机制以及对抽象的支持。

（5）逻辑视图和物理视图。

（6）系统抽象模型。

4．需求工程

需求工程就是包括创建和维护系统需求文档所必须的一切活动的过程，也就是指需求开发和需求管理两个工作。

（1）需求开发。包括需求捕获、需求分析、编写规格说明书和需求验证四个阶段。在这个阶段需要确定产品所期望的用户类型、获取每种用户类型的需求、了解实际用户的任务和目标，以及这些任务所支持的业务需求、分析源于用户的信息、对需求进行优先级分类、将所搜集的需求编写成软件规格说明书和需求分析模型、对需求进行评审等工作。

（2）需求管理。通常包括定义需求基线、处理需求变更和需求跟踪等方面的工作。

4.1.3　软件开发项目管理

软件项目管理的对象是软件项目。为了使软件项目开发获得成功，必须对软件开发项目的工作范围、可能遇到的风险、需要的资源（人、硬／软件）、要实现的任务、经历的里程碑、花费的工作量（成本）以及进度的安排等做到心中有数。而软件项目管理可以提供这些信息。这种管理的范围覆盖了整个软件工程过程，即开始于技术工作开始之前，在软件从概念到实现的过程中持续进行，最后终止于软件工程过程结束。

1．成本估算

由于软件具有可见性差、定量化难等特殊性，因此很难在项目完成前准确地估算出开发软件所需的工作量和费用。通常可以根据以往开发类似软件的经验（也可以是别人的经验）来进行成本估算。也可以将软件项目划分成若干个子系统或按照软件生存周期的各个阶段分别估算其成本，然后汇总出整个软件的成本。此外，还可以使用经验公式和成本估算模型来进行估算。

1）成本估算方法

（1）自顶向下估算方法。估算人员参照以前完成的项目所耗费的总成本（或总工作量），来推算将要开发的软件的总成本（或总工作量），然后把它们按阶段、步骤和工作单元进行分配，这种方法称为自顶向下估算方法。

自顶向下估算方法的主要优点是对系统级工作的重视，所以估算中不会遗漏诸如集成、配置管理之类的系统级事务的成本估算，且估算工作量小、速度快。它的缺点是往往不清楚低级别上的技术性困难问题，而这些困难将会使成本上升。

（2）自底向上估算方法。自底向上估算方法是将待开发的软件细分，分别估算每一个子任务所需要的开发工作量，然后将它们加起来，得到软件的总开发量。这种方法的优点是对每一部分的估算工作交给负责该部分工作的人来做，所以估算较为准确。其缺点是其估算往往缺少各项子任务之间相互联系所需要的工作量和与软件开发有关的系统级工作量，所以估算往往偏低。

（3）差别估算方法。差别估算方法的思想是将待开发项目与一个或多个已完成的类似项目进行比较，找出与某个相类似项目的若干不同之处，并估算每个不同之处对成本的影响，导出待开发项目的总成本。该方法的优点是可以提高估算的准确度，缺点是不容易明确“差别”的界限。

除了以上方法之外，还有专家估算法、类推估算法和算式估算法等。

·　专家估算法。依靠一个或多个专家对要求的项目做出估算，其精确性取决于专家估算项目的定性参数的了解和他们的经验。

·　类推估算法。在自顶向下的方法中，它是将估算项目的总体参数与类似项目进行直接比较得到结果；在自底向上方法中，类推是在两个具有相似条件的工作单元之间进行。

·　算式估算法。专家估算法和类推估算法的缺点在于它们依靠带有一定盲目性和主观性的猜测对项目进行估算。算式估算法则是企图避免主观因素的影响。用于估算的方法有两种基本类型：由理论导出和由经验导出。

2）成本估算模型

常用的软件成本估算模型有Putnam模型和COCOMO模型。

Putnam模型是一种动态多变量模型，它是假设在软件开发的整个生存期中工作量有特定的分布。

结构性成本模型COCOMO是最精确、最易于使用的成本估算模型之一。该模型可以分为如下几种。

（1）基本COCOMO模型。是一个静态单变量模型，它是对整个软件系统进行估算。

（2）中级COCOMO模型。是一个静态多变量模型，它将软件系统模型分为系统和部件两个层次，系统由部件构成，它把软件开发所需人力（成本）看作是程序大小和一系列“成本驱动属性”的函数。

（3）详细COCOMO模型。它将软件系统模型分为系统、子系统和模块三个层次，它除包括中级模型所考虑的因素外，还考虑了在需求分析、软件设计等每一步的成本驱动属性的影响。

2．风险分析

当在软件工程环境中考虑风险时，主要是基于关心未来、关心变化、关心选择这三个概念提出的。在进行软件工程分析时，项目管理人员要进行4种风险评估活动，包括建立表示风险概念的尺度，描述风险引起的后果，估计风险影响的大小，确定风险估计的正确性。

风险分析实际上是4个不同的活动：风险识别，风险预测，风险评估和风险控制。

1）风险识别

风险识别是试图系统化地确定对项目计划（估算、进度、资源分配）的威胁。风险识别的一种方法是建立风险条目检查表。该检查表可以用于识别风险，并使得人们集中来识别下列常见的已知的及可预测的风险。

（1）产品规模。与要建造或要修改的软件的总体规模相关的风险。

（2）商业影响。与管理或市场所加诸的约束相关的风险。

（3）客户特性。与客户的素质以及开发者和客户定期通信的能力相关的风险。

（4）过程定义。与软件过程被定义的程度以及它们被开发组织所遵守的程序相关的风险。

（5）开发环境。与用以构建产品的工具的可用性及质量相关的风险。

（6）构建的技术。与待开发软件的复杂性及系统所包含技术的“新奇性”相关的风险。

（7）人员数目及经验。与参与工作的软件工程师的总体技术水平及项目经验相关的风险。

2）风险预测

风险预测，又称风险估算，它从两个方面评估一个风险：风险发生的可能性或概率；以及如果风险发生了，所产生的后果。通常，项目计划人员与管理人员、技术人员一起进行4种风险预测活动。

（1）建立一个尺度或标准，以反映风险发生的可能性。

（2）描述风险的后果。

（3）估计风险对项目和产品的影响。

（4）标注风险预测的整体精确度，以免产生误解。

3）风险评估

在进行风险评估时，建立了如下形式的三元组：

其中，ri
表示风险，li
表示风险发生的概率，xi
则表示风险产生的影响。

一种对风险评估很有用的技术就是定义风险参照水准。对于大多数软件项目来说，成本、进度和性能就是三种典型的风险参照水准。也就是说，对于成本超支、进度延期、性能降低（或它们的某种组合），有一个表明导致项目终止的水准。

在风险评估过程中，需要执行以下4个步骤。

（1）定义项目的风险参考水平值。

（2）建立每一组（ri
，li
，xi
）与每一个参考水平值之间的关系。

（3）预测一组临界点以定义项目终止区域，该区域由一条曲线或不确定区域所界定。

（4）预测什么样的风险组合会影响参考水平值。

4）风险控制

这一步的所有风险分析活动只有一个目的——辅助项目组建立处理风险的策略。一个有效的策略必须考虑如下三个问题。

（1）风险避免。

（2）风险监控。

（3）风险管理及意外事件计划。

如果软件项目组对于风险采用主动的方法，则避免永远是最好的策略。这可以通过建立一个风险缓解计划来达到。

风险管理策略可以包含在软件项目计划中，或者风险管理步骤也可以组织成一个独立的风险缓解、监控和管理计划（RMMM计划）。RMMM计划将所有风险分析工作文档化，并由项目管理者作为整个项目计划中的一部分来使用。

3．进度管理

进度的合理安排是如期完成软件项目的重要保证，也是合理分配资源的重要依据，因此进度安排是管理工作的一个重要组成部分。软件开发项目的进度安排有如下两种方式。

（1）系统最终交付日期已经确定，软件开发部门必须在规定期限内完成。

（2）系统最终交付日期只确定了大致的年限，最后交付日期由软件开发部门确定。

进度安排的常用图形描述方法有Gantt图（甘特图）和项目计划评审技术（Program Evaluation & Review Technique, PERT）图。

1）Gantt图

Gantt图是一种简单的水平条形图，它以日历为基准描述项目任务。水平轴表示日历时间线（如时、天、周、月和年等），每个条形表示一个任务，任务名称垂直地列在左边的列中，图中水平条的起点和终点对应水平轴上的时间，分别表示该任务的开始时间和结束时间，水平条的长度表示完成该任务所持续的时间。当日历中同一时段存在多个水平条时，表示任务之间的并发。图4-1所示的Gantt图描述了三个任务的进度安排。任务1首先开始，完成它需要6个月时间；任务2在1个月后开始，完成它需要9个月时间；任务3在6个月后开始，完成它需要5个月时间。

图4-1　Gantt图实例

Gantt图能清晰地描述每个任务从何时开始，到何时结束，任务的进展情况以及各个任务之间的并行性。但是它不能清晰地反映出各任务之间的依赖关系，难以确定整个项目的关键所在，也不能反映计划中有潜力的部分。

2）PERT图

PERT图是一个有向图，图中的箭头表示任务，它可以标上完成该任务所需的时间；图中的节点表示流入节点的任务的结束，并开始流出节点的任务，这里把节点称为事件。只有当流入该节点的所有任务都结束时，节点所表示的事件才出现，流出节点的任务才可以开始。事件本身不消耗时间和资源，它仅表示某个时间点。一个事件有一个事件号和出现该事件的最早时刻和最迟时刻。最早时刻表示在此时刻之前从该事件出发的任务不可能开始；最迟时刻表示从该事件出发的任务必须在此时刻之前开始，否则整个工程就不能如期完成。每个任务还可以有一个松弛时间（slack time），表示在不影响整个工期的前提下，完成该任务有多少机动余地。为了表示任务间的关系，图中还可以加入一些空任务（用虚线箭头表示），完成空任务的时间为0。

图4-2是PERT图的一个实例。不难看出，图4-2中的松弛时间为0的这些任务是完成整个工程的关键路径，其事件流为1→2→3→4→6→8→10→11。

PERT图不仅给出了每个任务的开始时间、结束时间和完成该任务所需的时间，还给出了任务之间的关系，即哪些任务完成后才能开始另外一些任务，以及如期完成整个工程的关键路径。图中的松弛时间则反映了完成某些任务时可以推迟其开始时间或延长其所需完成的时间。但是，PERT图不能反映任务之间的并行关系。

图4-2　PERT图实例

4．人员管理

合理地组织好参加软件项目的人员，有利于发挥每个人的作用，有利于软件项目的成功开发。在人员组织时，应考虑软件项目的特点、软件人员的素质等多方面的因素。

可以按软件项目对软件人员分组，如需求分析组、设计组、编码组、测试组和维护组等，为了控制软件的质量，还可以有质量保证组。

程序设计小组的组织形式也可以有多种，如主程序员组、无主程序员组和层次式程序员组等。

（1）主程序员组。主程序员组由一名主程序员、一名后备程序员（back up programmer）、一名资料员和若干名程序员组成。主程序员由经验丰富、能力强的高级程序员担任，他是该组织的技术领导和项目负责人，全面负责软件项目的开发。后备程序员是主程序员的助手，协助主程序员工作，必要时能代替主程序员工作。资料员负责保存和管理所有的软件配置元素，如文档资料、程序清单和存储介质等，还编译和链接代码、对提交的所有模块进行初步的测试。程序员则集中精力负责完成主程序员分配给他最擅长的任务——编程。这种组织形式便于集中领导，步调统一，容易按规范办事，但不利于发挥个人的积极性。

（2）无主程序员组。无主程序员组中的成员之间相互平等，工作目标和决策都由全体成员民主讨论，根据需要也可以轮流坐庄。这种组民主气氛比较足，依赖个人的成分少，有利于发挥每个人的积极性。但这种组中交流量大，往往职责不明确，出了问题谁也不负责，而且不利于与外界的联系。

（3）层次式程序员组。层次式组中有一位组长，组长负责全面的工作，他领导若干名高级程序员，每个高级程序员又领导若干名程序员。这种组适合于具有层次结构特点的更大型的软件项目，该项目可分成若干个子项目，每个高级程序员负责一个子项目，然后再对子项目分解，并分配给程序员。

4.1.4　软件配置管理

在软件建立时变更是不可避免的，而变更时由于没有进行变更控制，可能加剧了项目中的混乱，为协调软件开发使得混乱减到最小，使用配置管理技术，使变更所产生的错误达到最小并最有效地提高生产率。

软件配置管理（Software Configure Management, SCM）用于整个软件工程过程。其主要目标是标识变更；控制变更；确保变更正确地实现；报告有关变更。SCM是一组管理整个软件生存期各阶段中变更的活动。

1．基线

基线是软件生存期中各开发阶段的一个特定点，它的作用是把开发各阶段工作的划分更加明确化，使本来连续的工作在这些点上断开，以便于检查与肯定阶段成果。因此，基线可以作为一个检查点，在开发过程中，当采用的基线发生错误时，可以知道所处的位置，返回到最近和最恰当的基线上。

2．软件配置项

软件配置项（Software Configure Item, SCI）是软件工程中产生的信息项，它是配置管理的基本单位，对已经成为基线的SCI，虽然可以修改，但必须按照一个特殊的、正式的过程进行评估，确认每一处修改。以下的SCI是SCM的对象，并可形成基线。

（1）系统规格说明书。

（2）软件项目实施计划。

（3）软件需求规格说明书。

（4）设计规格说明书（数据设计、体系结构设计、模块设计、接口设计、对象描述（使用面向对象技术时））。

（5）源代码清单。

（6）测试计划和过程、测试用例和测试结果记录。

（7）操作和安装手册。

（8）可执行程序（可执行程序模块、连接模块）。

（9）数据库描述（模式和文件结果、初始内容）。

（10）用户手册。

（11）维护文档（软件问题报告、维护请求、工程变更次序）。

（12）软件工程标准。

（13）项目开发小结。

此外，许多软件工程组织把配置控制之下的软件工具，即编辑程序、编译程序、其他CASE工具的特定版本都作为软件配置的一部分列入其中。

3．版本控制

软件配置实际上是一个动态的概念，它一方面随着软件生存期向前推进，SCI的数量在不断增多，一些文档经过转换生成另一些文档，并产生一些信息；另一方面又随时会有新的变更出现，形成新的版本。

图4-3　版本演变

表达系统不同版本的一种表示如图4-3所示的演变图，在图中各个节点是一个完全的软件版本。软件的每一个版本都是SCI（源代码、文档、数据）的一个汇集，且各个版本都可能由不同的变种组成。

4．变更控制

软件工程过程中某一阶段的变更，均要引起软件配置的变更，这种变更必须严格加以控制和管理，保持修改信息，并把精确、清晰的信息传递到软件工程过程的下一步骤。

对于一个大型软件来说，不加控制的变更很快就会引起混乱。因此，变更控制是一项最重要的软件配置任务。为有效地实现变更控制，需借助于配置数据库和基线的概念。

配置数据库可以分为如下三类。

（1）开发库。专供开发人员使用，其中的信息可能作频繁修改，对其控制相当宽松。

（2）受控库。在生存期某一阶段工作结束时发布的阶段产品，这些是与软件开发工作相关的计算机可读信息和人工可读信息。软件配置管理正是对受控库中的各个软件项进行管理，受控库也称为软件配置库。

（3）产品库。在开发的软件产品完成系统测试后，作为最终产品存入产品库，等待交付用户或现场安装。

4.1.5　软件工具与软件开发环境

用来辅助软件开发、运行、维护、管理和支持等过程中的活动的软件称为软件工具。早期的软件工具主要用来辅助程序员编程，如编辑程序、编译程序和排错程序等。在提出了软件工程的概念以后，又出现了软件生存周期的概念，出现了许多开发模型和开发方法，同时软件管理也开始受到人们的重视。与此同时，出现了一批软件工具来辅助软件工程的实施，这些软件工具涉及到软件开发、维护、管理过程中的各项活动，并辅助这些活动高效、高质量地进行。因此，软件工具通常也称为CASE工具。

软件开发环境是指支持软件产品开发的软件系统，它由软件工具集和环境集成机制构成。工具集应包括支持软件开发相关过程、活动、任务的软件工具，以对软件开发提供全面的支持。环境集成机制为工具集成和软件开发、维护和管理提供统一的支持，通常包括数据集成、控制集成和界面集成。

1．软件工具

软件工具的种类繁多，很难有一种统一的分类方法，通常从不同的观点来进行分类。由于大多数软件工具仅支持软件生存周期过程中的某些特定活动，因此通常可以按软件过程的活动来进行分类。

1）软件开发工具

对应于软件开发过程的各种活动，软件开发工具通常有需求分析工具、设计工具、编码与排错工具、测试工具等。

（1）需求分析工具。用以辅助软件需求分析活动的软件称为需求分析工具，它辅助系统分析员从需求定义出发，生成完整的、清晰的、一致的功能规范（functional specification）。功能规范是软件所要完成功能的准确而完整的陈述，它描述该软件要做什么及只做什么。

按照需求定义的方法可将需求分析工具分为基于自然语言或图形描述的工具和基于形式化需求定义语言的工具。

（2）设计工具。用以辅助软件设计活动的软件称为设计工具，它辅助设计人员从软件功能规范出发，得到相应的设计规范（design specification）。对应于概要设计活动和详细设计活动，设计工具通常可分为概要设计工具和详细设计工具。

概要设计工具用以辅助设计人员设计目标软件的体系结构、控制结构和数据结构，详细设计工具用以辅助设计人员设计模块的算法和内部实现细节。除此之外，还有基于形式化描述的设计工具和面向对象分析与设计工具。

（3）编码与排错工具。辅助程序员进行编码活动的工具有编码工具和排错工具。编码工具辅助程序员用某种程序设计语言编制源程序，并对源程序进行翻译，最终转换成可执行的代码。因此，编码工具通常与编码所使用的程序语言密切相关。排错工具用来辅助程序员寻找源程序中错误的性质和原因，并确定其出错的位置。

（4）测试工具。用于支持进行软件测试的工具称为测试工具，分为数据获取工具、静态分析工具、动态分析工具、模拟工具以及测试管理工具。其中，静态分析工具通过对源程序的程序结构、数据流和控制流进行分析，得出程序中函数（过程）的调用与被调用关系、分支和路径、变量定义和引用等情况，发现语义错误。动态分析工具通过执行程序，检查语句、分支和路径覆盖，测试有关变量值的断点，即对程序的执行流进行探测。

2）软件维护工具

辅助软件维护过程中活动的软件称为软件维护工具，它辅助维护人员对软件代码及其文档进行各种维护活动。软件维护工具主要有版本控制工具、文档分析工具、开发信息库工具、逆向工程工具和再工程工具。

（1）版本控制工具。在软件开发和维护过程中，一个软件往往有多个版本，版本控制工具用来存储、更新、恢复和管理一个软件的多个版本。

（2）文档分析工具。文档分析工具用来对软件开发过程中形成的文档进行分析，给出软件维护活动所需的维护信息。例如，基于数据流图的需求文档分析工具可给出对数据流图的某个成分（如加工）进行维护时的影响范围及被影响范围，以便在该成分修改的同时考虑其影响范围内的其他成分是否也要修改。除此之外，利用文档分析工具还可以得到被分析文档的有关信息，如文档各种成分的个数、定义及引用情况等。

（3）开发信息库工具。开发信息库工具用来维护软件项目的开发信息，包括对象、模块等。它记录每个对象的修改信息（已确定的错误及重要改动）和其他变形（如抽象数据结构的多种实现），还必须维护对象和与之有关信息之间的关系。

（4）逆向工程工具。逆向工程工具辅助软件人员将某种形式表示的软件（源程序）转换成更高抽象形式表示的软件。这种工具力图恢复源程序的设计信息，使软件变得更容易理解。逆向工程工具分为静态的和动态的两种。

（5）再工程工具。再工程工具用来支持重构一个功能和性能更为完善的软件系统。目前的再工程工具主要集中在代码重构、程序结构重构和数据结构重构等方面。

3）软件管理和软件支持工具

软件管理和软件支持工具用来辅助管理人员和软件支持人员的管理活动和支持活动，以确保软件高质量地完成。辅助软件管理和软件支持的工具很多，其中常用的工具有项目管理工具、配置管理工具和软件评价工具。

（1）项目管理工具。项目管理工具用来辅助软件的项目管理活动。通常项目管理活动包括项目的计划、调度、通信、成本估算、资源分配及质量控制等。一个项目管理工具通常把重点放在某一个或某几个特定的管理环节上，而不提供对管理活动包罗万象的支持。

（2）配置管理工具。配置管理工具用来辅助完成软件配置项的标识、版本控制、变化控制、审计和状态统计等基本任务，使得各配置项的存取、修改和系统生成易于实现，从而简化了审计过程，改进状态统计，减少错误，提高系统的质量。

（3）软件评价工具。软件评价工具用来辅助管理人员进行软件质量保证的有关活动。它通常可以按照某个软件质量模型（如McCall软件质量模型，ISO软件质量度量模型等）对被评价的软件进行度量，然后得到相关的软件评价报告。软件评价工具有助于软件质量控制，对确保软件质量有重要的作用。

软件工具的种类非常多，上面只列举了一些主要的也是常用的工具。软件工具可以从不同的角度进行分类，上述分类只是其中较流行的一种，而且这种分类并非是严格的，有些工具可以属于这一类，也可以属于另一类。

2．软件开发环境

软件开发环境（software development environment）是支持软件产品开发的软件系统。它由软件工具集和环境集成机制构成，前者用来支持软件开发的相关过程、活动和任务；后者为工具集成和软件开发、维护和管理提供统一的支持，通常包括数据集成、控制集成和界面集成。通过环境集成机制，各工具用统一的数据接口规范存储或访问环境信息库；各工具采用统一的界面形式，保证各工具界面的一致性，同时为各工具或开发活动之间的通信、切换、调度和协同工作提供支持。在软件开发环境中进行软件开发，可以使用环境中提供的各种工具，同时在环境信息库的支持下，一个工具所产生的结果信息可以为其他工具利用，使得软件开发的各项活动得到连续的支持，从而大大提高了软件的开发效率，提高了软件的质量。

软件开发环境的特征如下。

（1）环境的服务是集成的。软件开发环境应支持多种集成机制，如平台集成、数据集成、界面集成、控制集成和过程集成等。

（2）环境应支持小组工作方式，并为其提供配置管理。

（3）环境的服务可用于支持各种软件开发活动，包括分析、设计、编程、测试、调试和文档等。

集成型开发环境是一种把支持多种软件开发方法和开发模型的软件工具集成在一起的软件开发环境。这种环境应该具有开放性和可剪裁性。开放性为环境外的工具集成到环境中来提供了方便，可剪裁性可根据不同的应用和不同的用户需求进行剪裁，以形成特定的开发环境。

3．计算机辅助软件工程

CASE是一组工具和方法的集合，可以辅助软件开发生命周期各阶段进行软件开发。CASE把软件开发技术、软件工具和软件开发方法集成到一个统一而一致的框架中，并且吸收了CAD（计算机辅助设计）、软件工程、操作系统、数据库、网络和许多其他计算机领域的原理和技术。

4.1.6　软件过程管理

软件过程是软件生存周期中的一系列相关活动，即用于开发和维护软件及相关产品的一系列活动。

软件产品的质量取决于软件开发过程，具有良好软件过程的软件机构能够开发出高质量的软件产品。这一点虽然早已为人们公认，但切实地在软件过程方面开展工作也只是十多年前的事。1987年，在美国国防部支持下，卡内基·梅隆大学率先推出了软件工程评估项目的研究成果——软件过程能力成熟度模型（CMM）。很快就引起了软件界的广泛关注，并在此后引发了一系列反响，以至在其基础上形成了国际标准（ISO/IEC 15504）。

1．软件过程评估的意义

软件过程评估是软件改进和软件能力评价的前提环节。

1）软件过程改进的需要

（1）软件过程不断改进是软件工程的基本原理之一。

1983年，美国TRW公司的B. W. Boehm总结了该公司在12年内总共花了15000人年，先后开发5代指挥控制软件的经验，得出了以下7条原则。

①　按软件生存周期分阶段制定计划并认真实施。

②　逐阶段进行确认。

③　坚持严格的产品控制。

④　使用现代程序设计技术。

⑤　明确责任。

⑥　用人少而精。

⑦　不断改进开发过程。

这就是著名的软件工程七原理。由此可见，不断改进软件开发过程是软件工程的基本原理之一。

（2）软件过程改进是软件生存周期的基本过程之一。

软件工程界始终十分重视对软件过程的研究。20世纪70年代中期形成了软件生存周期的概念，1995年正式发布了一项国际标准，即ISO/IEC 12207信息技术——软件生存周期过程，这是软件过程研究的一个重要成果。这项标准科学地定义了软件生存周期的过程，总共17个，其中一个就是改进过程。

2）降低软件风险的需要

（1）软件采购者的需要。软件产品或软件服务的采购单位进行招标、选择承制者时，为了降低风险，需要对备选单位的软件过程能力进行评价，而这种评价的依据是对该单位的软件过程的评估结果。

（2）软件承制者的需要。软件产品研制单位和软件服务单位在响应顾客的需要、进行投标时，为了降低风险，需要对自己的软件过程能力进行评价，避免承担力所不及的任务，而这种评价的依据仍然是根据实际需要，对相应软件过程的评估结果。

2．软件能力成熟度模型简介

1）软件能力成熟度模型简介

软件能力成熟度模型是对软件组织进化阶段的描述，随着软件组织定义、实施、测量、控制和改进其软件过程，软件组织的能力经过这些阶段逐步前进。这个能力成熟度模型使软件组织能够较容易地确定其当前过程的成熟度并识别出其软件过程执行中的薄弱环节，确定对软件质量和过程改进最为关键的几个问题，从而形成对其过程的改进策略。软件组织只要关注并认真实施一组有限的关键实践活动，就能稳步地改善其全组织的软件过程，使全组织的软件过程能力持续增长。

软件过程能力成熟度模型将软件过程能力成熟度划分为如下5级。

（1）初始级（initial）。软件过程是无序的，有时甚至是混乱的，对过程几乎没有定义，项目的成功完全依赖个人努力和英雄式核心人物。

（2）可重复级（repeatable）。建立了基本的项目管理过程和实践来跟踪项目费用、进度和功能特性。制定了必要的过程纪律，能重复早先类似应用项目取得的成功。

（3）已定义级（defined）。已将软件管理和工程两方面的过程文档化、标准化，并综合成该组织的标准软件过程。所有项目均使用经批准、剪裁的标准软件过程来开发和维护软件。

（4）已管理级（managed）。收集对软件过程和产品质量的详细度量，对软件过程和产品都有定量的理解与控制。

（5）优化级（optimized）。过程的量化反馈和先进的新思想、新技术促使过程不断改进。

每一个成熟度等级为过程改进达到下一个等级提供一个基础。当前一个等级没有达到时，不能进入下一个等级。

2）能力成熟度集成模型

能力成熟度集成模型（Capacity Maturity Model Integrated, CMMI）是CMM模型的最新版本。随着应用的推广与模型本身的发展，该方法演绎成为一种被广泛应用的综合性模型。

CMMI有两种不同的表述方式，不同的表述方式，其级别表示不同的内容。CMMI的一种表述方式为连续表述，主要是关注某特定域的过程改进和能力评估；另一种表述方式为阶段式，主要是衡量一个企业的成熟度，而不把单个过程是否完成作为重点。

和CMM的5个成熟度等级一样，基于阶段式表述的CMMI有5个成熟度级别：初始级、已管理级、已定义级、量化管理级（quantitatively managed）和优化级。这5个成熟度等级中，要一级一级逐级进行。每一个成熟度等级为过程改进达到下一个等级提供基础。企业在实施CMMI的时候，当前一个等级没有达到时，不能进入下一个等级。

基于连续表述的CMMI共有6个（0～5）能力等级，未完整级（incomplete）、已执行级（performed）、已管理级、已定义级、量化管理级和优化级。这样的描述方式主要强调能力度等级，一个过程是否完成非常重要，因此分为6个等级。每个能力等级对应到一个一般目标，以及一组一般执行方法和特定方法。

3．统一过程（UP）

统一过程模型是一种“用例和风险驱动，以架构为中心，迭代并且增量”的开发过程，由UML方法和工具支持。迭代的意思是将整个软件开发项目划分为许多个小的“袖珍项目”，每个“袖珍项目”都包含正常软件项目的所有元素：计划、分析和设计、构造、集成和测试，以及内部和外部发布。

统一过程定义了5个阶段及其制品。

（1）起始阶段。专注于项目的初创活动。产生的主要工作产品有愿景文档、初始用例模型、初始项目术语表、初始业务用例、初始风险评估、项目计划（阶段及迭代）、业务模型以及一个或多个原型（需要时）。

（2）精化阶段。理解了最初的领域范围之后，进行需求分析和架构演进方面。产生的主要工作产品有用例模型、补充需求（包括非功能需求）、分析模型、软件体系结构描述、可执行的软件体系结构原型、初步的设计模型、修订的风险列表、项目计划（包括迭代计划、调整的工作流、里程碑和技术工作产品）以及初始用户手册。

（3）构建阶段。关注系统的构建，产生实现模型。产生的主要工作产品有设计模型、软件构件、集成的软件增量、测试计划及步骤、测试用例以及支持文档（用户手册、安装手册和对于并发增量的描述）。

（4）移交阶段。关注于软件提交方面的工作，产生软件增量。产生的主要工作产品有提交的软件增量、β测试报告和综合用户反馈。

（5）产生阶段。运行软件并监控软件的持续使用，提供运行环境的支持，提交并评估缺陷报告和变更请求。

每次迭代产生包括最终系统的部分完成的版本和任何相关的项目文档的基线，通过逐步迭代基线之间相互构建，直到完成最终系统。在每个迭代中，有5个核心工作流：捕获系统应该做什么的需求工作流，精化和结构化需求的分析工作流，在系统构架内实现需求的设计工作流，构造软件的实现工作流，验证实现是否如期望那样工作的测试工作流。随着UP的阶段进展，每个核心工作流的工作量发生了变化。前4个技术阶段由主要里程碑所终止。

统一过程的典型代表是RUP（Rational Unified Process）。RUP是UP的商业扩展，完全兼容UP，但比UP更完整、更详细。

4．敏捷方法

敏捷开发的总体目标是通过“尽可能早地、持续地对有价值的软件的交付”使客户满意。通过在软件开发过程中加入灵活性，敏捷方法可以使用户能够在开发周期的后期增加或改变需求。

敏捷过程的典型方法有很多，每一种方法基于一套原则，这些原则实现了敏捷方法所宣称的理念（敏捷宣言）。这些方法如下。

（1）极限编程（XP）。XP是一种轻量级（敏捷）、高效、低风险、柔性、可预测的、科学的软件开发方式。它由价值观、原则、实践和行为4个部分组成，彼此相互依赖、关联，并通过行为贯穿于整个生存周期。

·　4大价值观：沟通、简单性、反馈和勇气。

·　5个原则：快速反馈、简单性假设、逐步修改、提倡更改和优质工作。

·　12个最佳实践：计划游戏（快速制定计划、随着细节的不断变化而完善）、小型发布（系统的设计要能够尽可能早地交付）、隐喻（找到合适的比喻传达信息）、简单设计（只处理当前的需求，使设计保持简单）、测试先行（先写测试代码，然后再编写程序）、重构（重新审视需求和设计，重新明确地描述它们以符合新的和现有的需求）、结队编程、集体代码所有制、持续集成（可以按日甚至按小时为客户提供可运行的版本）、每周工作40个小时、现场客户和编码标准。

（2）水晶法（Crystal）。它认为每一个不同的项目都需要一套不同的策略、约定和方法论。

（3）并列争求法（Scrum）。该方法使用迭代的方法，其中把每30天一次的迭代称为一个“冲刺”，并按需求的优先级别来实现产品。多个自组织和自治的小组并行地递增实现产品。协调是通过简短的日常情况会议来进行，就像橄榄球中的“并列争球”。

（4）自适应软件开发（ASD）。它有6个基本的原则：有一个使命作为指导；特征被视为客户价值的关键点；过程中的等待是很重要的，因此“重做”与“做”同样关键；变化不被视为改正，而是被视为对软件开发实际情况的调整；确定的交付时间迫使开发人员认真考虑每一个生产的版本的关键需求；风险也包含其中。

4.1.7　软件质量管理与质量保证

软件质量是指反映软件系统或软件产品满足规定或隐含需求的能力的特征和特性全体。软件质量管理是指对软件开发过程进行独立的检查活动，由质量保证、质量规划和质量控制三个主要活动构成。软件质量保证是指为保证软件系统或软件产品充分满足用户要求的质量而进行的有计划、有组织的活动，其目的是生产高质量的软件。

1．软件质量特性

讨论软件质量首先要了解软件的质量特性。目前已经有多种软件质量模型来描述软件质量特性，如ISO/IEC 9126软件质量模型和Mc Call软件质量模型。

1）ISO/IEC 9126软件质量模型

ISO/IEC 9126软件质量模型由三个层次组成：第一层是质量特性，第二层是质量子特性，第三层是度量指标。该模型的质量特性和质量子特性如图4-4所示。

其中各质量特性和质量子特性的含义如下。

（1）功能性（functionality）。与一组功能及其指定的性质的存在有关的一组属性。功能是指满足规定或隐含需求的那些功能。

·　适应性（suitability）。与对规定任务能否提供一组功能以及这组功能是否适合有关的软件属性。

·　准确性（accurateness）。与能够得到正确或相符的结果或效果有关的软件属性。

·　互用性（interoperability）。与同其他指定系统进行交互操作的能力相关的软件属性。

·　依从性（compliance）。使软件服从有关的标准、约定、法规及类似规定的软件属性。

·　安全性（security）。与避免对程序及数据的非授权故意或意外访问的能力有关的软件属性。

图4-4　ISO/IEC软件质量模型

（2）可靠性（reliability）。与在规定的一段时间内和规定的条件下，软件维持在其性能水平有关的能力。

·　成熟性（maturity）。与由软件故障引起失效的频度有关的软件属性。

·　容错性（fault tolerance）。与在软件错误或违反指定接口的情况下，维持指定的性能水平的能力有关的软件属性。

·　易恢复性（recoverability）。与在故障发生后，重新建立其性能水平并恢复直接受影响数据的能力，以及为达到此目的所需的时间和努力有关的软件属性。

（3）易使用性（usability）。与为使用所需的努力和由一组规定或隐含的用户对这样使用所作的个别评价有关的一组属性。

·　易理解性（understandability）。与用户为理解逻辑概念及其应用所付出的劳动有关的软件属性。

·　易学性（learnability）。与用户为学习其应用（例如操作控制、输入、输出）所付出的努力相关的软件属性。

·　易操作性（operability）。与用户为进行操作和操作控制所付出的努力有关的软件属性。

（4）效率（efficiency）。在规定条件下，软件的性能水平与所用资源量之间的关系有关的软件属性。

·　时间特性（time behavior）。与响应和处理时间以及软件执行其功能时的吞吐量有关的软件属性。

·　资源特性（resource behavior）。与软件执行其功能时，所使用的资源量以及使用资源的持续时间有关的软件属性。

（5）可维护性（maintainability）。与进行规定的修改所需要的努力有关的一组属性。

·　易分析性（analyzability）。与为诊断缺陷或失效原因，或为判定待修改的部分所需努力有关的软件属性。

·　易改变性（changeability）。与进行修改、排错或适应环境变换所需努力有关的软件属性。

·　稳定性（stability）。与修改造成未预料效果的风险有关的软件属性。

·　易测试性（testability）。为确认经修改软件所需努力有关的软件属性。

（6）可移植性（portability）。与软件可从某一环境转移到另一环境的能力有关的一组属性。

·　适应性（adaptability）。与软件转移到不同环境时的处理或手段有关的软件属性。

·　易安装性（installability）。与在指定环境下安装软件所需努力有关的软件属性。

·　一致性（conformance）。使软件服从与可移植性有关的标准或约定的软件属性。

·　易替换性（replaceability）。与一软件在该软件环境中用来替代指定的其他软件的可能和努力有关的软件属性。

2）Mc Call软件质量模型

Mc Call软件质量模型从软件产品的运行、修正和转移三个方面确定了11个质量特性（如图4-5所示）。Mc Call也给出了一个三层模型框架，第一层是质量特性，第二层是评价准则，第三层是度量指标。

图4-5　Mc Call软件质量模型

2．软件质量保证

软件质量保证是指为保证软件系统或软件产品充分满足用户要求的质量而进行的有计划、有组织的活动，其目的是生产高质量的软件。在软件质量方面强调如下三个要点。

（1）软件必须满足用户规定的需求，与用户需求不一致的软件就无质量可言。

（2）软件应遵循规定标准所定义的一系列开发准则，不遵循这些准则的软件，其质量难以得到保证。

（3）软件还应满足某些隐含的需求，例如希望有好的可理解性、可维护性等，而这些隐含的需求可能未被明确地写在用户规定的需求中，如果软件只满足它的显式需求而不满足其隐含需求，那么该软件的质量是令人置疑的。

软件质量保证包括了与以下7个主要活动相关的各种任务。

（1）应用技术方法。应该把软件质量设计到软件产品或软件系统中去，而不是在事后再施加质量保证。由于这个原因，软件质量保证首先从选择一组技术方法和工具开始，这些方法和工具帮助分析人员形成高质量的规格说明和高质量的设计。

（2）进行正式的技术评审。一旦形成了规格说明和设计，就要对它们进行质量评估。完成质量评估的中心活动是正式的技术评审。正式的技术评审是一种由技术人员实施的程式化会议，其唯一的目的是揭露质量问题。在多数情况下，评审能像测试一样有效地揭露软件中的缺陷。

（3）测试软件。软件测试组合了多种测试策略，这些测试策略带有一系列有助于有效地检测错误的测试用例设计方法。许多软件开发人员把软件测试用作质量保证的“安全网”，也就是说，开发人员以为通过测试能揭露最多的错误，借此减轻对其他软件质量保证活动的需要。遗憾的是，即使是完成得很好的测试也不会像我们所期望的那样揭露所有的错误种类。

（4）标准的实施。对软件工程过程应用正式的开发标准和过程的程度在各公司中是不同的。多数情况，标准由客户或者某些章程确定。某些场合下标准是自己确定的。如果存在正式的标准，软件质量保证活动必须保证遵循这些标准。与标准是否一致的评估可以被软件开发者作为正式技术评审的一部分来进行。

（5）控制变更。对软件质量的主要威胁来自“变更（change）”。对软件的每次变更都有可能引入错误或者引起传播错误的副作用。变更控制过程是通过对变更的正式申请、评价变更的特性和控制变更的影响等直接提高软件的质量。变更控制应用在软件开发期间和较后的软件维护阶段。

（6）度量（metrics）。度量是任何工程科学必备的活动。软件质量保证的重要目标是跟踪软件质量和评价方法学及程序上的变更对软件质量的影响程度。要达到这个目标，应该收集软件度量。软件度量包括某些技术上的和面向管理的度量。

（7）记录保存和报告。记录保存和报告为软件质量保证提供收集和传播软件质量保证信息的过程。评审、检查、变更控制、测试和其他软件质量保证活动的结果必须变成项目历史记录的一部分，并且应该把它传播给需要知道这些结果的开发人员。例如，记录过程设计的每次正式技术评审结果，并把记录放置在文件夹中，该文件夹包含了有关模块的所有技术信息和软件质量保证信息。

3．软件复杂性

软件度量的一个重要分支就是软件复杂性度量。对于软件复杂性度量的参数很多，主要有如下几个。

·　规模。即总共的指令数，或源程序行数。

·　难度。通常由程序中出现的操作数的数目所决定的量来表示。

·　结构。通常用与程序结构有关的度量来表示。

·　智能度。即算法的难易程度。

软件复杂性主要表现在程序的复杂性。程序的复杂性主要指模块内程序的复杂性。程序复杂性主要有以下几种度量方法。

（1）代码行度量法。度量程序的复杂性，最简单的方法就是统计程序的源代码行数。此方法的基本考虑是统计一个程序的源代码行数，并以源代码行数作为程序复杂性的度量。

（2）McCabe度量法。McCabe度量法是由Thomas McCabe提出的一种基于程序控制流的复杂性度量方法。McCabe复杂性度量又称为环路度量，它认为程序的复杂性很大程度上取决于控制的复杂性。单一的顺序程序结构最为简单，循环和选择所构成的环路越多，程序就越复杂。这种方法以图论为工具，先画出程序图，然后用该图的环路数作为程序复杂性的度量值。程序图是退化的程序流程图，也就是说，把程序流程图中每个处理符号都退化成一个节点，原来连接不同处理符号的流线变成连接不同点的有向弧，这样得到的有向图就叫做程序图，如图4-6所示。程序图仅描述程序内部的控制流程，完全不表现对数据的具体操作以及分支和循环的具体条件。

图4-6　程序图的复杂性

根据图论，在一个强连通的有向图G中，环的个数V（G）由以下公式给出：

其中，V（G）是有向图G中的环路数，m是图G中弧的个数，n是图G中的节点数，p是G中的强连通分量个数。在一个程序中，从程序图的入口点总能到达图中的任何一个节点，因此，程序总是连通的，但不是强连通的。为了使程序图成为强连通图，从图的入口点到出口点加一条用虚线表示的有向边，使图成为强连通图。这样就可以使用上式计算环路复杂性了。

以图4-6为例，其中节点数n＝6，弧数m＝9，p＝1，则有：

即McCabe环路复杂的度量值为5。

4．软件评审

通常，把“质量”理解为“用户满意程度”。为了使得用户满意，有如下两个必要条件。

（1）设计的规格说明书符合用户的要求，这称为设计质量。

（2）程序按照设计规格说明所规定的情况正确执行，这称为程序质量。

软件的规格说明分为外部规格说明和内部规格说明。外部规格说明是从用户角度来看的规格，包括硬件／软件系统设计、功能设计；而内部规格说明是为了实现外部规格的更详细的规格，即软件模块结构与模块处理过程的设计。因此，内部规格说明是从开发者角度来看的规格说明。设计质量是由外部规格说明决定的，程序是由内部规格说明决定的。

1）设计质量的评审内容

设计质量评审的对象是在需求分析阶段产生的软件需求规格说明、数据需求规格说明，在软件概要设计阶段产生的软件概要设计说明书等。通常从以下几个方面进行评审。

（1）评价软件的规格说明是否合乎用户的要求，即总体设计思想和设计方针是否明确；需求规格说明是否得到了用户或单位上级机关的批准；需求规格说明与软件的概要设计规格说明是否一致等。

（2）评审可靠性，即是否能避免输入异常（错误或超载等）、硬件失效及软件失效所产生的失效，一旦发生应能及时采取代替手段或恢复手段。

（3）评审保密措施实现情况，即是否对系统使用资格进行检查；是否对特定数据、特定功能的使用资格进行检查；在检查出有违反使用资格的情况后，能否向系统管理人员报告有关信息；是否提供对系统内重要数据加密的功能等。

（4）评审操作特性实施情况，即操作命令和操作信息的恰当性，输入数据与输入控制语句的恰当性；输出数据的恰当性；应答时间的恰当性等。

（5）评审性能实现情况，即是否达到所规定性能的目标值。

（6）评审软件是否具有可修改性、可扩充性、可互换性和可移植性。

（7）评审软件是否具有可测试性。

（8）评审软件是否具有复用性。

2）程序质量的评审内容

程序质量评审通常是从开发者的角度进行评审，与开发技术直接相关。它是着眼于软件本身的结构、与运行环境的接口以及变更带来的影响而进行的评审活动。

软件的结构如下。

（1）功能结构。在软件的各种结构中，功能结构是用户唯一能见到的结构。因此，功能结构是联系用户与开发者的规格说明，它在软件的设计中占有极其重要的地位。在评审软件的功能结构时，必须明确软件的数据结构。需要检查的项目如下。

·　数据结构。包括数据名和定义；构成该数据的数据项；数据与数据间的关系。

·　功能结构。包括功能名和定义；构成该功能的子功能；功能与子功能之间的关系。

·　数据结构和功能结构之间的对应关系。包括数据元素与功能元素之间的对应关系；数据结构与功能结构的一致性。

（2）功能的通用性。在软件的功能结构中，某些功能有时可以作为通用功能反复出现多次。从功能便于理解、增强软件的通用性及降低开发的工作量等观点出发，希望尽可能多地使功能通用化。需检查的项目包括抽象数据结构（抽象数据的名称和定义、抽象数据组成元素的定义）；抽象功能结构。

（3）模块的层次。模块的层次是指程序模块结构。由于模块是功能的具体体现，所以模块层次应当根据功能层次来设计。

（4）模块结构。上述的模块层次结构是模块的静态结构。现在要检查模块的动态结构。模块分为处理模块和数据模块两类。模块间的动态结构也与这些模块分类有关。对这样的模块结构进行检查的项目如下。

·　控制流结构。规定了处理模块与处理模块之间的流程关系。检查处理模块之间的控制转移关系与控制转移形式（调用方式）。

·　数据流结构。规定了数据模块是如何被处理模块进行加工的流程关系。检查处理模块与数据模块之间的对应关系；处理模块与数据模块之间的存取关系。

·　模块结构与功能结构之间的对应关系。包括功能结构与控制流结构的对应关系；功能结构与数据流结构之间的对应关系；每个模块的定义（功能、输入输出数据）。

（5）处理过程的结构。处理过程是最基本的加工逻辑过程。对它的检查项目有模块的功能结构与实现这些功能的处理过程的结构应明确对应；控制流应是结构化的；数据的结构与控制流之间的对应关系应是明确的，并且可根据这种对应关系来明确数据流程的关系；用于描述的术语标准化。

3）与运行环境的接口

运行环境包括硬件、其他软件和用户。主要的检查项目如下。

（1）与硬件的接口。包括与硬件的接口约定，即根据硬件的使用说明等所做出的规定；硬件故障时的处理和超载时的处理。

（2）与用户的接口。包括与用户的接口约定，即输入数据的结构；输出数据的结构；异常输入时的处理，超载输入时的处理；用户存取资格的检查等。

5．软件容错技术

提高软件质量和可靠性的技术大致可分为两类，一类是避开错误，即在开发的过程中不让差错潜入软件的技术；另一类是容错技术，即对某些无法避开的差错，使其影响减至最小的技术。

1）容错软件定义

归纳容错软件的定义，有以下4种。

（1）规定功能的软件，在一定程度上对自身错误的作用（软件错误）具有屏蔽能力，则称此软件为具有容错功能的软件，即容错软件。

（2）规定功能的软件，在一定程度上能从错误状态自动恢复到正常状态，则称之为容错软件。

（3）规定功能的软件，在因错误而发生错误时，仍然能在一定程度上完成预期的功能，则把该软件称为容错软件。

（4）规定功能的软件，在一定程度上具有容错能力，则称之为容错软件。

2）容错的一般方法

实现容错的主要手段是冗余。冗余是指对于实现系统规定功能是多余的那部分资源，包括硬件、软件、信息和时间。由于加入了这些资源，有可能使系统的可靠性得到较大的提高。通常冗余技术分为4类。

（1）结构冗余。结构冗余是通常采用的冗余技术。按其工作方法，可以分为静态、动态和混合冗余三种。

①　静态冗余。常用的有三模冗余（Triple Module Redundancy, TMR）和多模冗余。静态冗余通过表决和比较来屏蔽系统中出现的错误。如三模冗余，是对三个功能相同但由不同的人采用不同的方法开发出来的模块的运行结果通过表决，以多数结果作为系统的最终结果。即如果模块中有一个出错，这个错误能够被其他模块的正确结果“屏蔽”。由于无须对错误进行特别的测试，也不必进行模块的切换就能实现容错，故称为静态容错。

②　动态冗余。动态冗余的主要方式是多重模块待机储备。当系统测试到某工作模块出现错误时，就用一个备用模块来顶替它并重新运行。这里包括检测、切换和恢复过程，故称其为动态冗余。每当一个出错模块被其他备用模块顶替后，冗余系统相当于进行了一次重构。各备用模块在其待机时，可与主模块一同工作，也可不工作。前者叫做热备份系统，后者叫做冷备份系统。在热备份系统中，备用模块在待机过程中的失效率为0。

③　混合冗余。它兼有静态冗余和动态冗余的长处。

（2）信息冗余。为检测或纠正信息在运算或传输中的错误须外加一部分信息，这种现象称为信息冗余。在通信和计算机系统中，信息常以编码的形式出现。采用奇偶码、循环码等冗余码制式就可以发现甚至纠正这些错误。为了达到此目的，这些码（统称为误差校验码）的码长远远大于不考虑误差校正时的码长，增加了计算量和信道占用的时间。

（3）时间冗余。时间冗余是指以重复执行指令或程序来消除瞬时错误带来的影响。多余重复执行不成功的情况，通常的处理办法是发出中断，转入错误处理程序，或对程序进行复算，或重新组合系统，或放弃程序处理。在程序复算中比较常用的方法是程序滚回（Program Rollback）技术。

（4）冗余附加技术。冗余附加技术是指为实现上述冗余技术所需的资源和技术，包括程序、指令、数据、存放和调动它们的空间和通道等。

在屏蔽硬件错误的容错技术中，冗余附加技术包括：

①　关键程序和数据的冗余存储及调用。

②　检测、表决、切换、重构、纠错和复算的实现。

在屏蔽软件错误的容错系统中，冗余附加技术的构成包括：

①　冗余备份程序的存储及调用。

②　实现错误检测和错误恢复的程序。

③　实现容错软件所需的固化程序。

4.2　系统分析基础知识

4.2.1　系统分析概述

1．系统分析的目的和任务

系统分析的主要任务是对现行系统进一步详细调查，将调查中所得到的文档资料集中，对组织内部整体管理状况和信息处理过程进行分析，为系统开发提供所需资料，并提交系统方案说明书。系统分析侧重于从业务全过程的角度进行分析，主要内容有业务和数据的流程是否通畅，是否合理；数据、业务过程和组织管理之间的关系；原系统管理模式改革和新系统管理方法的实现是否具有可行性等。

确定的分析结果包括开发者对于现有组织管理状况的了解，用户对信息系统功能的需求，数据和业务流程，管理功能和管理数据指标体系以及新系统拟改动和新增的管理模型等。

最后，提出信息系统的各种设想和方案，并对所有的设想和方案进行分析、研究、比较、判断和选择，获得一个最优的新系统的逻辑模型，并在用户理解计算机系统的工作流程和处理方式的情况下，将它明确地表达成书面资料——系统分析报告，即系统方案说明书。

2．系统分析的主要步骤

企业信息系统是一个具有业务复杂性和技术复杂性的大系统，为的是目标系统既能实现当前系统的基本职能，又能改进和提高。系统开发人员首先必须理解并描述出已经实际存在的当前系统，然后进行改进，从而创造出基于当前系统，又高于当前系统的目标系统，即新系统。

系统分析过程一般按图4-7所示的逻辑进行。

图4-7　系统分析过程图

（1）认识、理解当前的现实环境，获得当前系统的“物理模型”。

（2）从当前系统的“物理模型”抽象出当前系统的“逻辑模型”。

（3）对当前系统的“逻辑模型”进行分析和优化，建立目标系统的“逻辑模型”。

（4）对目标系统的逻辑模型具体化（物理化），建立目标系统的物理模型。

系统开发的目的是把现有系统的物理模型转化为目标系统的物理模型，即图4-7中双虚线所描述的路径，而系统分析阶段的结果是得到目标系统的逻辑模型。逻辑模型反应了系统的功能和性质，而物理模型反映的是系统的某一种具体实现方案。

按照图4-7所示，可将系统分析阶段的主要工作分为如下几步。

（1）对当前系统进行详细调查，收集数据。

（2）建立当前系统的逻辑模型。

（3）对现状进行分析，提出改进意见和新系统应达到的目标。

（4）建立新系统的逻辑模型。

（5）编写系统方案说明书。

4.2.2　结构化分析方法

结构化分析（Structured Analysis, SA）方法是一种面向数据流的需求分析方法，适用于分析大型数据处理系统，是一种简单、实用的方法，现在已经得到广泛的使用。

结构化分析方法的基本思想是自顶向下逐层分解。分解和抽象是人们控制问题复杂性的两种基本手段。对于一个复杂的问题，人们很难一下子考虑问题的所有方面和全部细节，通常可以把一个大问题分解成若干个小问题，每个小问题再分解成若干个更小的问题，经过多次逐层分解，每个最底层的问题都是足够简单，容易解决的，于是复杂的问题也就迎刃而解了。这个过程就是分解的过程。

SA方法的分析结果由以下几部分组成：一套分层的数据流图、一本数据词典、一组小说明（也称加工逻辑说明）、补充材料。

1．数据流图

数据流图或称数据流程图（Data Flow Diagram, DFD），是一种便于用户理解、分析系统数据流程的图形工具。它摆脱了系统的物理内容，精确地在逻辑上描述系统的功能、输入、输出和数据存储等，是系统逻辑模型的重要组成部分。

1）DFD的基本成份

DFD的基本成份及其图形表示方法如图4-8所示。

图4-8　DFD的基本成份

·　数据流。数据流由一组固定成分的数据组成，表示数据的流向。值得注意的是，DFD中描述的是数据流，而不是控制流。除了流向数据存储或从数据存储流出的数据流不必命名外，每个数据流都必须有一个合适的名字，以反映该数据流的含义。

·　加工。加工描述了输入数据流到输出数据流之间的变换，也就是输入数据流经过什么处理后变成了输出数据流。每个加工有一个名字和编号。编号能反映出该加工位于分层DFD中的哪个层次和哪张图中，也能够看出它是哪个加工分解出来的子加工。

·　数据存储。数据存储用来表示存储的数据，每个数据存储都有一个名字。

·　外部实体。外部实体是指存在于软件系统之外的人员或组织，它指出系统所需数据的发源地和系统所产生的数据的归宿地。

2）分层数据流图的画法

（1）画系统的输入和输出。把整个软件系统看作一个大的加工，然后根据系统从哪些外部实体接收数据流，以及系统发送数据流到哪些外部实体，就可以画出系统的输入和输出图，这张图称为顶层图。

（2）画系统的内部。将顶层图的加工分解成若干个加工，并用数据流将这些加工连接起来，使得顶层图中的输入数据经过若干个加工处理后变换成顶层图的输出数据流。这张图称为0层图。从一个加工画出一张数据流图的过程实际上就是对这个加工的分解。

可以用下述的方法来确定加工：在数据流的组成或值发生变化的地方应画一个加工，这个加工的功能就是实现这一变化；也可根据系统的功能确定加工。

确定数据流的方法：当用户把若干个数据看作一个单位来处理（这些数据一起到达，一起加工）时，可把这些数据看成一个数据流。

对于一些以后某个时间要使用的数据可以组织成一个数据存储来表示。

（3）画加工的内部。把每个加工看作一个小系统，该加工的输入输出数据流看成小系统的输入输出数据流。于是可以用与画0层图同样的方法画出每个加工的DFD子图。

（4）对第（3）步分解出来的DFD子图中的每个加工，重复第（3）步的分解，直至图中尚未分解的加工都足够简单（也就是说这种加工不必再分解）为止。至此，得到了一套分层数据流图。

3）对图和加工进行编号

对于一个软件系统，其数据流图可能有许多层，每一层又有许多张图。为了区分不同的加工和不同的DFD子图，应该对每张图和每个加工进行编号，以利于管理。

（1）父图与子图。

假设分层数据流图里的某张图（记为图A）中的某个加工可用另一张图（记为图B）来分解，称图A是图B的父图，图B是图A的子图。在一张图中，有些加工需要进一步分解，有些加工则不必分解。因此，如果父图中有n个加工，那么它可以有0～n张子图（这些子图位于同一层），但每张子图都只对应于一张父图。

（2）编号。

①　顶层图只有一张，图中的加工也只有一个，所以不必编号。

②　0层图只有一张，图中的加工号可以分别是0.1，0.2，…或者是1，2，…。

③　子图号就是父图中被分解的加工号。

④　图的加工号由图号、圆点和序号组成。

例如，某图中的某加工号为2.4，这个加工分解出来的子图号就是2.4，子图中的加工号分别为2.4.1，2.4.2，…。

4）实例

某考务处理系统有如下功能。

（1）对考生送来的报名单进行检查。

（2）对合格的报名单进行检查。

（3）对阅卷站送来的成绩清单进行检查，并根据考试中心指定的合格标准审定合格者。

（4）制作考生通知单（内含成绩合格／不合格标志）送给考生。

（5）按地区、年龄、文化程度、职业和考试级别等进行成绩分类统计和试题难度分析，产生统计分析表。

该考务处理系统的分层数据流图如图4-9所示。

5）应注意的问题

（1）适当地为数据流、加工、数据存储、外部实体命名，名字应反映该成分的实际含义，避免空洞的名字。

（2）画数据流而不要画控制流。

（3）每条数据流的输入或者输出是加工。

（4）一个加工的输出数据流不应与输入数据流同名，即使它们的组成成分相同。

（5）允许一个加工有多条数据流流向另一个加工，也允许一个加工有两个相同的输出数据流流向两个不同的加工。

（6）保持父图与子图平衡。也就是说，父图中某加工的输入输出数据流必须与它的子图的输入输出数据流在数量和名字上相同。值得注意的是，如果父图的一个输入（或输出）数据流对应于子图中几个输入（或输出）数据流，而子图中组成这些数据流的数据项全体正好是父图中的这一个数据流，那么它们仍然算是平衡的。

（7）在自顶向下的分解过程中，若一个数据存储首次出现时只与一个加工有关，那么这个数据存储应作为这个加工的内部文件而不必画出。

（8）保持数据守恒。也就是说，一个加工所有输出数据流中的数据必须能从该加工的输入数据流中直接获得，或者是通过该加工能产生的数据。

（9）每个加工必须既有输入数据流，又有输出数据流。

（10）在整套数据流图中，每个数据存储必须既有读的数据流，又有写的数据流。但在某一张子图中可能只有读没有写，或者只有写没有读。

图4-9　考务处理系统分层数据流图

2．数据字典（DD）

数据流图描述了系统的分解，但没有对图中各成分进行说明。数据字典就是为数据流图中的每个数据流、文件、加工，以及组成数据流或文件的数据项做出说明。其中对加工的描述称为“小说明”，也可以称为“加工逻辑说明”。

1）数据字典的内容

数据字典有以下4类条目：数据流、数据项、数据存储和基本加工。数据项是组成数据流和数据存储的最小元素。源点、终点不在系统之内，故一般不在字典中说明。

（1）数据流条目。数据流条目给出了DFD中数据流的定义，通常列出该数据流的各组成数据项。在定义数据流或数据存储组成时，使用表4-1给出的符号。

表4-1　在数据字典的定义式中出现的符号

（2）数据存储条目。数据存储条目是对数据存储的定义。

（3）数据项条目。数据项条目是不可再分解的数据单位。

（4）加工条目。加工条目是用来说明DFD中基本加工的处理逻辑的，由于上层的加工是由下层的基本加工分解而来，只要有了基本加工的说明，就可理解其他加工。

2）数据词典管理

词典管理主要是把词典条目按照某种格式组织后存储在词典中，并提供排序、查找和统计等功能。如果数据流条目包含了来源和去向，文件条目包含了读文件和写文件，还可以检查数据词典与数据流图的一致性。

3．加工逻辑的描述

加工逻辑也称为“小说明”。常用的加工逻辑描述方法有结构化语言、判定表和判定树三种。

1）结构化语言

结构化语言（如结构化英语）是一种介于自然语言和形式化语言之间的半形式化语言，是自然语言的一个受限子集。

结构化语言没有严格的语法，它的结构通常可分为内层和外层。外层有严格的语法，而内层的语法比较灵活，可以接近于自然语言的描述。

（1）外层。用来描述控制结构，采用顺序、选择和重复三种基本结构。

①　顺序结构。一组祈使语句、选择语句、重复语句的顺序排列。祈使语句是指至少包含一个动词及一个名词，指出要执行的动作及接受动作的对象。

②　选择结构。一般用IF-THEN-ELSE-ENDIF、CASE-OF-ENDCASE等关键词。

③　重复结构。一般用DO-WHILE-ENDDO、REPEAT-UNTIL等关键词。

（2）内层。一般是采用祈使语句的自然语言短语，使用数据字典中的名词和有限的自定义词，其动词含义要具体，尽量不用形容词和副词来修饰。还可使用一些简单的算法运算和逻辑运算符号。

2）判定表

在有些情况下，数据流图中某个加工的一组动作依赖于多个逻辑条件的取值。这时，用自然语言或结构化语言都不易于清楚地描述出来，而用判定表就能够清楚地表示复杂的条件组合与应做的动作之间的对应关系。

判定表由4部分组成，用双线分割成4个区域，如图4-10所示。

图4-10　判定表结构

3）判定树

判定树是判定表的变形，一般情况下它比判定表更直观，且易于理解和使用。

4.2.3　系统分析报告

完成整个系统分析阶段的工作后，作为工作成果，应提交一份完整的系统分析报告。系统分析报告一经确认，就成为具有约束力的指导性文件，成为下一阶段系统设计工作的依据和今后验收目标系统的检验标准。系统分析报告形成后必须组织各方面的人员（包括组织的领导、管理人员、专业技术人员和系统分析人员等）一起对已经形成的方案进行论证，尽可能发现其中的问题和不足。对于有争论的问题要重新核实当初的原始调查资料或进一步地深入调查研究，对于重大的问题甚至可能需要调整或修改系统目标，重新进行系统分析。

在系统分析报告中，数据流图、数据字典和加工说明这三部分是主体，是系统分析报告中必不可少的组成部分。而其他各部分的内容，则应根据所开发的目标系统的规模、性质等具体情况酌情选用，不必生搬硬套。总之，系统分析报告必须简明扼要、抓住本质，反映出目标系统的全貌和开发人员的设想。

1．系统分析报告的主要作用

系统分析报告主要有以下三个作用。

（1）描述了目标系统的逻辑模型，作为开发人员进行系统设计和实施的基础。

（2）作为用户和开发人员之间的协议或合同，为双方的交流和监督提供基础。

（3）作为目标系统验收和评价的依据。

因此，系统分析报告是系统开发过程中的一份重要文档，必须完整、一致、精确且简明易懂。

2．系统分析报告的主要内容

一份完整的系统分析报告应该包括下述内容。

（1）组织情况概述。

①　对分析对象的基本情况作概括性的描述，包括组织的结构、组织的目标、组织的工作过程和性质、业务功能。

②　系统与外部实体（其他系统或机构）间有哪些物质以及信息的交换关系和联系。

③　参考资料和专门术语说明。

（2）现行系统概述。

①　现行系统现状调查说明。通过现行系统的组织结构图、数据流图和概况表等，说明现行系统的目标、规模、主要功能、组织机构、业务流程、数据存储和数据流，以及存在的薄弱环节。

②　系统需求说明。用户要求以及现行系统主要存在的问题等。

（3）系统逻辑模型

①　新系统拟定的业务流程及业务处理方式。提出明确的功能目标、并与现行系统进行比较和分析，重点要突出计算机处理的优越性。

②　新系统拟定的数据指标体系和分析优化后的数据流程，各个层次的数据流图、数据字典和加工说明，以及计算机系统将完成的工作部分。

③　出错处理要求。

④　其他特性要求。例如系统的输入输出格式、启动和退出等。

⑤　遗留问题。根据目前条件，暂时不能满足的一些用户要求或设想，并提出今后解决的措施和途径。

（4）新系统在各个业务处理环节拟采用的管理方法、算法或模型。

（5）与新系统相配套的管理制度和运行体制的建立。

（6）系统设计与实施的初步计划。

①　工作任务的分解。根据资源及其他条件确定各子系统开发的先后次序，在此基础上分解工作任务，落实到具体组织和个人。

②　根据系统开发资源与时间进度估计，指定进度安排计划。

③　预算。对开发费用的进一步估计。

（7）用户领导审批意见。

4.3　系统设计知识

4.3.1　系统设计的内容和步骤

在系统分析阶段，已经搞清楚了软件“做什么”的问题，并把这些需求通过规格说明书描述了出来，这也是目标系统的逻辑模型。进入设计阶段，要把软件“做什么”的逻辑模型转换成“怎么做”的物理模型，即着手实现软件系统的需求。

系统设计的主要目的就是为系统制定蓝图，在各种技术和实施方法中权衡利弊，精心设计，合理使用各种资源，最终勾画出新系统的详细设计方案。

系统设计的主要内容包括新系统总体结构设计、代码设计、输出设计、输入设计、处理过程设计、数据存储设计、用户界面设计和安全控制设计等。

系统设计的基本任务大体上可以分为概要设计和详细设计两个步骤。

1．概要设计的基本任务

1）设计软件系统总体结构

其基本任务是采用某种设计方法，将一个复杂的系统按功能划分成模块；确定每个模块的功能；确定模块之间的调用关系；确定模块之间的接口，即模块之间传递的信息；评价模块结构的质量。

软件系统总体结构的设计是概要设计关键的一步，直接影响到下一个阶段详细设计与编码的工作。软件系统的质量及一些整体特性都在软件系统总体结构的设计中决定。

2）数据结构及数据库设计

（1）数据结构的设计。逐步细化的方法也适用于数据结构的设计。在需求分析阶段，已经通过数据字典对数据的组成、操作约束和数据之间的关系等方面进行了描述，确定了数据的结构特性，在概要设计阶段要加以细化，详细设计阶段则规定具体的实现细节。在概要设计阶段，宜使用抽象的数据类型。

（2）数据库的设计。数据库的设计是指数据存储文件的设计，主要进行以下几方面设计。

①　概念设计。在数据分析的基础上，采用自底向上的方法从用户角度进行视图设计，一般用ER模型来表述数据模型。ER模型既是设计数据库的基础，也是设计数据结构的基础。

②　逻辑设计。ER模型是独立于数据库管理系统（DBMS）的，要结合具体的DBMS特征来建立数据库的逻辑结构。

③　物理设计。对于不同的DBMS，物理环境不同，提供的存储结构与存取方法各不相同。物理设计就是设计数据模式的一些物理细节，如数据项存储要求、存取方法和索引的建立等。

本节对数据库技术不做详细讨论，详细内容参见本书第7章。

3）编写概要设计文档

文档主要有概要设计说明书、数据库设计说明书、用户手册以及修订测试计划。

4）评审

对设计部分是否完整地实现了需求中规定的功能、性能等要求，设计方法的可行性，关键的处理及内外部接口定义的正确性、有效性、各部分之间的一致性等都一一进行评审。

2．详细设计的基本任务

（1）对每个模块进行详细的算法设计。用某种图形、表格和语言等工具将每个模块处理过程的详细算法描述出来。

（2）对模块内的数据结构进行设计。

（3）对数据库进行物理设计，即确定数据库的物理结构。

（4）其他设计。根据软件系统的类型，还可能要进行以下设计。

①　代码设计。为了提高数据的输入、分类、存储和检索等操作，节约内存空间，对数据库中某些数据项的值要进行代码设计。

②　输入输出格式设计。

③　用户界面设计。

（5）编写详细设计说明书。

（6）评审。对处理过程的算法和数据库的物理结构都要评审。

系统设计的结果是一系列的系统设计文件，这些文件是物理实现一个信息系统（包括硬件设备和编制软件程序）的重要基础。

4.3.2　系统设计的基本原理

1．抽象

抽象是一种设计技术，重点说明一个实体的本质方面，而忽略或者掩盖不很重要或非本质的方面。抽象是一种重要的工具，用来将复杂的现象简化到可以分析、实验或者可以理解的程度。软件工程中从软件定义到软件开发要经历多个阶段，在这个过程中每前进一步都可看作是对软件解法的抽象层次的一次细化。抽象的最低层就是实现该软件的源程序代码。在进行模块化设计时也可以有多个抽象层次，最高抽象层次的模块用概括的方式叙述问题的解法，较低抽象层次的模块是对较高抽象层次模块对问题解法描述的细化。

2．模块化

模块在程序中是数据说明、可执行语句等程序对象的集合，或者是单独命名和编址的元素，如高级语言中的过程、函数和子程序等。在软件的体系结构中，模块是可组合、分解和更换的单元。

模块化是指将一个待开发的软件分解成若干个小的简单部分——模块，每个模块可独立地开发、测试，最后组装成完整的程序。这是一种复杂问题“分而治之”的原则。模块化的目的是使程序的结构清晰，容易阅读、理解、测试和修改。

3．信息隐蔽

信息隐蔽是开发整体程序结构时使用的法则，即将每个程序的成分隐蔽或封装在一个单一的设计模块中，定义每一个模块时尽可能少地显露其内部的处理。在设计时首先列出一些可能发生变化的因素，在划分模块时将一个可能发生变化的因素隐蔽在某个模块的内部，使其他模块与这个因素无关。在这个因素发生变化时，只需修改含有这个因素的模块，而与其他模块无关。

信息隐蔽原则对提高软件的可修改性、可测试性和可移植性都有重要的作用。

4．模块独立

模块独立是指每个模块完成一个相对独立的特定子功能，并且与其他模块之间的联系简单。衡量模块独立程度的标准有两个：耦合性和内聚性。

1）耦合

耦合性是指模块之间联系的紧密程度。耦合性越高，则模块的独立性越差。模块间耦合的高低取决于模块间接口的复杂性、调用的方式及传递的信息。模块的耦合有以下几种类型。

·　无直接耦合。指两个模块间没有直接的关系，它们分别从属于不同模块的控制与调用，它们之间不传递任何信息。因此，模块间耦合性最弱，模块独立性最高。

·　数据耦合。指两个模块之间有调用关系，传递的是简单的数据值，相当于高级语言中的值传递。这种耦合程度较低，模块的独立性较高。

·　标记耦合。指两个模块之间传递的数据结构，如高级语言中的数据组名、记录名、文件名等这些名字即为标记，其实传递的是这个数据结构的地址。

·　控制耦合。指一个模块调用另一个模块时，传递的是控制变量，被调模块通过该控制变量的值有选择地执行块内的某一功能。

·　公共耦合。指通过一个公共数据环境相互作用的那些模块之间的耦合。

·　内容耦合。这是程度最高的耦合。当一个模块直接使用另一个模块的内部数据，或通过非正常入口而转入另一个模块内部，这种模块之间的耦合为内容耦合，这种情况往往出现在汇编程序设计中。

2）内聚

内聚是指模块内部各元素之间联系的紧密程度，例如一个完成多个功能的模块的内聚度就比完成单一功能的模块的内聚度低。内聚度越低，模块的独立性越差。内聚性有以下几种类型。

·　偶然内聚。指一个模块内的各个处理元素之间没有任何联系。

·　逻辑内聚。指模块内执行几个逻辑上相似的功能，通过参数确定该模块完成哪一个功能。

·　时间内聚。把需要同时执行的动作组合在一起形成的模块为时间内聚模块。

·　通信内聚。指模块内所有处理元素都在同一个数据结构上操作，或者指各处理使用相同的输入数据或者产生相同的输出数据。

·　顺序内聚。指一个模块中各个处理元素都密切相关于同一功能且必须顺序执行，前一功能元素的输出就是下一功能元素的输入。

·　功能内聚。这是最强的内聚。指模块内所有元素共同完成一个功能，缺一不可。耦合性和内聚性是模块独立性的两个定性标准，将软件系统划分模块时，尽量做到高内聚、低耦合，提高模块的独立性。

4.3.3　系统总体结构设计

系统总体结构设计是要根据系统分析的要求和组织的实际情况来对新系统的总体结构形式和可利用的资源进行大致设计，这是一种宏观、总体上的设计和规划。下面介绍系统总体设计的主要内容。

1．系统结构设计原则

为保证总体结构设计的顺利完成，主要应遵循以下几条原则。

（1）分解—协调原则。整个系统是一个整体，具有整体目的和功能。但这些目的和功能的实现又是由相互联系的各个组成部分共同工作的结果。解决复杂问题的一个很重要的原则就是把它分解成多个小问题分别处理，在处理过程中根据系统总体要求协调各部门的关系。

（2）自顶向下的原则。首先抓住系统总的功能目的，然后逐层分解，即先确定上层模块的功能，再确定下层模块的功能。

（3）信息隐蔽、抽象的原则。上层模块只规定下层模块做什么和所属模块间的协调关系，但不规定怎么做，以保证各模块的相对独立性和内部结构的合理性，使得模块与模块之间层次分明，易于理解、实施和维护。

（4）一致性原则。要保证整个软件设计过程中具有统一的规范、统一的标准和统一的文件模式等。

（5）明确性原则。每个模块必须功能明确、接口明确，消除多重功能和无用接口。

（6）模块之间的耦合尽可能小，模块的内聚度尽可能高。

（7）模块的扇入系数和扇出系数要合理。一个模块直接调用其他模块的个数称为模块的扇出系数；反之，一个模块被其他模块调用时，直接调用它的模块个数称为模块的扇入系数。模块的扇入、扇出系数必须适当。经验表明，一个设计得好的系统的平均扇入、扇出系数通常是3或4，一般不应超过7，否则会引起出错概率的增大。但菜单调用型模块扇入与扇出系数可以大一些，公用模块扇入系数可以大一些。

（8）模块的规模适当。过大的模块常常使系统分解得不充分，其内部可能包含了若干部分的功能，因此有必要进一步把原有的模块分解成若干功能尽可能单一的模块。但分解也必须适度，因为过小的模块有可能降低模块的独立性，造成系统接口的复杂性。

2．子系统划分

1）子系统划分的原则

为了便于今后系统开发和系统运行，子系统的划分应遵循如下几点原则。

（1）子系统要具有相对独立性。子系统的划分，必须使得子系统的内部功能、信息等各方面的凝聚性较好。子系统独立可以减少子系统间的相互影响，有利于多人分工开发不同的模块，从而提高软件产品的生产率，保证软件产品的质量，同时也增强了系统的可维护性和适应性。

（2）子系统之间数据的依赖性尽量小。子系统之间的联系要尽量减少，接口要简单明确。一个内部联系强的子系统对外部的联系必然很少，所以划分的时候，应将联系较多者列入子系统内部，剩余的一些分散、跨度比较大的联系，就成为这些子系统间的联系和接口。这样划分的子系统，将来调试、维护和运行都是非常方便的。

（3）子系统划分的结果应使数据冗余较小。如果把相关的功能数据分布到各个不同的子系统中，则会有大量的原始数据需要调用，大量的中间结果需要保存和传递，大量计算工作将要重复进行。从而使得程序结构紊乱，数据冗余，不但给软件编制工作带来很大的困难，而且系统的工作效率也大大降低。

（4）子系统的设置应考虑今后管理发展的需要。子系统的设置仅依靠上述系统分析的结构是不够的，因为现存的系统由于各种原因，很可能没有考虑到一些高层次管理决策的要求。

（5）子系统的划分应便于系统分阶段实现。信息系统的开发是一项较大的工程，它的实现一般都要分批进行，所以子系统的划分应能适应这种分期分批的实施。另外，子系统的划分还必须兼顾组织结构的要求。

（6）子系统的划分应考虑到各类资源的充分利用。一个适当的子系统划分应该既考虑有利于各种设备资源在开发过程中的搭配使用，又考虑到各类信息资源的合理分布和充分使用，以减少系统对网络资源的过分依赖，减少输入、输出和通信等设备压力。

2）子系统结构设计

子系统结构设计的任务是确定划分后的子系统模块结构，并画出模块结构图。这个过程中必须考虑以下几个问题。

（1）每个子系统如何划分成多个模块。

（2）如何确定子系统之间、模块之间传送的数据及其调用关系。

（3）如何评价并改进模块结构的质量。

（4）如何从数据流图导出模块结构图。

3．系统模块结构设计

1）模块的概念

模块是组成系统的基本单位，它的特点是可以组合、分解和更换。系统中任何一个处理功能都可以看成是一个模块。根据模块功能具体化程度的不同，可以分为逻辑模块和物理模块。在系统逻辑模型中定义的处理功能可视为逻辑模块。物理模块是逻辑模块的具体化，可以是一个计算机程序、子程序或若干条程序语句，也可以是人工过程的某项具体工作。

一个模块应具备以下4个要素。

（1）输入和输出。模块的输入来源和输出去向都是同一个调用者，即一个模块从调用者那里取得输入，进行加工后再把输出返回给调用者。

（2）处理功能。指模块把输入转换成输出所作的工作。

（3）内部数据。指仅供该模块本身引用的数据。

（4）程序代码。指用来实现模块功能的程序。

前两个要素是模块外部特性，即反映了模块的外貌。后两个要素是模块的内部特性。在结构化设计中，主要考虑的是模块的外部特性，其内部特性只做必要了解，具体的实现将在系统实施阶段完成。

2）模块结构图

为了保证系统设计工作的顺利进行，结构设计应遵循如下原则。

（1）所划分的模块其内部的凝聚性要强，模块之间的联系要少，即模块具有较强的独立性。

（2）模块之间的连接只能存在上下级之间的调用关系，不能有同级之间的横向联系。

（3）整个系统呈树状结构，不允许网状结构或交叉调用关系出现。

（4）所有模块（包括后继IPO图）都必须严格地分类编码并建立归档文件。

模块结构图主要关心的是模块的外部属性，即上下级模块、同级模块之间的数据传递和调用关系，并不关心模块的内部。

模块结构图是结构化设计中描述系统结构的图形工具。作为一种文档，它必须严格地定义模块的名字、功能和接口，同时还应当在模块结构图上反映出结构化设计的思想。模块结构图由模块、调用、数据、控制和转接5种基本符号组成，如图4-11所示，现说明如下。

图4-11　模块结构图的基本符号

·　模块。这里所说的模块通常是指用一个名字就可以调用的一段程序语句。长方形中间标上能反映模块处理功能的模块名字。

·　调用。箭头总是由调用模块指向被调用模块，但是应该理解被调用模块执行后又返回到调用模块。

如果一个模块是否调用一个从属模块，决定于调用模块内部的判断条件，则该调用模块间的判断调用采用菱形符号表示。如果一个模块通过其内部的循环功能来循环调用一个或多个从属模块，则该调用称为循环调用，用弧形箭头表示。判断调用和循环调用的表示方法如图4-12所示。

图4-12　模块调用示例

·　数据。当一个模块调用另一个模块时，调用模块可以把数据传送到被调用模块供处理，而被调用模块又可以将处理的结构送回到被调用模块。在模块之间传送的数据，使用与调用箭头平行的带空心圆的箭头表示，并在旁边标上数据名。图4-13（a）表示模块A调用模块B时，A将数据x、y传送给B，B将处理结果数据z返回给A。

·　控制信息。模块间有时还必须传送某些控制信息。例如，数据输入完成后给出的结束标志，文件读到末尾时所产生的文件结束标志等。控制信息与数据的主要区别是前者只反映数据的某种状态，不必进行处理。图4-13（b）中“无此职工”就是用来表示送来的职工号有误的控制信息。

·　转接符号。当模块结构图在一张纸上画不下，需要转接到另一张纸上，或者为了避免图上线条交叉时，都可以使用转接符号，圆圈内加上标号，如图4-14所示。

图4-13　模块间的数据传递

图4-14　转接符号的使用

4．数据存储设计

信息系统的主要任务是通过大量的数据获得管理所需要的信息，这就必须存储和管理大量的数据。因此，建立一个良好的数据组织结构和数据库，使整个系统都可以迅速、方便、准确地调用和管理所需的数据，是衡量信息系统开发工作好坏的主要指标之一。

数据结构组织和数据库或文件设计，就是要根据数据的不同用途、使用要求、统计渠道和安全保密性等，来决定数据的整体组织形式、表或文件的形式，以及决定数据的结构、类别、载体、组织方式、保密级别等一系列的问题。

一个好的数据结构和数据库应该充分反映物流发展变化的状况，充分满足组织的各级管理要求。同时还应该使得后继系统开发工作方便、快捷、系统开销（如占用空间、网络传输频度、磁盘或光盘读写次数等）小、易于管理和维护。有关数据库及数据库设计的相关内容可参见本书第7章。

在建立了数据的整体关系结构之后，剩下的就是要确定数据资源分布和安全保密属性了。其中数据资源的分布是针对分布数据库系统而言的，而安全保密属性的定义则是针对某些特殊信息，如财务数据等而言的。

（1）数据资源分布。如果所规划和设计的系统是在网络环境之下，那么数据库设计必须考虑整个数据资源在网络各节点（包括网络服务器）上的分配问题。

（2）数据的安全保密。一般数据库软件都提供定义数据安全保密性的基本功能。系统所提供的安全保密功能一般有8个等级（0～7级），4种不同方式（只读、只写、删除、修改），而且允许用户利用这8个等级的4种方式对每一个表自由地进行定义。

4.3.4　结构化设计方法

结构化设计（structured design, SD）方法是一种面向数据流的设计方法，它可以与SA方法衔接。结构化设计方法的基本思想是将系统设计成由相对独立、功能单一的模块组成的结构。

1．信息流的类型

在需求分析阶段，用SA方法产生了数据流图。面向数据流的设计能方便地将DFD转换成程序结构图。DFD中从系统的输入数据流到系统的输出数据流的一连串连续变换形成了一条信息流。DFD的信息流大体上可以分为两种类型：变换流和事务流。

（1）变换流。信息沿着输入通路进入系统，同时将信息的外部形式转换成内部表示，然后通过变换中心（也称主加工）处理，再沿着输出通路转换成外部形式离开系统。具有这种特性的信息流称为变换流。变换流型的DFD可以明显地分成输入、变换（主加工）和输出三大部分。

（2）事务流。信息沿着输入通路到达一个事务中心，事务中心根据输入信息（即事务）的类型在若干个动作序列（称为活动流）中选择一个来执行，这种信息流称为事务流。事务流有明显的事务中心，各活动流以事务中心为起点呈辐射状流出。

2．变换分析

变换分析是从变换流型的DFD导出程序结构图。

1）确定输入流和输出流，分离出变换中心

把DFD中系统输入端的数据流称为物理输入，系统输出端的数据流称为物理输出。物理输入通常要经过编辑、格式转换、合法性检查、预处理等辅助性的加工才能为主加工的真正输入（称为逻辑输入）。从物理输入端开始，一步步向系统的中间移动，可找到离物理输入端最远，但仍可被看作系统输入的那个数据流，这个数据流就是逻辑输入。同样，由主加工产生的输出（称为逻辑输出）通常也要经过编辑、格式转换、组成物理块、缓冲处理等辅助加工才能变成物理输出。从物理输出端开始，一步步向系统的中间移动，可找到离物理输出端最远，但仍可被看作系统输出的那个数据流，这个数据流就是逻辑输出。

DFD中从物理输入到逻辑输入的部分构成系统的输入流，从逻辑输出到物理输出的部分构成系统的输出流，位于输入流和输出流之间的部分就是变换中心。

2）第一级分解

第一级分解主要是设计模块结构的顶层和第一层。一个变换流型的DFD可映射成图4-15所示的程序结构图。图中顶层模块的功能就是整个系统的功能。输入控制模块用来接收所有的输入数据，变换控制模块用来实现输入到输出的变换，输出控制模块用来产生所有的输出数据。

图4-15　变换分析的第一级分解

3）第二级分解

第二级分解主要是设计中、下层模块。

（1）输入控制模块的分解。从变换中心的边界开始，沿着每条输入通路，把输入通路上的每个加工映射成输入控制模块的一个低层模块。

（2）输出控制模块的分解。从变换中心的边界开始，沿着每条输出通路，把输出通路上的每个加工映射成输出控制模块的一个低层模块。

（3）变换控制模块的分解。变换控制模块通常没有通用的分解方法，应根据DFD中变换部分的实际情况进行设计。

4）事务分析

事务分析是从事务流型DFD导出程序结构图。

（1）确定事务中心和每条活动流的流特性。图4-16给出了事务流型DFD的一般形式。其中事务中心（图中的T）位于数条活动流的起点，这些活动流从该点呈辐射状地流出。每条活动流也是一条信息流，它可以是变换流，也可以是另一条事务流。一个事务流型的DFD由输入流、事务中心和若干条活动流组成。

（2）将事务流型DFD映射成高层的程序结构。事务流型DFD的高层结构如图4-17所示。顶层模块的功能就是整个系统的功能。接收模块用来接收输入数据，它对应于输入流。发送模块是一个调度模块，控制下层的所有活动模块。每个活动流模块对应于一条活动流，它也是该活动流映射成的程序结构图中的顶层模块。

图4-16　事务流

图4-17　事务流型DFD的高层程序结构

（3）进一步分解。接收模块的分解类同于变换分析中输入控制模块的分解。每个活动流模块根据其流特性（变换流或事务流）进一步采用变换分析或事务分析进行分解。

5）SD方法的设计步骤

（1）复查并精化数据流图。

（2）确定DFD的信息流类型（变换流或事务流）。

（3）根据流类型分别实施变换分析或事务分析。

（4）根据系统设计的原则（参见4.3.3节）对程序结构图进行优化。

4.3.5　面向数据结构的设计方法

面向数据结构的设计方法以数据结构作为设计的基础，它根据输入输出数据结构导出程序的结构，适用于规模不大的数据处理系统。Jackson方法是一种典型的面向数据结构的设计方法。

1．Jackson图

尽管程序中实际使用的数据结构有许多种，但这些数据结构中数据元素间的逻辑关系只有顺序、选择和重复三类。表示数据元素间逻辑关系的Jackson图如图4-18所示。

图4-18　Jackson图的基本符号

图4-18（a）表示数据结构A由B、C、D三个成分顺序组成，其次序是从左到右；图4-18（b）表示数据结构A是B或C或D中的一个；图4-18（c）表示数据结构A由B重复出现多次（可以是0次）组成。

Jackson图同样也可用来表示程序结构，此时图中的方框表示模块。图4-18（a）表示程序的顺序结构，图4-18（b）表示程序的分支结构，图4-18（c）表示程序的重复结构。

2．Jackson方法的设计步骤

（1）分析并确定输入和输出数据的逻辑结构，并用Jackson图表示。

（2）找出输入数据结构与输出数据结构间有对应关系的数据单元。所谓有对应关系的数据单元，是指有直接因果关系，在程序中可以同时处理的数据单元。对于重复结构的数据单元，必须在重复次数和次序都相同时才有对应关系。

（3）用下述的三条规则从描述数据结构的Jackson图导出描述程序结构的Jackson图。

①　为每对有对应关系的数据单元，按照它们在数据结构图中的层次在程序结构图的相应层次画一个处理框。如果这对有对应关系的数据单元在输入数据结构图中所处的层次与输出数据结构图中所处的层次不同，则取它们中较低的层次作为处理框在程序结构图中的层次。

②　为输入数据结构图中剩余的每个数据单元，在程序结构图的相应层次上画一对应的处理框。

③　为输出数据结构图中剩余的每个数据单元，在程序结构图的相应层次上画一处理框。

（4）列出所有的操作，并把它们分配到程序结构图的适当位置上。

（5）用伪码表示程序。与描述程序结构的Jackson图对应的伪码如图4-19所示。

图4-19　Jackson图对应的伪码

4.3.6　系统详细设计

1．代码设计

代码是用来表征客观事物的一组有序的符号，以便于计算机和人工识别与处理。代码的类指代码符号的表示形式，一般有数字型、字母型和数字字母混合型。三种类型的代码各有所长，应根据使用者的要求、信息量的多少、信息交换的频度和使用者的习惯等方面综合考虑。

代码设计应该遵循以下基本原则。

（1）唯一性。一个对象可能有多个名称，也可按不同的方式对它进行描述。但在一个编码体系中，一个对象只能赋予它唯一的代码。最简单的就是职工编号、学生的学号等。

（2）合理性。代码结构与相应的分类体系相对应。

（3）可扩充性。应留有充分的余地，以备将来不断扩充的需要。

（4）简单性。结构尽可能简单，以减少各种差错。

（5）适用性。代码尽可能反映对象的特点，以助记忆，便于填写。

（6）规范性。国家有关编码标准是代码设计的重要依据，已有标准的必须遵循。在一个代码体系中，代码结构、类型和编写格式必须统一。

（7）系统性。有一定的分组规则，从而在整个系统中具有通用性。例如，在会计领域中，一级会计科目由国家财政部进行标准分类，二级会计科目由各部委或行业协会统一进行标准分类，并且这个分类还必须参照一二级科目的规律进行。

代码设计可以按照以下步骤来进行。

（1）确定代码对象。

（2）考察是否已有标准代码。如果国家或者某个部门对某些事物已经规定了标准代码，那么应遵循这些标准代码。

（3）根据代码的使用范围、使用时间和实际情况选择代码的种类与类型。

（4）考虑检错功能。

（5）编写代码表。代码编好后，要编制代码表，做详细说明，通知有关部门组织学习，以便正确使用。

目前常用的编码有顺序码、数字码、字符码和混合码，限于篇幅，这里不做详细论述。

2．输出设计

从系统开发的角度看，输出决定输入，即输入信息只有根据输出要求才能确定。输出设计包括以下几方面的内容。

（1）确定输出内容。输出内容的设计首先要确定用户在使用信息方面的要求。根据用户要求，设计输出信息的内容，包括信息形式（表格、图形、文字）、输出项目及数据结构、数据类型、位数及取值范围、数据的生成途径、完整性及一致性的考虑等。

（2）选择输出设备与介质。常用的输出设备有显示终端、打印机、磁带机、绘图仪、缩微胶卷输出器和多媒体设备。输出介质有纸张、磁带、磁盘、缩微胶卷、光盘和多媒体介质等。这些设备和介质各有特点，应根据用户对输出信息的要求，结合现有设备和资金条件选择。

（3）确定输出格式。输出格式要满足使用者的要求和习惯，做到格式清晰、美观、易于阅读和理解。

最终输出方式常用的只有两种：报表输出和图形输出。采用哪种输出形式为宜，应根据系统分析和管理业务的要求而定。一般来说，对于基层和具体事务的管理者，应用报表方式给出详细的记录数据为宜；而对于高层领导或宏观、综合管理部门，则应该使用图形方式用以显示综合数据或发展趋势等信息。

3．输入设计

输入设计的目的是保证向系统输入正确的数据。在此前提下，做到输入方法简单、迅速、经济、方便。为此，输入设计应遵循以下原则。

（1）最小量原则。这就是保证满足处理要求的前提下使输入量最小。输入量越少，出错机会越小，花费时间越少，数据一致性越好。

（2）简单性原则。输入的准备、输入过程应尽量容易，以减少错误的发生。

（3）早检验原则。对输入数据的检验尽量接近源数据发生点，使错误能及时得到改正。

（4）少转换原则。输入数据尽量使用其处理所需的形式记录，以免数据转换介质时发生错误。

输入设计的内容如下。

（1）确定输入数据内容。包括确定输入数据项名称、数据内容、精度和数值范围。

（2）输入方式设计。主要是根据总体设计和数据库设计的要求来确定数据输入的具体形式。常用的输入方式有键盘输入，模／数、数／模输入，网络数据传送，磁／光盘读入等。通常在设计新系统的输入方式时，应尽量利用已有的设备和资源，避免大批量的数据通过键盘输入。

（3）输入格式设计。实际设计数据输入（特别是大批量的数据统计报表输入）时，常常遇到统计报表（文件）结构与数据库文件结构不完全一致的情况，如有可能，应尽量改变统计报表或数据库关系表二者之一的结构，使其一致，以减少输入格式设计的难度。现在还可采用智能输入方式，由计算机自动将输入送至不同表格。

（4）校对方式设计。特别是针对数字、金额数等字段，没有适当的校对措施作保证是很危险的。所以对一些重要的报表，输入设计一定要考虑适当的校对措施，以减少出错的可能性。但应指出的是，绝对保证不出错的校对方式是没有的。

常用的校对方式有人工校对、二次输入校对（同一批数据两次输入）和数据平衡校对。

4．处理过程设计

总体结构设计将系统分解成许多模块，并决定了每个模块的外部特征：功能和界面。计算机处理过程的设计则要确定每个模块的内部特征，即内部的执行过程，包括局部的数据组织、控制流、每一步的具体加工要求及种种实施细节。通过这样的设计，为编写程序制定一个周密的计划。

处理过程设计的关键是用一种合适的表达方法来描述每个模块的执行过程。这种表示方法应该简明、精确，并由此能直接导出用编程语言表示的程序。常用的描述方式有图形、语言和表格三类，如传统的框图、各种程序语言和判定表等。

1）程序流程图

流程图（flow chart）即程序框图，是历史最久、流行最广的一种图形表示方法。程序流程图包括三种基本成分：加工步骤，用方框表示；逻辑条件，用菱形表示；控制流，用箭头表示。

图形表示的优点是直观、形象、容易理解。但从结构化程序设计的角度看，流程图不是理想的表达工具。缺点之一是表示控制的箭头过于灵活。使用得当，流程图简单易懂；使用不当，流程图可能非常难懂，而且无法维护。流程图的另一个缺点是只描述执行过程而不能描述有关数据。

2）盒图（NS图）

盒图是结构化程序设计出现之后，为支持这种设计方法而产生的一种描述工具。在NS图中，每个处理步骤用一个盒子表示，盒子可以嵌套。盒子只能从上头进入，从下头走出，除此之外别无其他出入口，所以盒图限制了随意的控制转移，保证了程序的良好结构。

3）形式语言

形式语言是用来描述模块具体算法的、非正式的、比较灵活的语言。其外层语法是确定的，而内层语法不确定。外层语法描述控制结构时使用类似于一般编程语言的保留字，所以是确定的。内层语法故意不确定，可以按系统的具体情况和不同层次灵活选用，实际上可用自然语言来描述具体操作。

可以看出，形式语言同结构性语言的想法是一致的。形式语言的优点是接近自然语言（英语），所以易于理解。另外，它可以作为注释嵌套在程序中成为内部文档，提高程序的自我描述性。同时，由于是语言形式，易于被计算机处理，所以可用行编辑程序或字处理系统对形式语言进行编辑修改。

4）决策树

如果一个加工决策或判断的步骤较多，则使用形式语言时，语句的嵌套层次太多，不便于基本加工的逻辑功能的清晰描述。决策树是一种图形工具，适合于描述加工中具有多个策略且每个策略和若干条件有关的逻辑功能。

5）决策表

在基本加工中，如果判断的条件较多，各条件又相互组合且相应的决策方案较多的情形下用决策树来描述，树的结构比较复杂，图中各项注释比较繁琐。决策表也是一种图形工具，呈表形。决策表将比较复杂的决策问题简洁、明确、一目了然地描述出来。

5．用户界面设计

用户界面是系统与用户之间的接口，也是控制和选择信息输入输出的主要途径。用户界面设计应坚持友好、简便、实用、易于操作的原则。例如，在设计菜单时应尽量避免菜单嵌套层次过多和每选择一次还需再确认一次的设计方式。在设计大批数据输入屏幕界面时，应避免颜色过于鲜艳和多变。

界面设计包括菜单方式、会话方式、操作提示方式以及操作权限管理方式等。

1）菜单方式

菜单是信息系统功能选择操作的最常用方式。按目前软件所提出的菜单设计工具，菜单的形式可以是下拉式、弹出式的，也可以是按钮选择方式的。

2）会话管理方式

在所有的用户界面中，几乎毫无例外地会遇到人机会话问题。最为常见的有当用户操作错误时，系统向用户发出提示和警告性信息；当系统执行用户操作命令遇到多种可能时，系统会要求用户进一步说明；系统定量分析的结果通过屏幕向用户发出控制性的信息等。这类会话的处理方式是让系统开发人员根据实际系统操作过程将会话语句写在程序中。

一般会话系统是面向企业领导的，会话系统设计必须满足会话的基本要求，如画面清晰、直观形象，明了简洁，具有容错和纠错能力，提供信息汉字化、图形化和表格化等。

因此，会话设计重点解决会话方式、容错能力和系统的模块结构。

在语音会话方式还没有广泛使用的今天，会话的基本工具是键盘、屏幕和打印机，常用的方式是回答式、菜单式、表格式和图形式。

纠错、容错的目的是保证会话的正确性，提高会话的效率，在系统中可采用如下方法。

·　提示法：分简单提示和重复提示法。

·　确认回答法：为用户误操作提供改错机会。

·　无效处理法：系统拒绝接收错误操作。

·　返回处理法：拒绝不熟悉系统的用户使用操作。

·　延时处理法：让用户有足够的时间理解系统的提问内容，防止错误回答。

·　帮助处理法：给用户提供帮助信息，并给予重新操作的机会。

3）提示方式与权限管理

为了操作使用方便，在设计系统时，常常把操作提示和要点同时显示在屏幕的旁边，以使用户操作方便，这是当前比较流行的用户界面设计方式。另一种操作提示设计方式则是将整个系统操作说明书全送入到系统文件中，并设置系统运行状态指针。当系统运行操作时，指针随着系统运行状态来改变，当用户按“求助”键时，系统则立刻根据当前指针调出相应的操作说明。

与操作方式有关的另一个内容就是对数据操作权限的管理。权限管理一般都是通过入网口令和建网时定义该节点级别相结合来实现的。

6．安全控制设计

从数据环境和数据处理两方面看，影响系统安全的因素有如下几个。

·　环境性因素。是指管理机构的组织、硬件和系统软件、系统开发、自然环境等方面的因素。例如，组织方面职责不分，没有监督机构等；硬件软件方面，硬件失灵，系统软件失灵，逻辑线路错误等；系统开发方面，没有按科学的方法开发系统和设计程序、系统未经测试和调试等；自然环境方面，火灾、水灾、风灾和地震等；安全管理方面，数据处理资源的接触是随意的、无必要的限制等。

·　数据处理因素。是指数据处理行为引起的各种情况。例如，输入环节录入错误信息，使用无效代码，按错功能键，丢失数据，重复输入，没有将数据存盘等；处理环节使用了错误的程序、错误的数据文件，处理不及时，丢失数据文件和程序等；输出环节错误地发送报表或不及时发送，报告中的错误未加更正等。

要进行系统的安全控制，应针对影响系统安全的两方面因素入手，有的放矢，相应地进行环境和数据处理两方面的有效控制，以保证系统安全有效地运行。

4.4　系统实施知识

4.4.1　系统实施概述

1．系统实施的目的和任务

系统实施是新系统开发工作的最后一个阶段。所谓实施，指的是将系统设计阶段的结果在计算机上实现，将原来纸面上的、类似于设计图式的新系统方案转换成可执行的应用软件系统。系统实施阶段的主要任务如下。

（1）按总体设计方案购置和安装计算机网络系统。硬件准备包括计算机主机、输入输出设备、存储设备、辅助设备（稳压电源、空调设备等）和通信设备等。购置、安装和调试这些设备要花费大量的人力、物力，并且持续相当长的时间。

（2）软件准备。软件准备包括系统软件、数据库管理系统以及一些应用程序。这些软件有些需要购买，有些需要组织人力编写。编写程序是系统实施阶段的重要任务之一。

（3）培训。主要指用户的培训，包括主管人员和业务人员。这些人多数来自现行系统，精通业务，但往往缺乏计算机知识。为了保证系统调试和运行顺利进行，应根据他们的基础提前进行培训，使他们适应、逐步熟悉新的操作方法。

（4）数据准备。数据的收集、整理、录入是一项既繁重、劳动量又大的工作。而没有一定基础数据的准备，系统调试就不可能很好地进行。一般来说，确定数据库模型之后，就应进行数据的整理、录入。这样既分散了工作量，又可以为系统调试提供真实的数据。

（5）投入转换和试运行。

在系统实施过程中，还有若干非技术因素的影响。信息系统的最终受益人是企业的最高领导层，信息系统建设涉及到企业机构、权限的重组，只有具备进行变革权利的人才能真正地推进企业信息化。

企业在推行管理信息化时，总经理首先要了解企业一些公众的心理，如企业的各级员工的习惯心理，对信息系统使用持不信任态度的怀疑性排斥心理。此外，信息系统的使用将传统的金字塔管理变为扁平管理，使以前无法暴露的灰色行为被一览无遗；素质较低或年龄较大的员工对操作计算机系统具有畏惧心理。如果没有妥善的培训或疏导，这些都将成为系统应用的极大障碍。

2．系统实施的步骤

系统开发工作沿着信息系统的生命周期逐渐推进，经过详细设计阶段后，便进入系统实施阶段。

（1）按总体设计方案购置和安装计算机网络系统。购置和安装硬件是比较简单的事情，只需要按总体设计的要求和可行性报告中财力资源的分析，选择好价格性能比高的设备，通知供货厂家按要求供货并安装即可。

（2）建立数据库系统。如果前面数据与数据流程分析以及数据库设计工作进行得比较规范，而且开发者又对数据库技术比较熟悉的话，按照数据库设计的要求只需1～2个人一天即可建立起一个大型数据库结构。

（3）程序设计。

（4）收集有关数据并进行录入工作，然后进行系统测试。

（5）人员培训、系统转换和试运行。

4.4.2　程序设计

程序设计的主要依据是系统设计阶段的HIPO图（Hierarchy Plus Input-Process-output，层次式输入—处理—输出）及数据库结构和编码设计。

1．程序设计方法

目前程序设计的方法大多按照结构化方法、原型方法、面向对象的方法进行。

程序设计的目的是为了实现开发者在系统分析和系统设计中提出管理方法和处理构想。所以在程序设计和实现中，应尽量借用已有的程序和各种开发工具，尽快尽好地实现系统，而不要在具体的程序设计和调试工作中花费过多的精力和时间。

1）结构化程序设计方法

若遇到某些开发过程不规范，模块划分不细，或者是因特殊业务处理的需要模块程序量较大时，结构化程序设计方法是一种非常有效的方法。结构化的程序设计方法主要强调三点。

（1）模块内部程序各部分要自顶向下地结构化划分。

（2）各程序部分应按功能组合。

（3）各程序部分的联系尽量使用调用子程序（CALL-RETURN）方式，不用或少用GOTO方式。

2）快速原型式的程序开发方法

具体实施方法是首先将HIPO图中类似带有普遍性的功能模块集中，如菜单模块、报表模块、查询模块、统计分析和图像模块等，这些模块几乎是每个子系统必不可少的；然后再去寻找有无相应、可用的软件工具，如果没有则可以考虑开发一个能够适合各子系统情况的通用模块；最后用这些工具生成这些程序模块原型。如果HIPO图中有一些特定的处理功能和模块，而这些功能和模块又是现有工具不可能生成出来的，则再考虑编制一段程序加进去，利用现有的工具和原型方法可以很快地开发出所要的程序。

3）面向对象程序设计方法

面向对象程序设计方法一般应与OOD所设计的内容相对应。它是一个简单直接的映射过程，即将OOD中所定义的范式直接用面向对象程序如C++、Smalltalk和Java等来取代即可。

2．程序设计基本模块

一个信息系统的应用软件由很多程序模块组成，这些程序模块可以归纳成为几种基本类型，其结构如图4-20所示。

图4-20　基本程序模块图

1）控制模块

控制模块包括主控制模块和各级控制模块。控制模块的主要功能是根据用户要求信息，由用户确定处理顺序，然后控制转向各处理模块的入口。

2）输入模块

主要用来输入数据，输入方式有键盘输入和软盘输入两种。

3）输入数据校验模块

该模块对已经输入计算机中的数据进行校验，以保证原始数据的正确性。校验的方法通常有重复输入校验和程序校验两种。

4）输出模块

输出模块用来将计算机的运行结果通过屏幕、打印机或磁盘、磁带等设备输出给用户。在信息系统中，一般都有大量的表格、图表需要输出，因此输出模块的质量直接关系到整个系统的性能。

5）处理模块

根据信息系统的不同应用要求，有不同的处理功能，通常有以下几种类型。

（1）文件更新模块。当系统应用的数据发生变化时，需要修改数据文件。例如，增加新的记录、修改数据项或记录、删除某些不需要的记录等。一般来说，文件更新模块应该具有下述功能。

①　对记录中关键字的控制功能，通过关键字查找相应记录。

②　控制总记录数的功能，以便控制追加、插入记录的位置。

③　具有按地址或字节访问的控制功能，以便确定修改数据的位置，控制插入或者追加的数据位置。

（2）分类合并模块。分类合并模块的主要功能是对已经建立的文件，按某关键字进行分类合并。例如，在材料核算系统中耗用材料要按照材料类型合并处理。分类合并程序应该具有下述功能。

①　控制记录总数的功能。

②　字符串比较的功能。

③　排序、统计和计数功能。

（3）计算模块。该模块的主要功能是进行计算处理，包括同类记录中各数据项的运算。

（4）数据检索模块。该模块的主要功能是为用户提供查询的有关信息，它包括输入查询要求和输出特定的查询结果。它是管理信息系统的人机接口，对于人机交互的友好程序以及查询响应时间等均有较高要求。

（5）预测或优化模块。该模块的主要功能是使用预测或优化的数学模型，利用信息系统所提供的有关数据进行计算和分析并输出结果，用来辅助企业或部门的管理人员进行决策。例如，库存管理中的ABC分类、最佳订货量计算和财务管理中的资金分析等。

3．程序设计语言的选择

每种程序设计语言都有自己的特点，为一个特定的开发项目选择编程语言时通常可以考虑下列一些因素：应用领域、算法和计算的复杂性、软件运行的环境（包括可使用的编译程序）、用户需求（特别是性能需求）、数据结构的复杂性、开发人员的水平等。

从应用领域来看，COBOL语言适用于商业领域的应用，FORTRAN语言适合于科学和工程计算的应用，PROLOG和LISP适合于人工智能的应用，对于一些采用面向对象方法的应用系统通常可选用C++或Java。有些程序设计语言可应用于多种应用领域。例如C语言，它原先是为辅助开发UNIX操作系统而设计的，主要用于开发系统软件，现在已经广泛应用于其他领域。

开发和维护高级语言程序要比开发和维护低级语言程序容易得多，但是高级语言程序经过编译后所产生的目标程序要比完成相同功能的低级语言程序长得多。也就是说，前者的功效要比后者的功效低。因此，某些对运行时间或存储空间有过高要求的项目，或者在不能提供高级语言编译程序的计算机上开发程序，往往要使用低级语言。在某些大型系统中，为了提高系统的运行效率，有时也对系统中在执行时间上起关键作用的模块局部使用低级语言。

当然，所选择的语言必须有可使用的编译程序。如果有多种语言都适合于某项目的开发时，也可以考虑选择开发人员比较熟悉的一种。

4.4.3　系统测试与调试

1．系统测试的意义、目的及原则

系统测试是为了发现错误而执行程序的过程，成功的测试是发现了至今尚未发现的错误的测试。

测试的目的就是希望能以最少的人力和时间发现潜在的各种错误和缺陷。应根据开发各阶段的需求、设计等文档或程序的内部结构精心设计测试实例，并利用这些实例来运行程序，以便发现错误的过程。信息系统测试应包括软件测试、硬件测试和网络测试。硬件测试、网络测试可以根据具体的性能指标来进行，此处所说的测试更多是指软件测试。

系统测试是保证系统质量和可靠性的关键步骤，是对系统开发过程中的系统分析、系统设计和实施的最后复查。根据测试的概念和目的，在进行信息系统测试时应遵循以下基本原则。

（1）应尽早并不断地进行测试。测试不是在应用系统开发完之后才进行的。由于原始问题的复杂性、开发各阶段的多样性以及参加人员之间的协调等因素，使得在开发各个阶段都有可能出现错误。因此，测试应贯穿在开发的各个阶段，尽早纠正错误，消除隐患。

（2）测试工作应该避免由原开发软件的人或小组承担，一方面，开发人员往往不愿否认自己的工作，总认为自己开发的软件没有错误；另一方面，开发人员的错误很难由本人测试出来，很容易根据自己编程的思路来制定测试思路，具有局限性。测试工作应由专门人员来进行，会更客观，更有效。

（3）设计测试方案时，不仅要确定输入数据，而且要根据系统功能确定预期输出结果。将实际输出结果与预期结果相比较就能发现测试对象是否正确。

（4）在设计测试用例时，不仅要设计有效合理的输入条件，也要包含不合理、失效的输入条件。测试的时候，人们往往习惯按照合理的、正常的情况进行测试，而忽略了对异常、不合理、意想不到的情况进行测试，而这些可能就是隐患。

（5）在测试程序时，不仅要检验程序是否做了该做的事，还要检验程序是否做了不该做的事。多余的工作会带来副作用，影响程序的效率，有时会带来潜在的危害或错误。

（6）严格按照测试计划来进行，避免测试的随意性。测试计划应包括测试内容、进度安排、人员安排、测试环境、测试工具和测试资料等。严格地按照测试计划可以保证进度，使各方面都得以协调进行。

（7）妥善保存测试计划、测试用例，作为软件文档的组成部分，为维护提供方便。

（8）测试例子都是精心设计出来的，可以为重新测试或追加测试提供方便。当纠正错误、系统功能扩充后，都需要重新开始测试，而这些工作重复性很高，可以利用以前的测试用例，或在其基础上修改，然后进行测试。

2．测试过程

测试是开发过程中一个独立且非常重要的阶段。测试过程基本上与开发过程平行进行。

一个规范化的测试过程通常包括以下基本的测试活动。

（1）制定测试计划。在制定测试计划时，要充分考虑整个项目的开发时间和开发进度以及一些人为因素和客观条件等，使得测试计划是可行的。测试计划的内容主要有测试的内容、进度安排、测试所需的环境和条件、测试培训安排等。

（2）编制测试大纲。测试大纲是测试的依据，它明确详尽地规定了在测试中针对系统的每一项功能或特性所必须完成的基本测试项目和测试完成的标准。

（3）根据测试大纲设计和生成测试用例，产生测试设计说明文档，其内容主要有被测项目、输入数据、测试过程和预期输出结果等。

（4）实施测试。测试的实施阶段是由一系列的测试周期组成的。在每个测试周期中，测试人员和开发人员将依据预先编制好的测试大纲和准备好的测试用例，对被测软件或设备进行完整的测试。

（5）生成测试报告。测试完成后，要形成相应的测试报告，主要对测试进行概要说明，列出测试的结论，指出缺陷和错误。另外，给出一些建议，如可采用的修改方法，各项修改预计的工作量及修改的负责人员。

4.4.4　测试策略和测试方法

1．软件测试策略

软件测试策略将软件测试用例的设计方法集成到一系列经过周密计划的步骤中去，从而使软件构造成功地完成。测试策略提供以下方面的路径图：描述将要进行的测试步骤，这些步骤计划和执行的时机，需要多少工作量、时间和资源。因此，任何测试策略都必须包含测试计划、测试用例设计、测试执行以及结果数据的收集和评估。

软件测试策略应该具有足够的灵活性，以便促进测试方法的定制。同时，它必须足够严格，以便在项目进行过程中对项目进行合理地策划和追踪管理。

有效的软件测试实际上分成4步进行。

1）单元测试

单元测试也称为模块测试，在模块编写完成且无编译错误后就可以进行。单元测试侧重于模块中的内部处理逻辑和数据结构。如果选用机器测试，一般用白盒测试法。这类测试可以对多个模块同时进行。

单元测试主要检查模块的以下5个特征。

（1）模块接口。模块的接口保证了测试模块的数据流可以正确地流入、流出。在测试中应检查以下要点。

①　测试模块的输入参数和形式参数在个数、属性、单位上是否一致。

②　调用其他模块时，所给出的实际参数和被调用模块的形式参数在个数、属性、单位上是否一致。

③　调用标准函数时，所用的参数在属性、数目和顺序上是否正确。

④　全局变量在各模块中的定义和用法是否一致。

⑤　输入是否仅改变了形式参数。

⑥　开／关的语句是否正确。

⑦　规定的I/O格式是否与输入输出语句一致。

⑧　在使用文件之前是否已经打开文件或使用文件之后是否已经关闭文件。

（2）局部数据结构。在单元测试中，局部数据结构出错是比较常见的错误，在测试时应重点考虑以下因素。

①　变量的说明是否合适。

②　是否使用了尚未赋值或尚未初始化的变量。

③　变量的初始值或默认值是否正确。

④　变量名是否有错（例如拼写错）。

（3）重要的执行路径。在单元测试中，对路径的测试是最基本的任务。由于不能进行穷举测试，需要精心设计测试例子来发现是否有计算、比较或控制流等方面的错误。

①　计算方面的错误。算术运算的优先次序不正确或理解错误；精度不够；运算对象的类型彼此不相容；算法错；表达式的符号表示不正确等。

②　比较和控制流的错误。本应相等的量由于精度造成不相等；不同类型进行比较；逻辑运算符不正确或优先次序错误；循环终止不正确（如多循环一次或少循环一次）、死循环；不恰当地修改循环变量；当遇到分支循环时，出口错误等。

（4）出错处理。好的设计应该能预测到出错的条件并且有对出错处理的路径。虽然计算机可以显示出错信息的内容，但仍需要程序员对出错进行处理，保证其逻辑的正确性，以便于用户维护。

（5）边界条件。边界条件的测试是单元测试的最后工作，也是非常重要的工作。软件容易在边界出现错误。

由于模块不是独立运行的程序，各模块之间存在调用与被调用的关系。在对每个模块进行测试时，需要开发两种模块。

·　驱动模块。相当于一个主程序，接收测试例子的数据，将这些数据送到测试模块，输出测试结果。

·　桩模块，也称为存根模块。桩模块用来代替测试模块中所调用的子模块，其内部可进行少量的数据处理，目的是为了检验入口，输出调用和返回的信息。

提高模块的内聚度可以简化单元测试。如果每个模块只完成一种功能，对于具体模块来讲，所需的测试方案数据就会显著减少，而且更容易发现和预测模块中的错误。

对于面向对象软件，单元的概念发生了变化。封装导出了类的定义。每个类和类的实例（对象）包装有属性（数据）和处理这些数据的操作（函数或方法）。封装的类常是单元测试的重点，然而，类中包含的操作是最小的可测试单元。由于类中可以包含一些不同的操作，且特殊的操作可以作为不同的类的一部分存在，因此，面向对象软件的类测试是由封装在该类中的操作和类的状态行为驱动的。

2）集成测试

集成测试就是把模块按系统设计说明书的要求组合起来进行测试。即使所有模块都通过了测试，但在集成之后，仍可能会出现问题：穿过模块的数据丢失；一个模块的功能对其他模块造成有害的影响；各个模块集成起来没有达到预期的功能；全局数据结构出现问题。另外，单个模块的误差可以接受，但模块组合后，可能会出现误差累积，最后到不能接受的程度，所以需要集成测试。

通常集成测试有两种方法：一种是分别测试各个模块，再把这些模块组合起来进行整体测试，即非增量式集成；另一种是把下一个要测试的模块组合到已测试好的模块中，测试完后再将下一个需要测试的模块组合起来进行测试，逐步把所有模块组合在一起，并完成测试，如自顶向下集成、自底向上集成，即增量式集成。非增量式集成可以对模块进行并行测试，能充分利用人力，并加快工程进度。但这种方法容易混乱，出现错误不容易查找和定位。增量式测试的范围一步步扩大，错误容易定位，而且已测试的模块可在新的条件下再测试，测试更彻底。

面向对象软件没有明显的层次控制结构，类的成分间的直接或间接相互作用，使得每次将一个操作集成到类中往往不可能。因此，面向对象系统的集成有两种不同的策略：一是基于线程的测试，集成响应系统的一个输入或事件所需的一组类，每个线程单独地集成和测试；另一种方法是基于使用的测试，通过测试很少使用服务类的那些类（称为独立类）开始构建系统，独立类测试完后，利用独立类测试下一层的类（称为依赖类）。继续依赖类的测试直到完成整个系统。

簇测试时面向对象软件集成测试中的一步。这里，利用试图发现协作中的错误的测试用例来测试协作的类簇。

3）确认测试

经过集成测试之后，软件就被集成起来，接口方面的问题已经解决，将进入软件测试的最后一个环节，即确认测试。确认测试的任务就是进一步检查软件的功能和性能是否与用户要求的一样。系统方案说明书描述了用户对软件的要求，所以是软件有效性验证的标准，也是确认测试的基础。

确认测试，首先要进行有效性测试以及软件配置审查，然后进行验收测试和安装测试，经过管理部门的认可和专家的鉴定后，软件即可以交给用户使用。

（1）有效性测试。就是在模拟环境下，通过黑盒测试检验所开发的软件是否与需求规格说明书一致。在设计测试例子时，除了检测软件的功能和性能之外，还需要对软件的容错性、维护性等其他方面进行检测。测试人员可由开发商的内部人员组成，但最好是没有参加该项目的有经验的软件设计人员。在所有测试例子完成之后，若发现测试结果与预期的不符，这时要列出缺陷清单。在这个阶段才发现的严重错误，一般很难在预定的时间内纠正，需要与用户协商，寻找妥善解决问题的办法。

（2）软件配置审查。主要是检查软件（源程序、目标程序）和文档（包括面向开发和用户的文档）以及数据（程序内部的数据或程序外部的数据）是否齐全，分类是否有序。确保文档、资料的正确和完善，以便维护阶段使用。

（3）验收测试。是以用户为主的测试。软件开发人员和质量保证人员也应该参加。在验收测试之前，需要对用户进行培训，以便熟悉该系统。验收测试的测试例子由用户参与设计，主要验证软件的功能、性能、可移植性、兼容性和容错性等，测试时一般采用实际数据。多数软件开发者使用α测试与β测试的过程，其中α测试是最终用户在开发者的场所进行，而β测试是在最终用户场所执行。

4）系统测试

系统测试是将已经确认的软件、计算机硬件、外设和网络等其他因素结合在一起，进行信息系统的各种集成测试和确认测试，其目的是通过与系统的需求相比较，发现所开发的系统与用户需求不符或矛盾的地方。系统测试是根据系统方案说明书来设计测试用例的，常见的系统测试主要有以下内容。

（1）恢复测试。监测系统的容错能力。检测方法是采用各种方法让系统出现故障，检验系统是否按照要求能从故障中恢复过来，并在约定的时间内开始事务处理，而且不对系统造成任何伤害。如果系统的恢复是自动的（由系统自动完成），需要验证重新初始化、检查点和数据恢复等是否正确。如果恢复需要人工干预，就要对恢复的平均时间进行评估并判断它是否在允许的范围内。

（2）安全性测试。检测系统的安全机制、保密措施是否完善，主要是为了检验系统的防范能力。测试的方法是测试人员模拟非法入侵者，采用各种方法冲破防线。系统安全性设计准则是使非法入侵者所花费的代价比进入系统后所得到的好处要大，此时非法入侵已无利可图。

（3）压力测试。也称为强度测试，是对系统在异常情况下的承受能力的测试，是检查系统在极限状态下运行时，性能下降的幅度是否在允许的范围内。因此，压力测试要求系统在非正常数量、频率或容量的情况下运行。压力测试主要是为了发现在有效的输入数据中可能引起不稳定或不正确的数据组合。例如，运行使系统处理超过设计能力的最大允许值的测试例子；使系统传输超过设计最大能力的数据，包括内存的写入和读出等。

（4）性能测试。检查系统是否满足系统设计方案说明书对性能的要求。性能测试覆盖了软件测试的各阶段，而不是等到系统的各部分都集成之后，才确定系统的真正性能。通常与强度测试结合起来进行，并同时对软件、硬件进行测试。软件方面主要从响应时间、处理速度、吞吐量和处理精度等方面来检测。

（5）可靠性、可用性和可维护性测试。通常使用以下两个指标来进行衡量：平均失效间隔时间（Mean Time Between Failures, MTBF）是否超过了规定的时限，因故障而停机时间（Mean Time To Repairs, MTTR）在一年中不应超过多少时间。

（6）安装测试。在安装软件系统时，会有多种选择。安装测试就是为了检测在安装过程中是否有误、是否容易操作等。主要监测系统的每一个部分是否齐全，硬件的配置是否合理，安装中需要产生的文件和数据库是否已产生，其内容是否正确等。

2．测试方法

测试是可以事先计划并可以系统地进行的一系列活动。因此，应该为软件过程定义软件测试模板，即将特定的测试方法和测试用例设计放在一系列的测试步骤中去。

软件测试方法分为静态测试和动态测试。

（1）静态测试。静态测试是指被测试程序不在机器上运行，而是采用人工检测和计算机辅助静态分析的手段对程序进行检测。

①　人工检测。人工检测是不依靠计算机而是靠人工审查程序或评审软件，包括代码检查、静态结构分析和代码质量度量等。

②　计算机辅助静态分析。利用静态分析工具对被测试程序进行特性分析，从程序中提取一些信息，以便检查程序逻辑的各种缺陷和可疑的程序构造。

（2）动态测试。动态测试是指通过运行程序发现错误。对软件产品进行动态测试时可以采用黑盒测试法和白盒测试法。

测试用例的设计如下。

测试用例由测试输入数据和与之对应的预期输出结构组成。在设计测试用例时，应当包括合理的输入条件和不合理的输入条件。

1）用黑盒法设计测试用例

黑盒测试也称为功能测试，在完全不考虑软件的内部结构和特性的情况下，测试软件的外部特性。进行黑盒测试主要是为了发现以下几类错误。

（1）是否有错误的功能或遗漏的功能？

（2）界面是否有误？输入是否正确接收？输出是否正确？

（3）是否有数据结构或外部数据库访问错误？

（4）性能是否能够接受？

（5）是否有初始化或终止性错误？

常用的黑盒测试技术有等价类划分、边值分析、错误猜测和因果图等。

（1）等价类划分。等价类划分法将程序的输入域划分为若干等价类，然后从每个等价类中选取一个代表性数据作为测试用例。每一类的代表性数据在测试中的作用等价于这一类中的其他值。这样就可以用少量代表性的测试用例取得较好的测试效果。等价类划分有两种不同的情况：有效等价类和无效等价类。在设计测试用例时，要同时考虑这两种等价类。

定义等价类的原则如下。

①　在输入条件规定了取值范围或值的个数的情况下，可以定义一个有效等价类和两个无效等价类。

②　在输入条件规定了输入值的集合或规定了“必须如何”的条件的情况下，可以定义一个有效等价类和一个无效等价类。

③　在输入条件是一个布尔量的情况下，可以定义一个有效等价类和一个无效等价类。

④　在规定了输入数据的一组值（假定n个），并且程序要对每一个输入值分别处理的情况下，可以定义n个有效等价类和一个无效等价类。

⑤　在规定了输入数据必须遵守的规则的情况下，可定义一个有效等价类（符合规则）和若干个无效等价类（从不同角度违反规则）。

⑥　在确知已划分的等价类中，各元素在程序处理中的方式不同的情况下，则应再将该等价类进一步地划分为更小的等价类。

定义好等价类之后，建立等价类表，并为每个等价类编号。在设计一个新的测试用例时，使其尽可能多地覆盖尚未覆盖的有效等价类，不断重复，最后使得所有有效等价类均被测试用例所覆盖。然后设计一个新的测试用例，使其只覆盖一个无效等价类。

（2）边界值划分。输入的边界比中间更加容易发生错误，因此用边界值分析来补充等价类划分的测试用例设计技术。边界值划分选择等价类边界的测试用例，既注重于输入条件边界，又适用于输出域测试用例。

对边界值设计测试用例应遵循的原则如下。

①　如果输入条件规定了值的范围，则应取刚达到这个范围的边界的值，以及刚刚超越这个范围边界的值作为测试输入数据。

②　如果输入条件规定了值的个数，则用最大个数、最小个数、比最小个数少1、比最大个数多1的数据作为测试数据。

③　根据规格说明的每个输出条件，使用上述两条原则。

④　如果程序的规格说明给出的输入域或输出域是有序集合，则应选取集合的第一个元素和最后一个元素作为测试用例。

⑤　如果程序中使用了一个内部数据结构，则应当选择这个内部数据结构边界上的值作为测试用例。

⑥　分析规格说明，找出其他可能的边界条件。

（3）错误推测。错误推测是基于经验和直觉推测程序中所有可能存在的各种错误，从而有针对性地设计测试用例的方法。其基本思想是列举出程序中所有可能有的错误和容易发生错误的特殊情况，根据它们选择测试用例。

（4）因果图。因果图法是从自然语言描述的程序规格说明中找出因（输入条件）和果（输出或程序状态的改变），通过因果图转换为判定表。

利用因果图导出测试用例需要经过以下几个步骤。

①　分析程序规格说明的描述中，哪些是原因，哪些是结果。原因常常是输入条件或是输入条件的等价类，而结果是输出条件。

②　分析程序规格说明的描述中语义的内容，并将其表示成连接各个原因与各个结果的“因果图”。

③　标明约束条件。由于语法或环境的限制，有些原因和结果的组合情况是不可能出现的。为表明这些特定的情况，在因果图上使用若干个标准的符号标明约束条件。

④　把因果图转换成判定表。

⑤　为判定表中每一列表示的情况设计测试用例。

这样生成的测试用例（局部，组合关系下的）包括了所有输入数据的取“真”和取“假”的情况，构成的测试用例数据达到最少，且测试用例数据随输入数据数目的增加而增加。

2）用白盒法设计测试用例

白盒测试也称为结构测试，根据程序的内部结构和逻辑来设计测试用例，对程序的路径和过程进行测试，检查是否满足设计的需要。

白盒测试常用的技术是逻辑覆盖、循环覆盖和基本路径测试。

（1）逻辑覆盖。逻辑覆盖考察用测试数据运行被测程序时对程序逻辑的覆盖程度。主要的逻辑覆盖标准有语句覆盖、判定覆盖、条件覆盖、判定／条件覆盖、条件组合覆盖和路径覆盖6种。

①　语句覆盖。语句覆盖是指选择足够的测试数据，使被测试程序中每条语句至少执行一次。语句覆盖对程序执行逻辑的覆盖很低，因此一般认为它是很弱的逻辑覆盖。

②　判定覆盖。判定覆盖是指设计足够的测试用例，使得被测程序中每个判定表达式至少获得一次“真”值和“假”值，或者说是程序中的每一个取“真”分支和取“假”分支至少都通过一次，因此判定覆盖也称为分支覆盖。判定覆盖要比语句覆盖更强一些。

③　条件覆盖。条件覆盖是指构造一组测试用例，使得每一判定语句中每个逻辑条件的各种可能的值至少满足一次。

④　判定／条件覆盖。判定／条件覆盖是指设计足够的测试用例，使得判定中每个条件的所有可能取值（真／假）至少出现一次，并使每个判定本身的判定结果（真／假）也至少出现一次。

⑤　条件组合覆盖。条件组合覆盖是指设计足够的测试用例，使得每个判定中条件的各种可能值的组合都至少出现一次。满足条件组合覆盖的测试用例是一定满足判定覆盖、条件覆盖和判定／条件覆盖的。

⑥　路径覆盖。路径覆盖是指覆盖被测试程序中所有可能的路径。

（2）循环覆盖。执行足够的测试用例，使得循环中的每个条件都得到验证。

（3）基本路径测试。基本路径测试法是在程序控制流图的基础上，通过分析控制流图的环路复杂性，导出基本可执行路径集合，从而设计测试用例。设计出的测试用例要保证在测试中程序的每一条独立路径都执行过，即程序中的每条可执行语句至少执行一次。此外，所有条件语句的真值状态和假值状态都测试过。路径测试的起点是程序控制流图。程序控制流图中的节点代表包含一个或多个无分支的语句序列，边代表控制流。

白盒测试的原则如下。

（1）程序模块中的所有独立路径至少执行一次。

（2）在所有的逻辑判断中，取“真”和取“假”的两种情况至少都能执行一次。

（3）每个循环都应在边界条件和一般条件下各执行一次。

（4）测试程序内部数据结构的有效性等。

4.4.5　调试

调试发生在测试之后，其任务是根据测试时所发现的错误，找出原因和具体的位置，进行改正。调试工作主要由程序开发人员来进行，谁开发的程序就由谁来进行调试。

目前常用的调试方法有如下几种。

（1）试探法。调试人员分析错误的症状，猜测问题的所在位置，利用在程序中设置输出语句，分析寄存器、存储器的内容等手段来获得错误的线索，一步步地试探和分析出错误所在。这种方法效率很低，适合于结构比较简单的程序。

（2）回溯法。调试人员从发现错误症状的位置开始，人工沿着程序的控制流程往回跟踪代码，直到找出错误根源为止。这种方法适合于小型程序，对于大规模程序，由于其需要回溯的路径太多而变得不可操作。

（3）对分查找法。这种方法主要用来缩小错误的范围，如果已经知道程序中的变量在若干位置的正确取值，可以在这些位置上给这些变量以正确值，观察程序运行输出结果，如果没有发现问题，则说明从赋予变量一个正确值开始到输出结果之间的程序没有错误，问题可能在除此之外的程序中。否则错误就在所考察的这部分程序中，对含有错误的程序段再使用这种方法，直到把故障范围缩小到比较容易诊断为止。

（4）归纳法。归纳法就是从测试所暴露的问题出发，收集所有正确或不正确的数据，分析它们之间的关系，提出假想的错误原因，用这些数据来证明或反驳，从而查出错误所在。

（5）演绎法。根据测试结果，列出所有可能的错误原因。分析已有的数据，排除不可能和彼此矛盾的原因。对其余的原因，选择可能性最大的，利用已有的数据完善该假设，使假设更具体。用假设来解释所有的原始测试结果，如果能解释这一切，则假设得以证实，也就找出错误；否则，要么是假设不完备或不成立，要么有多个错误同时存在，需要重新分析，提出新的假设，直到发现错误为止。

4.4.6　系统文档

信息系统的文档，是系统建设过程的“痕迹”，是系统维护人员的指南，是开发人员与用户交流的工具。规范的文档意味着系统是按照工程化开发的，意味着信息系统的质量有了形式上的保障。文档的欠缺、文档的随意性和文档的不规范，极有可能导致原来的开发人员流动以后，系统不可维护、不可升级，变成了一个没有扩展性、没有生命力的系统。

信息系统的文档，不但包括应用软件开发过程中产生的文档，还包括硬件采购和网络设计中形成的文档；不但包括上述有一定格式要求的规范文档，也包括系统建设过程中的各种来往文件、会议纪要、会计单据等资料形成的不规范文档，后者是建设各方谈判甚至索赔的重要依据；不但包括系统实施记录，也包括程序资料和培训教程等。

文档在系统开发人员、项目管理人员、系统维护人员、系统评价人员以及用户之间的多种作用总结如下。

（1）用户与系统分析人员在系统规划和系统分析阶段通过文档进行沟通。这里的文档主要包括可行性研究报告、总体规划报告、系统开发合同和系统方案说明书等。有了文档，用户就能依次对系统分析员是否正确理解了系统的需求进行评价，如不正确，可以在已有文档的基础上进行修正。

（2）系统开发人员与项目管理人员通过文档在项目期内进行沟通。这里的文档主要有系统开发计划（包括工作任务分解表、PERT图、甘特图和预算分配表等）、系统开发月报以及系统开发总结报告等项目管理文件。有了这些文档，不同阶段之间的开发人员就可以进行工作的顺利衔接，同时还能降低因为人员流动带来的风险，因为接替人员可以根据文档理解前面人员的设计思路或开发思路。

（3）系统测试人员与系统开发人员通过文档进行沟通。系统测试人员可以根据系统方案说明书、系统开发合同、系统设计说明书和测试计划等文档对系统开发人员所开发的系统进行测试。系统测试人员再将评估结果撰写成系统测试报告。

（4）系统开发人员与用户在系统运行期间进行沟通。用户通过系统开发人员撰写的文档运行系统。这里的文档主要是用户手册和操作指南。

（5）系统开发人员与系统维护人员通过文档进行沟通。这里的文档主要有系统设计说明书和系统开发总结报告。有的开发总结报告写得很详细，分为研制报告、技术报告和技术手册三个文档，其中的技术手册记录了系统开发过程中的各种主要技术细节。这样，即使系统维护人员不是原来的开发人员，也可以在这些文档的基础上进行系统的维护与升级。

（6）用户与维修人员在运行维护期间进行沟通。用户在使用信息系统过程中，将运行过程中的问题进行记载，形成系统运行报告和维护修改建议。系统维护人员根据维护修改建议以及系统开发人员留下的技术手册等文档，对系统进行维护和升级。

4.4.7　系统转换

在进行新旧系统转换以前，首先要进行新系统的试运行。在系统测试、调试中，使用的是系统测试数据，有些实际运行中可能出现的问题，很难通过这些数据被发现。所以，一个系统开发后，让它实际运行一段时间，是对系统最好的检验和测试方法。

系统试运行阶段的主要工作如下。

（1）对系统进行初始化、输入各种原始数据记录。

（2）记录系统运行的数据和状况。

（3）核对新系统输出和旧系统（人工或计算机系统）输出的结果。

（4）对实际系统的输入方式进行考察（是否方便、效率如何、安全可靠性、误操作保护等）。

（5）对系统实际运行、响应速度（包括运算速度、传递速度、查询速度和输出速度等）进行实际测试。

新系统试运行成功之后，就可以在新系统和旧系统之间互相转换。新旧系统之间的转换方式有直接转换、并行转换和分段转换。

（1）直接转换。直接转换就是在确定新系统运行无误时，立刻启用新系统，终止旧系统运行。这种方式对人员、设备费用很节省。这种方式一般适用于一些处理过程不太复杂，数据不很重要的场合。

（2）并行转换。这种转换方式是新旧系统并行工作一段时间，经过一段时间的考验以后，新系统正式替代旧系统。对于较复杂的大型系统，它提供了一个与旧系统运行结果进行比较的机会，可以对新旧两个系统的时间要求、出错次数和工作效率给以公正的评价。当然，由于与旧系统并行工作，消除了尚未认识新系统之前的紧张和不安。在银行、财务和一些企业的核心系统中，这是一种经常使用的转换方式。它的主要特点是安全、可靠，但费用和工作量都很大，因为在相当长时间内系统要两套班子并行工作。

（3）分段转换。分段转换又称逐步转换、向导转换、试点过渡法等。这种转换方式实际上是以上两种转换方式的结合。在新系统全部正式运行前，一部分一部分地代替旧系统。那些在转换过程中还没有正式运行的部分，可以在一个模拟环境中继续试运行。这种方式既保证了可靠性，又不至于费用太大。但是，这种分段转换要求子系统之间有一定的独立性，对系统的设计和实现都有一定的要求，否则就无法实现这种分段转换的设想。

在实际工作中，转换方法较为灵活。一个信息系统从使用到成熟再到提高，是一个比较长的过程。只有遵循数据处理的阶段性，信息系统才能健康发展。现以一个连锁企业开始实施新系统为例。

（1）初始阶段。企业首先为应用系统做基本资料的准备，进行总部、“配送”核心系统的实施。

（2）推广阶段。总部、“配送”系统稳定后，先从1～2家门店试点开始，以门店核心模块为主，完成门店与总部的信息交换、物流过程。然后再逐步推广门店系统，直至完成所有门店的联网工作。

（3）控制阶段。所有门店联网完成后，进行准确、及时的基本数据采集和调整工作。

（4）集成阶段。考虑自动补货、自动配货、财务接口等高级模块应用的工作。

（5）管理阶段。进入数据的全面启用和介入管理决策。最后，系统步入成熟阶段。

实际上，每个企业在不同阶段的发展过程中，对具体问题的解决方法也是不同的，随时都会进行以上阶段的周期重复，随着每次重复时起点的不断升高，整个企业的数据应用水平也就随之逐步提高了。

4.5　系统运行和维护知识

4.5.1　系统维护概述

软件维护是软件生命周期中的最后一个阶段，处于系统投入生产性运行以后的时期中，因此不属于系统开发过程。软件维护是在软件已经交付使用之后，为了改正错误或满足新的需求而修改软件的过程，即软件在交付使用后对软件所做的一切改动。

1．系统可维护性概念

系统的可维护性可以定性地定义为：维护人员理解、改正、改动和改进这个软件的难易程度。提高可维护性是开发软件系统所有步骤的关键目的，系统是否能被很好地维护，可用系统的可维护性这一指标来衡量。

1）系统的可维护性的评价指标

（1）可理解性。指别人能理解系统的结构、界面、功能和内部过程的难易程度。模块化、详细设计文档、结构化设计和良好的高级程序设计语言等，都有助于提高可理解性。

（2）可测试性。诊断和测试的容易程度取决于易理解的程度。好的文档资料有利于诊断和测试，同时，程序的结构、高性能的测试工具以及周密计划的测试工序也是至关重要的。为此，开发人员在系统设计和编程阶段就应尽力把程序设计成易诊断和测试的。此外，在系统维护时，应该充分利用在系统测试阶段保存下来的测试用例。

（3）可修改性。诊断和测试的容易程度与系统设计所制定的设计原则有直接关系。模块的耦合、内聚、作用范围与控制范围的关系等，都对可修改性有影响。

2）维护与软件文档

文档是软件可维护性的决定因素。由于长期使用的大型软件系统在使用过程中必然会经受多次修改，所以文档显得非常重要。

软件系统的文档可以分为用户文档和系统文档两类。用户文档主要描述系统功能和使用方法，并不关心这些功能是怎样实现的；系统文档描述系统设计、实现和测试等各方面的内容。

可维护性是所有软件都应具有的基本特点，必须在开发阶段保证软件具有可维护的特点。在软件工程的每一个阶段都应考虑并提高软件的可维护性，在每个阶段结束前的技术审查和管理复查中，应该着重对可维护性进行复审。

在系统分析阶段的复审过程中，应该对将来要改进的部分和可能会修改的部分加以注解并指明，并且指出软件的可移植性问题以及可能影响软件维护的系统界面；在系统设计阶段的复审期间，应该从容易修改、模块化和功能独立的目的出发，评价软件的结构和过程；在系统实施阶段的复审期间，代码复审应该强调编码风格和内部说明文档这两个影响可维护性的因素。在完成了每项维护工作之后，都应该对软件维护本身进行认真的复审。

3）软件文档的修改

维护应该针对整个软件配置，不应该只修改源程序代码。如果对源程序代码的修改没有反映在设计文档或用户手册中，可能会产生严重的后果。每当对数据、软件结构、模块过程或任何其他有关的软件特点作了改动时，必须立即修改相应的技术文档。不能准确反映软件当前状态的设计文档可能比完全没有文档更坏。在以后的维护工作中很可能因文档不完全符合实际而不能正确理解软件，从而在维护中引入过多的错误。

2．系统维护的内容及类型

系统维护主要包括硬件设备的维护、应用软件的维护和数据的维护。

1）硬件维护

硬件的维护应由专职的硬件维护人员来负责，主要有两种类型的维护活动：一种是定期的设备保养性维护，保养周期可以是一周或一个月不等，维护的主要内容是进行例行的设备检查与保养，易耗品的更换与安装等；另一种是突发性的故障维护，即当设备出现突发性故障时，由专职的维修人员或请厂方的技术人员来排除故障，这种维修活动所花时间不能过长，以免影响系统的正常运行。

2）软件维护

软件维护主要是指根据需求变化或硬件环境的变化对应用程序进行部分或全部的修改。修改时应充分利用源程序，修改后要填写程序修改登记表，并在程序变更通知书上写明新旧程序的不同之处。

软件维护的内容一般有以下几个方面。

①　正确性维护。是指改正在系统开发阶段已发生而系统测试阶段尚未发现的错误。这方面的维护工作量要占整个维护工作量的17％～21％。所发现的错误有的不太重要，不影响系统的正常运行，其维护工作可随时进行；而有的错误非常重要，甚至影响整个系统的正常运行，其维护工作必须制定计划，进行修改，并且要进行复查和控制。

②　适应性维护。是指使应用软件适应信息技术变化和管理需求变化而进行的修改。这方面的维护工作量占整个维护工作量的18％～25％。由于目前计算机硬件价格的不断下降，各类系统软件层出不穷，人们常常为改善系统硬件环境和运行环境而产生系统更新换代的需求；企业的外部市场环境和管理需求的不断变化也使得各级管理人员不断提出新的信息需求。这些因素都将导致适应性维护工作的产生。进行这方面的维护工作也要像系统开发一样，有计划、有步骤地进行。

③　完善性维护。这是为扩充功能和改善性能而进行的修改，主要是指对已有的软件系统增加一些在系统分析和设计阶段中没有规定的功能与性能特征。这些功能对完善系统功能是非常必要的。另外，还包括对处理效率和编写程序的改进，这方面的维护占整个维护工作的50％～60％，比重较大，也是关系到系统开发质量的重要方面。这方面的维护除了要有计划、有步骤地完成外，还要注意将相关的文档资料加入到前面相应的文档中去。

④　预防性维护。为了改进应用软件的可靠性和可维护性，为了适应未来的软硬件环境的变化，应主动增加预防性的新的功能，以使应用系统适应各类变化而不被淘汰。例如将专用报表功能改成通用报表生成功能，以适应将来报表格式的变化。这方面的维护工作量占整个维护工作量的4％左右。

3）数据维护

数据维护工作主要是由数据库管理员来负责，主要负责数据库的安全性和完整性以及进行并发性控制。数据库管理员还要负责维护数据库中的数据，当数据库中的数据类型、长度等发生变化时，或者需要添加某个数据项、数据库时，要负责修改相关的数据库、数据字典，并通知有关人员。另外，数据库管理员还要负责定期出版数据字典文件及一些其他数据管理文件，以保留系统运行和修改的轨迹。当系统出现硬件故障并得到排除后，要负责数据库的恢复工作。

数据维护中还有一项很重要的内容，那就是代码维护。不过代码维护发生的频率相对较小。代码的维护应由代码管理小组进行。变更代码应经过详细讨论，确定之后要用书面形式贯彻。代码维护的困难往往不在于代码本身的变更，而在于新代码的贯彻。为此，除了成立专门的代码管理小组外，各业务部门要指定专人进行代码管理，通过他们贯彻使用新代码。这样做的目的是要明确管理职责，有助于防止和更正错误。

3．系统维护的管理和步骤

要强调的是，系统的修改往往会“牵一发而动全身”。程序、文件、代码的局部修改都可能影响系统的其他部分。因此，系统的维护工作应有计划有步骤地统筹安排，按照维护任务的工作范围、严重程度等诸多因素确定优先顺序，制定出合理的维护计划，然后通过一定的批准手续实施对系统的修改和维护。

通常对系统的维护应执行以下步骤。

（1）提出维护或修改要求。操作人员或业务领导用书面形式向系统维护工作的主管人员提出对某项工作的修改要求。这种修改要求一般不能直接向程序员提出。

（2）领导审查并做出答复，如同意修改则列入维护计划。系统主管人员进行一定的调查后，根据系统的情况和工作人员的情况，考虑这种修改是否必要、是否可行，做出是否修改、何时修改的答复。如果需要修改，则根据优先程度的不同列入系统维护计划。计划的内容应包括维护工作的范围、所需资源、确认的需求、维护费用、维护进度安排以及验收标准等。

（3）领导分配任务，维护人员执行修改。系统主管人员按照计划向有关的维护人员下达任务，说明修改的内容、要求和期限。维护人员在仔细了解原系统的设计和开发思路的情况下对系统进行修改。

（4）验收维护成果并登记修改信息。系统主管人员组织技术人员对修改部分进行测试和验收。验收通过后，将修改的部分嵌入系统，取代旧的部分。维护人员登记所做的修改，更新相关的文档，并将新系统作为新的版本通报用户和操作人员，指明新的功能和修改的地方。

在进行系统维护过程中，还要注意维护的副作用。维护的副作用包括两个方面：一是修改程序代码有时会发生灾难性的错误，造成原来运行比较正常的系统变得不能正常运行。为了避免这类错误，要在修改工作完成后进行测试，直至确认和复查无错为止。二是修改数据库中数据的副作用，当一些数据库中的数据发生变化时可能导致某些应用软件不再适应这些已经变化了的数据而产生错误。为了避免这类错误，一是要有严格的数据描述文件，即数据字典系统；二是要严格记录这些修改并进行修改后的测试工作。

总之，系统维护工作是信息系统运行阶段的重要工作内容，必须予以充分的重视。维护工作做得越好，信息系统的作用才能够得以充分发挥，信息系统的寿命也就越长。

4.5.2　系统评价

1．系统评价概述

信息系统的评价分为广义和狭义两种。广义的信息系统评价是指从系统开发的一开始到结束的每一阶段都需要进行评价。狭义的信息系统评价则是指在系统建成并投入运行之后所进行的全面、综合的评价。

按评价的时间与信息系统所处的阶段的关系，又可从总体上把广义的信息系统评价分成立项评价、中期评价和结项评价。

（1）立项评价。指信息系统方案在系统开发前的预评价，即系统规划阶段中的可行性研究。评价的目的是决定是否立项进行开发，评价的内容是分析当前开发新系统的条件是否具备，明确新系统目标实现的重要性和可能性，主要包括技术上的可行性、经济上的可行性、管理上的可行性和开发环境的可行性等方面。由于事前评价所用的参数大都是不确定的，所以评价的结论具有一定的风险性。

（2）中期评价。项目中期评价包含两种含义，一是指项目方案在实施过程中，因外部环境出现重大变化，例如市场需求变化、竞争性技术或更完美的替代系统的出现，或者发现原先设计有重大失误等，需要对项目的方案进行重新评估，以决定是继续执行还是终止该方案；另一种含义也可称为阶段评估，是指在信息系统开发正常情况下，对系统设计、系统分析、系统实施阶段的阶段性成果进行评估。由于一般都将阶段性成果的提交视为信息系统建设的里程碑，所以，阶段评估又可叫里程碑式评价。

（3）结项评价。信息系统的建设是一个项目，是项目就需要有终结时间。结项评价是指项目准备结束时对系统的评价，一般是指在信息系统投入正式运行以后，为了了解系统是否达到预期的目的和要求而对系统运行的实际效果进行的综合评价。所以，结项评价又是狭义的信息系统评价。信息系统项目的鉴定是结项评价的一种正规的形式。结项评价的主要内容包括系统性能评价、系统的经济效益评价以及企业管理效率提高、管理水平改善、管理人员劳动强度减轻等间接效果。通过结项评价，用户可以了解系统的质量和效果，检查系统是否符合预期的目的和要求；开发人员可以总结开发工作的经验、教训，这对今后的工作十分有益。

在对信息系统进行评价考核时，应该注意以下几个问题。

（1）信息系统通过基本资料录入、进货、订货、盘点和零售等各个环节采集进来，其中任意一个环节的数据录入出现问题，都将导致最终报表的不准确，而报表不准确就意味着企业决策者无法根据报表决定企业的运作，更谈不上数据分析和决策支持了。这也是目前大部分使用了信息系统的企业普遍存在的问题。究竟是什么原因导致了数据采集的不准确呢？一些企业错误地将数据准确性作为考核信息部的一个指标，其实数据不准确更多源于管理上存在的问题，正因为数据源头非常多，所造成的数据不一致的问题不是信息部都能解决的，最终数据采集的成败将由最高管理层对数据采集各环节的管理力度所决定。而数据采集的成败又将最终决定整个信息系统应用的成败。

（2）信息系统并不是万能系统。系统应用的过程中，有些问题是信息系统擅长解决的，如大量的、重复的、规范性的事务处理；而有些问题是信息系统不擅长解决的，如特殊的、偶然的、不规范的经营管理内容。让信息系统做不擅长的工作，势必在应用的过程中，投入的管理成本远远大于它所产生的效益。对这种灵活、多变的情况，不妨采用人工处理或通过制度的限制，尽量避免不规范的行为频繁发生，从而真正实现企业简单复制、快速扩张、规模效益的目的。

2．系统评价的指标

从以下几方面综合考虑，建立起一套指标体系理论框架。

（1）从信息系统的组成部分出发，信息系统是一个由人机共同组成的系统，所以可以按照运行效果和用户需求（人）、系统质量和技术条件（机）这两条线索构造指标。

（2）从信息系统的评价对象出发，对于开发方来说，他们所关心的是系统质量和技术水平；对于用户方而言，关心的是用户需求和运行质量；系统外部环境则主要通过社会效益指标来反映。

（3）从经济学角度出发，分别按系统成本、系统效益和财务指标三条线索建立指标。

第5章　网络基础知识

计算机网络是由多台计算机组成的系统，与传统的单机系统、多机系统相比有很大的区别。计算机网络的结构、功能、组成以及实现技术更复杂，维护起来难度更大。本章将简要介绍计算机网络的体系结构、网络应用、和网络互连设备、网络构建与网络安全方面的基本内容，涉及到与网络有关的软硬件和应用知识。

5.1　网络概述

由于对信息共享和信息传递应用的迫切需求，计算机应用日益渗透到各行各业。计算机已不仅仅是一种计算的工具，而是更多地应用于信息的收集、处理和传输。计算机参与社会信息化的进程，促进了信息产业的发展。而应用需求又推动了计算机技术的不断更新，越来越多的领域急需计算机在一定范围内“联合起来”，进行群体工作。在此环境下，计算机网络应运而生了。计算机网络是计算机技术与通信技术相结合的产物，它实现了远程通信、远程信息处理和资源共享。经过几十年的发展，计算机网络已由早期的“终端—计算机网”、“计算机—计算机网”成为现代具有统一网络体系结构的计算机网络。

5.1.1　计算机网络的概念

1．计算机网络的发展

计算机网络是计算机技术与通信技术日益发展和密切结合的产物，它的发展过程大致可以划分为如下4个阶段。

1）具有通信功能的单机系统

该系统又称终端—计算机网络，是早期计算机网络的主要形式。它将一台计算机经通信线路与若干终端直接相连。美国于20世纪50年代建立的半自动地面防空系统SAGE就属于这一类网络。它把远距离的雷达和其他测量控制设备的信息通过通信线路送到一台旋风型计算机上进行处理和控制，首次实现了计算机技术与通信技术的结合。

2）具有通信功能的多机系统

对终端—计算机网进行改进：在主计算机的外围增加了一台计算机，专门用于处理终端的通信信息及控制通信线路，并能对用户的作业进行某些预处理操作，这台计算机称为“前端处理机”或“通信控制处理机”。在终端设备较集中的地方设置一台集中器，终端通过低速线路先汇集到集中器上，然后再用高速线路将集中器连到主机上。这就形成了多机系统。

3）以共享资源为目的的计算机网络

具有通信功能的多机系统是计算机—计算机网络，它是由若干台计算机互联的系统，即利用通信线路将多台计算机连接起来，在计算机之间进行通信。该网络有两种结构形式：一种形式是主计算机通过通信线路直接互连的结构，其中主计算机同时承担数据处理和通信工作；另一种形式是通过通信控制处理机间接地把各主计算机连接的结构，其中通信处理机和主计算机分工，前者负责网络上各主计算机间的通信处理和控制，后者是网络资源的拥有者，负责数据处理，它们共同组成资源共享的计算机网络。20世纪70年代，美国国防部高级研究计划局所研制的ARPANET是计算机—计算机网络的典型代表。最初该网仅由4台计算机连接而成，到1975年，已连接100多台不同型号的大型计算机。ARPANET成为第一个完善地实现分布式资源共享的网络，为计算机网络的发展奠定了基础。

在这期间，国际标准化组织（ISO）提出了开放系统互连参考模型OSI/RM（Open System Interconnection Refernce Model）。该模型定义了异种机联网所应遵循的框架结构。OSI/RM很快得到了国际上的认可，并为许多厂商所接受。由此使计算机网络的发展进入了新的阶段。

4）以局域网及因特网为支撑环境的分布式计算机系统

局域网是继远程网之后发展起来的，它继承了远程网的分组交换技术和计算机的I/O总线结构技术。局域网的发展也促使计算机网络的模式发生了变革，即由早期的以大型机为中心的集中式模式转变为由微机构成的分布式计算机模式。

计算机网络的定义随网络技术的更新可从不同的角度给予描述。目前人们已公认的有关计算机网络的定义是利用通信设备和线路将地理位置分散的、功能独立的自主计算机系统或由计算机控制的外部设备连接起来，在网络操作系统的控制下，按照约定的通信协议进行信息交换，实现资源共享的系统。

定义中涉及的“资源”应该包括硬件资源（CPU、大容量的磁盘、光盘以及打印机等）和软件资源（语言编译器、文本编辑器、各种软件工具和应用程序等）。

2．计算机网络的功能

计算机网络提供的主要功能有如下。

（1）数据通信。通信或数据传输是计算机网络主要功能之一，用以在计算机系统之间传送各种信息。利用该功能，地理位置分散的生产单位和业务部门可通过计算机网络连接在一起进行集中控制和管理。也可以通过计算机网络传送电子邮件，发布新闻消息及进行电子数据交换，极大地方便了用户，提高了工作效率。

（2）资源共享。资源共享是计算机网络最有吸引力的功能。通过资源共享，可使网络中分散在异地的各种资源互通有无，分工协作，从而大大提高系统资源的利用率。资源共享包括软件资源共享和硬件资源共享。

（3）负载均衡。在计算机网络中可进行数据的集中处理或分布式处理，一方面可以通过计算机网络将不同地点的主机或外设采集到的数据信息送往一台指定的计算机，在此计算机上对数据进行集中和综合处理，通过网络在各计算机之间传送原始数据和计算结果；另一方面，当网络中某台计算机任务过重时，可将任务分派给其他空闲的多台计算机，使多台计算机相互协作，均衡负载，共同完成任务。

（4）高可靠性。指在计算机网络中的各台计算机可以通过网络彼此互为后备机，一旦某台计算机出现故障，故障机的任务就可由其他计算机代为处理，从而提高系统的可靠性。避免了单机无后备使用的情况下，计算机出现故障而导致系统瘫痪的现象，从而大大提高了系统的可靠性。

借助于计算机网络，在各种功能软件的支持下，人类可以进行高速的异地电子信息交换，并获得了多种服务，如新闻浏览和信息检索、传送电子邮件、多媒体电信服务、远程教育、网上营销、网上娱乐和远程医疗诊断等。

计算机网络按照数据通信和数据处理的功能，可分为两层：内层通信子网和外层资源子网，如图5-1所示。通信子网（图中虚线内）的节点计算机和高速通信线路组成独立的数据系统，承担全网的数据传输、交换、加工和变换等通信处理工作，即将一台计算机的输出信息传送给另一台计算机。资源子网（图中点划线内虚线外）包括计算机、终端、通信子网接口设备、外部设备（如打印机、磁带机和绘图机等）及各种软件资源等，它负责全网的数据处理和向网络用户提供网络资源及网络服务。

图5-1　通信子网和资源子网关系图

H—主计算机；T—终端；TIP—集线器

通信子网和资源子网的划分，完全符合国际标准化组织所制定的开放式系统互连参考模型（OSI）的思想。其中，通信子网对应于OSI中的低三层（物理层、数据链路层、网络层），而资源子网对应于OSI中的高三层（会话层、表示层、应用层）。这种划分将通信子网的任务从主机中抽取出来，由通信子网中的设备专门解决数据传输和通信控制问题。而资源子网中的计算机可集中精力处理数据，从而提高主机效率和网络的整体性能。

5.1.2　计算机网络的分类

计算机网络的分类方式很多，按照不同的分类原则，可以得到各种不同类型的计算机网络。例如，按通信距离可分为广域网、局域网和城域网；按信息交换方式可分为电路交换网、分组交换网和综合交换网；按网络拓扑结构可分为星型网、树型网、环型网和总线网；按通信介质可分为双绞线网、同轴电缆网、光纤网和卫星网等；按传输带宽可分为基带网和宽带网；按使用范围可分为公用网和专用网；按速率可分为高速网、中速网和低速网；按通信传播方式可分为广播式和点到点式。

这里主要介绍根据计算机网络的覆盖范围和通信终端之间相隔的距离不同将其分为局域网、城域网和广域网三类的情况，各类网络的特征参数如表5-1所示。

表5-1　各类网络的特征参数

1．局域网

局域网（Local Area Network, LAN）是指传输距离有限，传输速度较高，以共享网络资源为目的的网络系统。由于局域网投资规模较小，网络实现简单，故新技术易于推广。局域网技术与广域网相比发展迅速。局域网的特点如下：

（1）分布范围有限。加入局域网中的计算机通常处在几千米的距离之内。通常它分布在一个学校、一个企业单位，为本单位使用。一般称为“园区网”或“校园网”。

（2）有较高的通信带宽，数据传输率高。一般为1Mb/s以上，最高已达1000Mb/s。

（3）数据传输可靠，误码率低。误码率一般为10-4
～10-6
。

（4）通常采用同轴电缆或双绞线作为传输介质。跨楼寓时使用光纤。

（5）拓扑结构简单简洁，大多采用总线、星型和环型等，系统容易配置和管理。网上的计算机一般采用多路控制访问技术或令牌技术访问信道。

（6）网络的控制一般趋向于分布式，从而减少了对某个节点的依赖性，避免并减小了一个节点故障对整个网络的影响。

（7）通常网络归单一组织所拥有和使用。不受任何公共网络管理机构的规定约束，容易进行设备的更新和新技术的引用，以不断增强网络功能。

2．城域网

城域网（Metropolitan Area Network, MAN）是规模介于局域网和广域网之间的一种较大范围的高速网络，一般覆盖临近的多个单位和城市，从而为接入网络的企业、机关、公司及社会单位提供文字、声音和图像的集成服务。城域网规范由IEEE802.6协议定义。

3．广域网

广域网（Wide Area Network, WAN）又称远程网，它是指覆盖范围广、传输速率相对较低、以数据通信为主要目的的数据通信网。广域网最根本的特点如下。

（1）分布范围广。加入广域网中的计算机通常处在从数公里到数千公里的地方。因此，网络所涉及的范围可为市、地区、省、国家乃至世界。

（2）数据传输率低。一般为几十兆位每秒以下。

（3）数据传输可靠性随着传输介质的不同而不同，若用光纤，误码率一般在10-6
～10-11
之间。

（4）广域网常常借用传统的公共传输网来实现，因为单独建造一个广域网极其昂贵。

（5）拓扑结构较为复杂，大多采用“分布式网络”，即所有计算机都与交换节点相连，从而实现网络中任何两台计算机都可以进行通信。

广域网的布局不规则，使得网络的通信控制比较复杂。尤其是使用公共传输网，要求连接到网上的任何用户都必须严格遵守各种标准和规程。设备的更新和新技术的引用难度较大。广域网可将一个集团公司、团体或一个行业的各处部门和子公司连接起来。这种网络一般要求兼容多种网络系统（异构网络）。

5.1.3　网络的拓扑结构

网络拓扑结构是指网络中通信线路和节点的几何排序，用以表示整个网络的结构外貌，反映各节点之间的结构关系。它影响着整个网络的设计、功能、可靠性和通信费用等重要方面，是计算机网络十分重要的要素。常用的网络拓扑结构有总线型、星型、环型、树型和分布式结构等。

1．总线型结构

总线型拓扑结构如图5-2（a）所示，其特点为总线型拓扑结构中只有一条双向通路，便于进行广播式传送信息；总线型拓扑结构属于分布式控制，无须中央处理器，故结构比较简单；节点的增、删和位置的变动较容易，变动中不影响网络的正常运行，系统扩充性能好；节点的接口通常采用无源线路，系统可靠性高；设备少，价格低，安装使用方便；由于电气信号通路多，干扰较大，因此对信号的质量要求高。负载重时，线路的利用率较低。网上的信息延迟时间不确定，故障隔离和检测困难。

2．星型结构

星型结构中，使用中央交换单元以放射状连接到网中的各个节点，如图5-2（b）所示。中央单元采用电路交换方式以建立所希望通信的两节点间专用的路径。通常用双绞线将节点与中央单元进行连接。其特点为维护管理容易，重新配置灵活；故障隔离和检测容易；网络延迟时间短；各节点与中央交换单元直接连通，各节点之间通信必须经过中央单元转换；网络共享能力差；线路利用率低，中央单元负荷重。

3．环型结构

环型结构的信息传输线路构成一个封闭的环型，各节点通过中继器连入网内，各中继器间首尾相接，如图5-2（c）所示。信息单向沿环路逐点传送。其特点为环型网中信息的流动方向是固定的，两个节点仅有一条通路，路径控制简单；有旁路设备，节点一旦发生故障，系统自动旁路，可靠性高；信息要串行穿过多个节点，在网中节点过多时传输效率低，系统响应速度慢；由于环路封闭，扩充较难。

4．树型结构

树型结构是总线型结构的扩充形式，传输介质是不封闭的分支电缆，如图5-2（d）所示。它主要用于多个网络组成的分级结构中。其特点同总线型网。

5．分布式结构

分布式结构无严格的布点规定和形状，各节点之间有多条线路相连，如图5-2（e）所示。其特点为分布式网有较高的可靠性，当一条线路有故障时，不会影响整个系统工作；资源共享方便，网络响应时间短；由于节点与多个节点连接，故节点的路由选择和流量控制难度大，管理软件复杂；硬件成本高。

图5-2　常用的网络拓扑结构

广域网与局域网所使用的网络拓扑结构有所不同，广域网多用分布式或树型结构，而局域网常使用总线型、环型、星型或树型结构。

5.2　ISO/OSI网络体系结构

计算机网络是相当复杂的系统，相互通信的两个计算机系统必须高度协调才能正常工作。为了设计这样复杂的计算机网络，人们提出了将网络分层的方法。分层可将庞大而复杂的问题转化为若干较小的局部问题进行处理，从而使问题简单化。

国际标准化组织在1977年成立了一个分委员会专门研究网络通信的体系结构问题，并提出了开放系统互连参考模型，它是一个定义异种计算机连接标准的框架结构。OSI为连接分布式应用处理的“开放”系统提供了基础。所谓“开放”，是指任何两个系统只要遵守参考模型和有关标准，都能够进行互连。OSI采用了层次化结构的构造技术。

ISO分委员会的任务是定义一组层次和每一层所完成的功能和服务。层次的划分应当从逻辑上将功能分组，层次应该足够多，应使每一层小到易于管理的程度，但也不能太多，否则汇集各层的处理开销太大。

1．ISO/OSI参考模型

IOS/OSI的参考模型共有7层，如图5-3所示。由低层至高层分别为物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。

图5-3　OSI参考模型

OSI参考模型具有以下特性。

（1）是一种将异构系统互连的分层结构。

（2）提供了控制互连系统交互规则的标准框架。

（3）定义了一种抽象结构，而并非具体实现的描述。

（4）不同系统上相同层的实体称为同等层实体。

（5）同等层实体之间的通信由该层的协议管理。

（6）相邻层间的接口定义了原语操作和低层向高层提供的服务。

（7）所提供的公共服务是面向连接的或无连接的数据服务。

（8）直接的数据传送仅在最低层实现。

（9）每层完成所定义的功能，修改本层的功能并不影响其他层。

OSI参考模型各层的功能简述如下。

OSI/RM中的1～3层主要负责通信功能，一般称为通信子网层。上三层（即5～7层）属于资源子网的功能范畴，称为资源子网层。传输层起着衔接上下三层的作用。各层说明如下。

（1）物理层（Physical Layer）。提供为建立、维护和拆除物理链路所需的机械、电气、功能和规程的特性；提供有关在传输介质上传输非结构的位流及物理链路故障检测指示。

用户要传递信息就要利用一些物理媒体，如双绞线、同轴电缆等，但具体的物理媒体并不在OSI的7层之内，有人把物理媒体当作第0层，物理层的任务就是为它的上一层提供一个物理连接，以及它们的机械、电气、功能和过程特性。如规定使用电缆和接头的类型，传送信号的电压等。在这一层，数据还没有被组织，仅作为原始的位流或电气电压处理，单位是位。

（2）数据链路层（Data Link Layer）。数据链路层负责在两个相邻节点间的线路上无差错地传送以帧为单位的数据，并进行流量控制。每一帧包括一定数量的数据和一些必要的控制信息。和物理层相似，数据链路层要负责建立、维持和释放数据链路的连接。在传送数据时，如果接收点检测到所传数据中有差错，就要通知发方重发这一帧。

（3）网络层（Network Layer）。为传输层实体提供端到端的交换网络数据传送功能，使得传输层摆脱路由选择、交换方式和拥挤控制等网络传输细节；可以为传输层实体建立、维持和拆除一条或多条通信路径；对网络传输中发生的不可恢复的差错予以报告。

在计算机网络中进行通信的两个计算机之间可能会经过很多个数据链路，也可能还要经过很多通信子网。网络层的任务就是选择合适的网间路由和交换节点，确保数据及时传送。网络层将数据链路层提供的帧组成数据包，包中封装有网络层包头，其中含有逻辑地址信息——源站点和目的站点地址的网络地址。

（4）传输层（Transport Layer）。为会话层实体提供透明、可靠的数据传输服务，保证端到端的数据完整性；选择网络层能提供最适宜的服务；提供建立、维护和拆除传输连接功能。传输层根据通信子网的特性，最佳地利用网络资源，为两个端系统（也就是源站和目的站）的会话层之间提供建立、维护和取消传输连接的功能，并以可靠和经济的方式传输数据。在这一层，信息的传送单位是报文。

（5）会话层（Session Layer）。为彼此合作的表示层实体提供建立、维护和结束会话连接的功能；完成通信进程的逻辑名字与物理名字间的对应；提供会话管理服务。

这一层也可以称为会晤层或对话层，在会话层及以上的高层次中，数据传送的单位不再另外命名，统称为报文。会话层不参与具体的传输，它提供包括访问验证和会话管理在内的建立和维护应用之间通信的机制。如服务器验证用户登录便是由会话层完成的。

（6）表示层（Presentation Layer）。为应用层进程提供能解释所交换信息含义的一组服务，即将欲交换的数据从适合于某一用户的抽象语法转换为适合于OSI系统内部使用的传送语法。提供格式化的表示和转换数据服务。数据的压缩、解压缩、加密和解密等工作都由表示层负责。

（7）应用层（Application Layer）。提供OSI用户服务，即确定进程之间通信的性质，以满足用户需要以及提供网络与用户应用软件之间的接口服务。例如，事务处理程序、电子邮件和网络管理程序等。

2．参考模型的信息流向

如图5-4所示，设A系统的用户要向B系统的用户传送数据。A系统用户的数据先送入应用层。该层给它附加控制信息AH（头标）后，送入表示层。表示层对数据进行必要的变换并加头标PH后送入会话层。会话层也加头标SH送入传输层。传输层将长报文分段后并加头标TH送至网络层。网络层将信息变成报文分组，并加组号NH送数据链路层。数据链路层将信息加上头标和尾标（DH及DT）变成帧，经物理层按位发送到对方（B系统）。B系统接收到信息后，按照与A系统相反的动作，层层剥去控制信息，最后把原数据传送给B系统的用户可见，两系统中只有物理层是实通信，而其余各层均为虚通信。因此，图5-4中只有两物理层间有物理连接，其余各层间均无连线。

图5-4　ISO/OSI RM内信息流动

5.3　网络互连硬件

构建一个实际的网络，需要网络的传输介质、网络互联设备作为支持。本节主要介绍构建网络的传输介质和互联设备。

5.3.1　网络的设备

网络互连的目的是使一个网络的用户能访问其他网络的资源，使不同网络上的用户能够互相通信和交换信息，实现更大范围的资源共享。在网络互连时，一般不能简单地直接相连，而是通过一个中间设备来实现。按照ISO/OSI的分层原则，这个中间设备要实现不同网络之间的协议转换功能，根据它们工作的协议层不同进行分类。网络互连设备可以有中继器（实现物理层协议转换，在电缆间转发二进制信号）、网桥（实现物理层和数据链路层协议转换）、路由器（实现网络层和以下各层协议转换）、网关（提供从最低层到传输层或以上各层的协议转换）和交换机等。

1．网络传输介质互联设备

网络线路与用户节点具体衔接时，需要网络传输介质的互联设备。如T型头（细同轴电缆连接器）、收发器、RJ-45（屏蔽或非屏蔽双绞线连接器）、RS232接口（目前计算机与线路接口的常用方式）、DB-15接口（连接网络接口卡的AUI接口）、VB35同步接口（连接远程的高速同步接口）、网络接口单元和调制解调器（数字信号与模拟信号转换器）等。

2．物理层的互连设备

物理层的互连设备有中继器（Repeater）和集线器（Hub）。

1）中继器

它是在物理层上实现局域网网段互连的，用于扩展局域网网段的长度。由于中继器只在两个局域网网段间实现电气信号的恢复与整形，因此它仅用于连接相同的局域段。

理论上说，可以用中继器把网络延长到任意长的传输距离，但是，局域网中接入的中继器的数量将受时延和衰耗的影响，因而必须加以限制。例如，在以太网中最多使用4个中继器。以太网设计连线时指定两个最远用户之间的距离，包括用于局域网的连接电缆，不得超过500m。即便使用了中继器，典型的Ethernet局域网应用要求从头到尾整个路径不超过1500m。中继器的主要优点是安装简便、使用方便、价格便宜。

2）集线器

可以看成是一种特殊的多路中继器，也具有信号放大功能。使用双绞线的以太网多用Hub扩大网络，同时也便于网络的维护。以集线器为中心的网络优点是当网络系统中某条线路或某节点出现故障时，不会影响网上其他节点的正常工作。集线器可分为无源（passive）集线器、有源（active）集线器和智能（intelligent）集线器。

无源集线器只负责把多段介质连接在一起，不对信号作任何处理，每一种介质段只允许扩展到最大有效距离的一半；有源集线器类似于无源集线器，但它具有对传输信号进行再生和放大从而扩展介质长度的功能；智能集线器除具有有源集线器的功能外，还可将网络的部分功能集成到集线器中，如网络管理、选择网络传输线路等。

3．数据链路层的互连设备

数据链路层的互连设备有网桥（Bridge）和交换机（Switch）。

1）网桥

用于连接两个局域网网段，工作于数据链路层。网桥要分析帧地址字段，以决定是否把收到的帧转发到另一个网络段上。确切地说，网桥工作于MAC子层，只要两个网络MAC子层以上的协议相同，都可以用网桥互连。

网桥检查帧的源地址和目的地址，如果目的地址和源地址不在同一个网络段上，就把帧转发到另一个网络段上；若两个地址在同一个网络段上，则不转发，所以网桥能起到过滤帧的作用。网桥的帧过滤特性很有用，当一个网络由于负载很重而性能下降时，可以用网桥把它分成两个网络段并使得段间的通信量保持最小。例如，把分布在两层楼上的网络分成每层一个网络段，段中间用网桥相连，这样的配置可以最大限度地缓解网络通信繁忙的程度，提高通信效率。同时，由于网桥的隔离作用，一个网络段上的故障不会影响到另一个网络段，从而提高了网络的可靠性。

2）交换机

交换机是一个具有简化、低价、高性能和高端口密集特点的交换产品，它是按每一个包中的MAC地址相对简单地决策信息转发，而这种转发决策一般不考虑包中隐藏的更深的其他信息。交换机转发数据的延迟很小，操作接近单个局域网性能，远远超过了普通桥接的转发性能。交换技术允许共享型和专用型的局域网段进行带宽调整，以减轻局域网之间信息流通出现的瓶颈问题。

交换机的工作过程为：当交换机从某一节点收到一个以太网帧后，将立即在其内存中的地址表（端口号—MAC地址）进行查找，以确认该目的MAC的网卡连接在哪一个节点上，然后将该帧转发至该节点。如果在地址表中没有找到该MAC地址，也就是说，该目的MAC地址是首次出现，交换机就将数据包广播到所有节点。拥有该MAC地址的网卡在接收到该广播帧后，将立即做出应答，从而使交换机将其节点的“MAC地址”添加到MAC地址表中。

交换机的三种交换技术：端口交换（用于将以太模块的端口在背板的多个网段之间进行分配、平衡）、帧交换（处理方式：直通交换——提供线速处理能力，交换机只读出网络帧的前14个字节，便将网络帧传送到相应的端口上；存储转发——通过对网络帧的读取进行验错和控制；碎片丢弃——检查数据包的长度是否够64个字节，如果小于64字节，说明是假包，则丢弃该包，否则发送该包）和信元交换（采用长度固定的信元交换）。

4．网络层互联设备

路由器（Router）是网络层互联设备，用于连接多个逻辑上分开的网络。逻辑网络是指一个单独的网络或一个子网，当数据从一个子网传输到另一个子网时，可通过路由器来完成。

路由器具有很强的异种网互连能力，互连的网络最低两层协议可以互不相同，通过驱动软件接口到第三层上而得到统一。对于互连网络的第三层协议，如果相同，可使用单协议路由器进行互连；如果不同，则应使用多协议路由器。多协议路由器同时支持多种不同的网络层协议，并可以设置为允许或禁止某些特定的协议。所谓支持多种协议，是指支持多种协议的路由，而不是指不同类协议的相互转换。

通常把网络层地址信息叫做网络逻辑地址，把数据链路层地址信息叫做物理地址。路由器最主要的功能是选择路径。在路由器的存储器中维护着一个路径表，记录各个网络的逻辑地址，用于识别其他网络。在互连网络中，当路由器收到从一个网络向另一个网络发送的信息包时，将丢弃信息包的外层，解读信息包中的数据，获得目的网络的逻辑地址，使用复杂的程序来决定信息经由哪条路径发送最合适，然后重新打包并转发出去。路由器的功能还包括过滤、存储转发、流量管理和介质转换等。一些增强功能的路由器还可有加密、数据压缩、优先和容错管理等功能。由于路由器工作于网络层，它处理的信息量比网桥要多，因而处理速度比网桥慢。

5．应用层互联设备

网关（Gateway）是应用层的互联设备。在一个计算机网络中，当连接不同类型而协议差别又较大的网络时，则要选用网关设备。网关的功能体现在OSI模型的最高层，它将协议进行转换，将数据重新分组，以便在两个不同类型的网络系统之间进行通信。由于协议转换是一件复杂的事，一般来说，网关只进行一对一转换，或是少数几种特定应用协议的转换，网关很难实现通用的协议转换。

5.3.2　网络的传输介质

传输介质是信号传输的媒体，常用的介质分为有线介质和无线介质。有线介质有双绞线、同轴电缆和光纤等；无线介质有微波、红外线和微波等。

1．有线介质

1）双绞线（Twisted—Pair）

双绞线是现在最普通的传输介质，它分为屏蔽双绞线（STP）和非屏蔽双绞线（UTP）。非屏蔽双绞线有线缆外皮作为屏蔽层，适用于网络流量不大的场合中。屏蔽式双绞线具有一个金属甲套，对电磁干扰具有较强的抵抗能力，适用于网络流量较大的高速网络协议应用。双绞线又可分为3类、4类和5类、6类和7类双绞线，现在常用的是5类UTP，其频率带宽为100MHz。6类、7类双绞线分别可工作于200MHz和600MHz的频率带宽之上，且采用特殊设计的RJ45插头。

双绞线最多应用于10Base-T和100Base-T的以太网中，具体规定有：一段双绞线的最大长度为100m，只能连接一台计算机；双绞线的每端需要一个RJ45插件；各段双绞线通过集线器互联，利用双绞线最多可连接64个站点到中继器。

2）同轴电缆（Coaxial）

同轴电缆也像双绞线那样由一对导体组成。同轴电缆又分为基带同轴电缆（阻抗为50Ω）和宽带同轴电缆（阻抗为75Ω）。基带同轴电缆用来直接传输数字信号，它又分为粗同轴电缆和细同轴电缆，其中粗同轴电缆适用于较大局域网的网络干线，布线距离较长，可靠性较好，但是网络安装、维护等方面比较困难，造价较高；而细同轴电缆安装较容易，而且造价较低，但因受网络布线结构的限制，其日常维护不甚方便。宽带同轴电缆用于频分多路复用（FDM）的模拟信号发送，还用于不使用频分多路复用的高速数字信号发送和模拟信号发送。闭路电视所使用的CATV电缆就是宽带同轴电缆。

3）光纤（Fiber Optic）

光导纤维简称光纤，它重量轻，体积小。用光纤传输电信号时，在发送端先要将其转换成光信号，而在接收端又要由光检波器还原成电信号。光纤是软而细的、利用内部全反射原理来传导光束的传输介质。按光源采用不同的发光管分为发光二极管和注入型激光二极管。多模光纤（Multimode Fiber）使用的材料是发光二极管，价格较便宜，但定向性较差；单模光纤（Single Mode Fiber）使用的材料是注入型二极管，定向性好，损耗少，效率高，传播距离长，但价格昂贵。

2．无线介质

无线传输介质都不需要架设或铺埋电缆或光纤，而是通过大气传输，目前有三种技术：微波、红外线和激光。

1）微波

微波通信是在对流层视线距离范围内利用无线电波进行传输的一种通信方式，频率范围为2～40GHz。微波通信是沿直线传播的，由于地球表面是曲面，微波在地面的传播距离有限，直接传播的距离与天线的高度有关，天线越高距离越远，但超过一定距离后就要用中继站来接力，两微波站的通信距离一般为30～50km，长途通信时必须建立多个中继站。中继站的功能是变频和放大，进行功率补偿。微波通信分为模拟微波通信和数字微波通信两种。模拟微波通信主要采用调频制，数字微波通信大都采用相移键控（PSK）。微波通信的传输质量比较稳定，影响质量的主要因素是雨雪天气对微波产生的吸收损耗，不利地形或环境对微波所造成的衰减现象。

2）红外线和激光

红外通信和激光通信也像微波通信一样，有很强的方向性，都是沿直线传播的。这三种技术都需要在发送方和接收方之间有一条视线（Line-of-sight）通路，有时统称这三者为视线媒体。所不同的是，红外通信和激光通信把要传输的信号分别转换为红外光信号和激光信号，直接在空间传播。由于这三种视线媒体都不需要铺设电缆，对于不论是在地下或用电线杆很难在建筑物之间架设电缆，特别是要穿越的空间属于公共场所的局域网特别有用。但这三种技术对环境气候较为敏感，例如雨、雾和雷电。相对来说，微波一般对雨和雾的敏感度较低。

3）卫星通信

卫星通信是以人造卫星为微波中继站，它是微波通信的特殊形式。卫星接收来自地面发送站发出的电磁波信号后，再以广播方式用不同的频率发回地面，被地面工作站接收。卫星通信可以克服地面微波通信距离的限制。一个同步卫星可以覆盖地球的1/3以上表面，三个这样的卫星就可以覆盖地球上全部通信区域，这样地球上的各个地面站之间都可互相通信了。由于卫星信道频带宽，也可采用频分多路复用技术分为若干个子信道，有些用于由地面站向卫星发送（称为上行信道），有些用于由卫星向地面转发（称为下行信道）。卫星通信的优点是容量大、距离远，缺点是传播延迟时间长。

5.3.3　组建网络

在一个局域网中，其基本组成部件为服务器、客户端、网络设备、通信介质和网络软件等。

（1）服务器（Server）。局域网的核心，根据它在网络中的作用，还可进一步分为文件服务器、打印服务器和通信服务器。

（2）客户端（Client）。客户端又称为用户工作站，是用户与网络应用接口设备。

（3）网络设备。主要指一些硬件设备，如网卡、收发器、中继器、集中器、网桥和路由器等。网卡是一种必不可少的网络设备，常用的网卡有Ethernet（以太网）网卡、ARCNET网卡、ESIA总线网网卡和Token-Ring网卡等。

（4）通信介质。数据的传输媒体。不同的通信介质有着不同的传输特性。

（5）网络软件。网络软件主要包括底层协议软件、网络操作系统（NOS）等。底层协议软件是由一组标准规则及软件构成，以使实体间或网络之间能够互相进行通信。网络操作系统主要对整个网络的资源和运行进行管理，并为用户提供应用接口。

【例5.1】　为了将两个相邻办公室的多台计算机连接成一个局域网，以方便传输文件、共享资源，最简单的连接方式如图5-5所示。

图5-5　简单网络连接方式

采用集线器将两个办公室的多台计算机连接成一个局域网，如果感觉速度较慢，可将Hub换成交换机（Switch），构成树型或总线和星型结合的拓扑结构；传输介质采用五类以上的非屏蔽双绞线，使用RJ45连接Hub与计算机；网卡采用10/100M自适应的以太网卡；协议使用TCP/IP或NETBIEU或其他协议。该网络安装和维护简单易行且费用低廉，计算机和Hub之间的最大UTP电缆长度为100m，两个计算机之间（即端—端）最多允许有4个Hub和5个电缆段，即最大网络长度为500m。

【例5.2】某校园／大型企业为本单位建设高速信息网络，网络主干中心为千兆以太网的光纤局域网，连接各学院、系、图书馆等信息网，并接入Internet。实现各级各类网络的互连互通，为学校的各级单位、教师、学生提供方便、快捷的信息与教学服务，从而有效地为科研、教学服务，提高学校的整体水平。网络的构建如图5-6所示。

图5-6　校园网的构建

【例.3】某家庭想连接Internet，决定申请一条ADSL线路，通过拨号来连接Internet，原因如下。

（1）ADSL具有很高的传输速率，其下行为2～8Mb/s，上行为64～640kb/s，为普通拨号Modem的百倍以上，也是宽带上网中速度较高的一种。

（2）ADSL上网和打电话互不干扰，ADSL数据信号和电话音频信号以频分复用原理调制于各自频段互不干扰，上网的同时可以使用电话，避免了拨号上网的烦恼。

（3）ADSL独享带宽，安全可靠，其他宽带方式虽然在速度方面有快过它的，但有的是属于共享带宽方式。例如cable modem下行可达到20Mb/s，但它是一种粗糙的总线型广播网络，成千上万用户争抢20Mb/s的带宽，而非独享，更为严重的是，它属于总线型的网络，先天的广播特性造成了信息传输的不安全性。ADSL利用中国电信深入千家万户的电话网络，先天形成星型结构的网络拓扑构造，骨干网络采用中国电信遍布全城、全国的光纤传输，独享2～8Mb/s带宽，信息传递快速可靠安全。

（4）ADSL费用低廉，虽然电话线同时传递电话语音和数据，但数据并不通过电话交换机，因此不用拨号，一直在线，属于专线上网方式。这意味着使用ADSL上网不需要缴纳拨号上网的电话费用。另一方面，不需要对原有电话线路进行改造，用户端不需要购买价格昂贵的设备，只需一个现在相当普及的ADSL Modem即可，相对来说投资较少。

（5）ADSL能提供真正的视频点播（VOD）、网上游戏、交互电视和网上购物等宽带多媒体服务，远程LAN接入，远地办公室，在家工作等高速数据应用，远程医疗，远程教学，远地可视会议，体育比赛现场实时传送等。

ADSL连接Internet的方式有两种：专线接入和虚拟拨号接入。采用虚拟拨号方式的用户采用类似Modem和ISDN的拨号程序，在使用习惯上与原来的方式没什么不同。采用专线接入的用户只要开机即可接入Internet。

使用ADSL上网所需的硬设有一块网卡和ADSL Modem。当然，家用计算机是必须的。连接方式如图5-7所示。

图5-7　ADSL单用户接入方式

如果不是家庭，而是公司或企业，即局域网通过ADSL接入Internet，其接入方法有如下两种。

（1）直接通过ADSL连上网的那台主机设置成代理服务器，然后本地局域网上的客户端通过该代理服务器访问外部信息资源。这种方法的好处是需要申请一个账号或一个IP地址，本地客户端可采用保留IP地址。

（2）采用专线方式，为局域网上的每台计算机向电话局申请一个IP地址。这种方法的好处是无须设置一台专用的代理服务网关，缺陷是目前对一条ADSL线路只能提供最多8个IP地址给局域网。

通过代理服务器将公司或企业接入Internet方式如图5-8所示。

图5-8　公司或企业的ADSL接入方式

连接时需要注意的问题如下。

（1）接口方式。有以太网、USB和PCI三种。USB、PCI适用于家庭用户，性价比好，小巧、方便、实用；外置以太网口的产品只适用于企业和办公室的局域网，它可以带多台机器进行上网。有的以太网接口的ADSL Modem同时具有桥接和路由功能。

（2）分离器。使上网和打电话互不干扰。

（3）支持的协议。ADSL Modem上网拨号方式有三种，即专线方式（静态IP）、PPPoA和PPPoE。普通用户多采用PPPoE（Point-to-Point Protocol over Ethernet）或PPPoA（Point-to-Point Protocol over ATM）虚拟拨号的方式上网。

5.4　网络的协议与标准

计算机网络的硬件设备是承载计算机通信的实体，但它们是怎样有序地完成计算机之间通信任务的呢？也就是说，要共享计算机网络的资源，以及进行网络中的交换信息，就需要实现不同系统中实体的通信。两个实体要想成功地通信，它们必须具有相同的语言，在计算机网络中称为协议（或规程）。所谓协议，指的是网络中的计算机与计算机进行通信时，为了能够实现数据的正常发送与接收，必须要遵循的一些事先约定好的规则（标准或约定），在这些规程中明确规定了通信时的数据格式、数据传送时序以及相应的控制信息和应答信号等内容。下面主要介绍网络的标准、局域网协议与广域网协议。

5.4.1　网络的标准

在网络的标准化方面，有许多标准化机构在工作，如国际标准化组织、国际电信联盟、电子工业协会、电气和电子工程师协会、因特网活动委员会等。

1．电信标准

1865年成立国际电信联盟（International Telecommunication Union, ITU），1947年ITU成为联合国的一个组织，它由如下三部分组成。

●　ITU-R：无线通信部门。ITU-R的主要工作是确保无线电频率和卫星轨道被所有国家平等、有效和经济地利用，召开世界性和地区性大会来制定无线电法规和地区性协议，起草并通过有关技术、业务和系统的建议。

●　ITU-T：电信标准部门。下设许多研究组，研究组下设专题，从事网络管理、网络维护、业务运营、网络和终端的端对端传输特性、网络总体方面、多媒体业务和系统等方面的研究。例如，Q42/SG Ⅶ专门研究OSI参考模型。

●　ITU-D：开发部门。主要宗旨是促进第三世界国家的电信发展。

1953年～1993年，ITU-T被称为CCITT（国际电报电话咨询委员会）。CCITT建议自1993年起都打上了ITU-T标记。已经公布并使用的最重要的标准如下。

（1）V系列。ITU-T提出的V系列标准主要是针对调制解调器的标准。例如，V.90是56kb/s调制解调器的标准。

（2）X系列。ITU-T提出的X系列标准是应用于广域网的，该系列标准分为如下两组。

●　X.1—X.39标准。应用于终端形式、接口、服务设施和设备。最著名的标准是X.25，它规定了数据包装和传送的协议。

●　X.40—X.199标准。管理网络结构、传输、发信号等。

2．国际标准

1946年成立的国际标准化组织负责制定各种国际标准，ISO有89个成员国家，85个其他成员。ISO的任务是促进全球范围内的标准化及其有关活动，以利于国际间产品与服务的交流，以及在知识、科学、技术和经济活动中发展国际间的相互合作。例如，ISO开发了开放式系统互连网络结构模型，模型定义了用于网络结构的7个数据处理层。

其他标准化组织如下。

●　ANSI：美国国家标准研究所，ISO的美国代表。ANSI设计了ASCII代码组，它是一种广泛使用的数据通信标准代码。

●　NIST：美国国家标准和技术研究所，美国商业部的标准化机构。

●　IEEE：电气和电子工程师协会（Institute of Electrical and Electronics Engineers）。IEEE设置了电子工业标准，分成一些标准委员会（或工作组），每个工作组负责标准的一个领域，工作组802设置了网络上的设备如何彼此通信的标准，即IEEE 802标准委员会划分成的工作组有802.1工作组，协调低档与高档OSI模型；802.2工作组，涉及逻辑数据链路标准；802.3工作组，有关CSMA/CD标准在以太网的应用；802.4工作组，令牌总线标准在LAN中的应用；802.5工作组，设置有关令牌环网络的标准。

●　EIA：电子工业协会（Electronic Industries Association）。最为人熟悉的EIA标准之一是RS-232C接口，这一通信接口允许数据在设备之间交换。

值得注意的是，ITU-T和ISO之间有很好的合作和协调。

3．Internet标准

Internet标准的特点是自发而非政府干预的，管理松散，每个分网络均由各自分别管理，目前已组成了一个民间性质的协会ISOC（Internet Society）进行必要的协调与管理，有一个网络信息中心（NIC）来管理IP地址，保证注册地址的唯一性，并为用户提供一些文件，介绍可用的服务。ISOC设有Internet总体管理机构结构（IAB）。

1969年，在ARPANET时代就开始发布请求评注（Request For Comments, RFC），至今已超过3000个。

5.4.2　局域网协议

IEEE局域网标准委员会对局域网的定义为：“局域网络中的通信被限制在中等规模的地理范围内，如一所学校；能够使用具有中等或较高数据速率的物理信道，且具有较低的误码率；局域网络是专用的，由单一组织机构所使用。”局域网技术由于具有规模小、组网灵活和结构规整的特点，因此极易形成标准。事实上，局域网技术也是在所有计算机网络技术中标准化程序最高的一部分。国际电子电气工程师协议早在20世纪70年代就制定了三个局域网标准：IEEE 802.3（CSMA/CD，以太网）、IEEE 802.4（Token Bus，令牌总线）和IEEE 802.5（Token Ring，令牌环）。由于它已被市场广泛接受，因此IEEE 802系列标准已被ISO采纳为国际标准。而且，随着网络技术的发展，又出现了IEEE 802.7（FDDI）、IEEE 802.3u（快速以太网）、IEEE 802.12（100VG-AnyLAN）和IEEE 802.3z（千兆以太网）等新一代网络标准。

一个局域网的基本组成主要有网络服务器、网络工作站、网络适配器和传输介质。这些设备在特定网络软件支持下完成特定的网络功能。决定局域网特性的主要技术有三个方面：用以传输数据的传输介质；用以连接各种设备的拓扑结构；用以共享资源的介质访问控制方法。它们在很大程度上决定了传输数据的类型、网络的响应时间、吞吐量和利用率，以及网络应用等各种网络特性。不同的局域网协议最重要的区别是介质访问控制方法，它对网络特性具有十分重要的影响。

1．LAN模型

ISO/OSI的7层参考模型本身不是一个标准，在制定具体网络协议和标准时，要依OSI/RM参考模型作为“参照基准”，并说明与该“参照基准”的对应关系。在IEEE 802局域网（LAN）标准中，只定义了物理层和数据链路层两层，并根据LAN的特点，把数据链路层分成逻辑链路控制（Logical Link Control, LLC）子层和介质访问控制（Medium Access Control, MAC）子层。还加强了数据链路层的功能，把网络层中的寻址、排序、流控和差错控制等功能放在LLC子层来实现。图5-9为LAN协议的层次以及与OSI/RM参考模型的对应关系。

图5-9　LAN层次与OSI/RM的对应关系

1）物理层

和OSI物理层的功能一样，主要处理在物理链路上发送、传递和接收非结构化的比特流，包括对带宽的频道分配和对基带的信号调制、建立、维持、撤销物理链路，处理机械的、电气的和过程的特性。其特点是可以采用一些特殊的通信媒体，在信息组成的格式上可以有多种。

2）MAC

主要功能是控制对传输介质的访问，MAC与网络的具体拓扑方式以及传输介质的类型有关，主要是介质的访问控制和对信道资源的分配。MAC层还实现帧的寻址和识别，完成帧检测序列产生和检验等功能。

3）LLC

可提供两种控制类型，即面向连接服务和非连接服务。其中，面向连接服务能够提供可靠的信道。逻辑链路控制层提供的主要功能是数据帧的封装和拆除，为高层提供网络服务的逻辑接口，能够实现差错控制和流量控制。

在计算机网络体系结构中，最具代表性和权威的是ISO的OSI/RM和IEEE的802协议。OSI是设计和实现网络协议标准的最重要的参考模型和依据，而IEEE 802则制定了一系列具体的局域网标准，并不断地增加新的标准，它们之间的关系如图5-10所示。

图5-10　IEEE 802标准系列间的关系

2．以太网（IEEE 802.3标准）

以太网技术可以说是局域网技术中历史最悠久和最常用的一种。它采用的“存取方法”是带冲突检测的载波监听多路访问协议（Carrier-Sense Multiple Access with Collision Detection, CSMA/CD）技术。

目前以太网主要包括三种类型：IEEE 802.3中定义的标准局域网，速度为10Mb/s，传输介质为细同轴电缆；IEEE 802.3u中定义的快速以太网，速度为100Mb/s，传输介质为双绞线；IEEE 802.3z中定义的千兆以太网，速度为1000Mb/s，传输介质为光纤或双绞线。

1）介质访问技术

IEEE 802.3所使用的介质访问协议CSMA/CD是让整个网络上的主机都以竞争的方式来抢夺传送数据的权力。工作过程为：首先侦听信道，如果信道空闲，则发送；如果信道忙，则继续侦听，直到信道空闲时立即发送。开始发送后再进行一段时间的检测，方法是边发送边接收，并将收、发信息相比较，若结果不同，表明发送的信息遇到碰撞，于是立即停止发送，并向总线上发出一串阻塞信号，通知信道上各站冲突已发生。已发出信息的各站收到阻塞信号后，等待一段随机时间，等待时间最短的站将重新获得信道，可重新发送。

在CSMA/CD中，当检测到冲突并发出阻塞信号后，为了降低再次冲突概率，需要等待一个退避时间。退避算法有许多种，常用的一种通用退避算法称为二进制指数退避算法。

2）IEEE 802.3——10Mb/s以太网

IEEE 802.3——10Mb/s以太网定义过10Base5、10Base2、10Base-T和10Base-F等几种（需要注明的是，其中10Base-T与10Base-F的最后一项就是以线缆类型进行命名的，其中T代表双绞线，F代表光纤）。10Base 5标准是最早的媒体规范，它使用阻抗为50Ω的同轴粗缆。但由于同轴粗缆的缆线直径大，所以比较笨重，不易铺设。10Base 2标准是为建立一个比10Base 5更廉价的局域网，它使用阻抗为50Ω的同轴细缆，唯一的差别就是它使得每两个节点间的距离限制从500m降为185m。10Base-T标准是一个使用非屏蔽双绞线为传输介质的标准，所要用到的非屏蔽双绞线只需3类线标准即可满足要求，是一个成功的标准。10Base-F标准充分利用了新兴媒体光纤的距离长、传输性能好的优点，大大改进了以太网技术。

3）IEEE 802.3u——100Mb/s快速以太网

随着计算机技术的不断发展，10Mb/s的网络传输速度实在无法满足日益增大的需求。IEEE 802.3u充分考虑到了向下兼容性：它采用了非屏蔽双绞线（或屏蔽双绞线、光纤）作为传输媒介，采用与IEEE 802.3一样的介质访问控制层——CSMA/CD。IEEE 802.3u常称为快速以太网。根据实现的介质不同，快速以太网可以分为100BaseTX、100BaseFX和100BaseT4三种。

100BaseTX用两对5类非屏蔽双绞线，或者1类、2类屏蔽双绞线作为传输媒介，来实现传输速度为100Mb/s的网络，最多支持两个中继器。100BaseFX是2束多模光纤上的标准，在没有中继设备的网络中最大传输距离为400m。100BaseT4利用10Mb/s的网络中使用的3类线有两对是空着没有利用的特点，使用4对3类非屏蔽双绞线上提供传输速度为100Mb/s网络。

4）IEEE 802.3z——1000Mb/s千兆以太网

IEEE 802.3z对介质访问控制层规范进行了重新定义，以维持适当的网络传输距离，介质访问控制方法仍采用CSMA/CD协议，并且重新制定了物理层标准，使之能提供1000Mb/s的原始带宽。因此，它仍是一种共享介质的局域网，发送到网上的信号是广播式的，接收站根据地址接收信号。网络接口硬件能监听线路上是否已存在信号，以避免冲突，或在没有冲突时重发数据。

在物理层，千兆以太网支持如下三种传输介质。

（1）光纤系统。支持多模光纤和单模光纤系统，多模光纤的工作距离为500m，单模光纤的工作距离为2000m。

（2）宽带同轴电缆系统。其传输距离为25m。

（3）5类UTP电缆。其传输距离为100m，链路操作模式为半双工。

千兆位以太网采用以交换机为中心的星型拓扑结构，主要用于交换机与交换机之间或者交换机与企业超级服务器之间的高速网络连接。

3．令牌环网（IEEE 802.5）

令牌环是环型网中最普遍采用的介质访问控制，它适用于环型网络结构的分布式介质访问控制，其流行性仅次于以太网。令牌环网的传输介质虽然没有明确定义，但主要基于屏蔽双绞线和非屏蔽双绞线两种，拓扑结构可以有多种，如环型（最典型）、星型（采用得最多）和总线型（一种变形）。编码方法为差分曼彻斯特编码。

IEEE 802.5的介质访问使用的是令牌环控制技术，工作过程为：首先，令牌环网在网络中传递一个很小的帧，称为“令牌”，只有拥有令牌环的工作站才有权力发送信息。令牌在网络上依次顺序传递。当工作站要发送数据时，等待捕获一个空令牌，然后将要发送的信息附加到后边，发往下一站，如此直到目标站。然后将令牌释放。如果工作站要发送数据时，经过的令牌不是空的，则等待令牌释放。

当信息帧绕环通过各站时，各站都要将帧的目的地址与本站地址相比较，如果地址符合，说明是发送给本站的，则将帧复制到本站的接收缓冲器中，同时将帧送回到环上，使帧继续沿环传送；如果地址不符合，则简单地将信息帧重新送到环上即可。

4．FDDI

FDDI（Fiber Distributed Data Interface，光纤分布式数据接口）类似令牌环网的协议，它用光纤作为传输介质，数据传速可达到100Mb/s，环路长度可扩展到200km，连接的站点数可以达到1000个。FDDI采用一种新的编码技术，称为4B/5B编码，即每次对4位数据进行编码。每4位数据编码成5位符号，用光信号的存在或不存在来代表5位符号中的每一位是1还是0。

光纤中传送的是光信号，有光脉冲表示1，无光脉冲表示0。这种简单编码的缺点是没有同步功能。在同轴电缆或双绞线作为传输介质的局域网中，通常采用曼彻斯特编码方式。它利用中间的跳变作为同步信号。这样对每一位数据单元产生两次瞬变，使带宽的利用率降低。5位编码的32种组合中，实际只使用了24种，其中的16种用来做数据，其余8种用来做控制符号（如帧的起始和结束符号等）。4B/5B编码中，5位码中的“1”码至少为2位，按NRZI编码原理，信号中就至少有两次跳变，因此接收端可得到足够的同步信息。

FDDI采用双环体系结构，两环上的信息反方向流动。双环中的一环称为主环，另一环称为次环。在正常情况下，主环传输数据，次环处于空闲状态。双环设计的目的是提供高可靠性和稳定性。FDDI定义的传输介质有单模光纤和多模光纤两种。

5.4.3　广域网协议

广域网通常是指覆盖范围大，传输速率低，以数据通信为主要目的的数据通信网。随着信息技术的迅速发展，很多国家的数据通信业务的增长率已大大提高，特别是国际互联网的普及促进了数据通信网技术的发展。

在地域分布很远、很分散，以至于无法用直接连接来接入局域网的场合，广域网（WAN）通过专用的或交换式的连接把计算机连接起来。这种广域连接可以是通过公众网建立的，也可以是通过服务于某个专门部门的专用网建立起来。相对来说，广域网显得比较错综复杂，目前主要用于广域传输的协议比较多，如PPP（点对点协议）、DDN、ISDN（综合业务数字网）、FR（帧中继）和ATM（异步传输模式）等。

1．点对点协议（PPP）

点对点协议主要用于“拨号上网”这种广域连接模式。它的优点在于简单、具备用户验证能力、可以解决IP分配等。它主要通过拨号或专线方式建立点对点连接发送数据，使其成为各种主机、网桥和路由器之间简单连接的一种共通的解决方案。

家庭拨号上网就是通过PPP在用户端和运营商的接入服务器之间建立通信链路。目前，宽带接入正在成为取代拨号上网的趋势，在宽带接入技术日新月异的今天，PPP也衍生出新的应用。典型的应用是在ADSL（Asymmetrical Digital Subscriber Line，非对称数据用户线）接入方式当中，PPP与其他的协议共同派生出了符合宽带接入要求的新的协议，如PPPoE和PPPoA。

利用以太网（Ethernet）资源，在以太网上运行PPP来进行用户认证接入的方式称为PPPoE。PPPoE既保护了用户方的以太网资源，又完成了ADSL的接入要求，是目前ADSL接入方式中应用最广泛的技术标准。同样，在ATM网络上运行PPP来管理用户认证的方式称为PPPoA。它与PPPoE的原理相同，作用相同。不同的是，它是在ATM网络上，而PPPoE是在以太网网络上运行，所以要分别适应ATM标准和以太网标准。

2．数字用户线（xDSL）

xDSL是各种数字用户线的统称，根据各种宽带通信业务需要，目前还有DSL技术和产品，如ADSL（Asymmetric DSL，不对称数字用户线）、SDSL（Single pair DSL，单对线数字用户环路）、IDSL（ISDN DSL，ISDN用的数字用户线）、RADSL（Rate adaptive DSL，速率自适应非对称型数字用户线）和VDSL（Very high Bit rate DSL，甚高速数字用户线）等。

ADSL是研制最早，发展较快的一种。它是在一对铜双绞线上为用户提供上、下行非对称的传输速率（即带宽）。ADSL接入服务能做到较高的性能价格比，ADSL接入技术较其他接入技术具有其独特的技术优势：它的速率可达到上行1兆／下行8兆，速度非常快。另外，使用ADSL上网不需要占用电话线路，在电话和上网互不干扰的同时，大大节省了普通上网方式的话费支出；独享带宽安全可靠；安装快捷方便；价格实惠。它把线路按频段分成语音、上行和下行三个信道，故语音和数据可共用1对线。ADSL特别适合于像VOD业务及Internet和多媒体业务的应用。ADSL一般采用CAP和DMT两种线路编码调制技术。传输距离与线径、速率有关，一般在3km以上。因此，ADSL是一种很有发展前途的数字接入技术。ADSL技术作为一种宽带接入方式，可以为用户提供中国电信宽带网的所有应用业务。采用各种拨号方式上网的用户将逐步过渡到ADSL宽带接入方式。ADSL在宽带接入中已经扮演着越来越重要的角色。

对于个人用户，在现有电话线上安装ADSL，只需在用户端安装一台ADSL Modem和一只分离器，用户线路不用任何改动，极其方便。数据线路为：PC—ADSL Modem—分离器—入户接线盒—电话线—DSL接入复用器—ATM/IP网络；语音线路为：话机—分离器—入户接线盒—电话线—DSL接入复用器—交换机。

对于企业用户，在现有电话线上安装ADSL和分离器，连接HUB或Switch。数据线路为：PC—以太网（HUB或Switch）—ADSL路由器—分离器—入户接线盒—电话线—DSL接入复用器—ATM/IP网络；语音线路为：话机—分离器—入户接线盒—电话线—DSL接入复用器—交换机。

3．数字专线

数字数据网（Digital Data Network, DDN）是采用数字传输信道传输数据信号的通信网，可提供点对点、点对多点透明传输的数据专线出租电路，为用户传输数据、图像和声音等信息。数字数据网是以光纤为中继干线网络，组成DDN的基本单位是节点，节点间通过光纤连接，构成网状的拓扑结构。

DDN专线就是市内或长途的数据电路，电信部门将它们出租给用户做资料传输使用后，它们就变成用户的专线，直接进入电信的DDN网络，因为这种电路是采用固定连接的方式，不需经过交换机房，所以称之为固定DDN专线。DDN专线不仅需要铺设专用线路（在DDN的客户端需要一个称为DDN Modem的CSU/DSU设备以及一个路由器）从用户端进入主干网络，而且需要付电信月租费、网络使用费和电路租用费等。其优势是网络传输速率高、时延小、质量好、网络透明度高、可支持任何规程、安全可靠。

4．帧中继

帧中继（Frame Relay, FR）是在用户网络接口之间提供用户信息流的双向传送，并保持顺序不变的一种承载业务。用户信息以帧为单位进行传输，并对用户信息流进行统计复用。帧中继是综合业务数字网标准化过程中产生的一种重要技术，它是在数字光纤传输线路逐渐代替原有的模拟线路，用户终端智能化的情况下，由X25分组交换技术发展起来的一种传输技术。

帧中继是一种基于可变帧长的数据传输网络，在传输过程中，网络内部可以采用“帧交换”，即以帧为单位进行传送；也可采用“信元交换”，即以信元（53字节长）为单位进行传送。

帧中继提供一种简单的面向连接的虚电路分组服务，包括交换虚电路连接和永久虚电路连接。帧中继的优点有降低网络互联费用、简化网络功能，提高网络性能、采用国际标准、各厂商产品相互兼容。

5．异步传输模式

异步传输模式（Asynchronous Transfer Mode, ATM）是B-ISDN的关键核心技术，它是一种面向分组的快速分组交换模式，使用了异步时分复用技术，将信息流分割成固定长度的信元。使用统一的信息单位能比较容易地实现各种信息流混合在一起的多媒体通信，并能根据业务类型、速率的需求动态地分配有效容量。ATM能够根据需要改变传送速率，对高速信息传递频次高，而对低速信息传递频次低，按照统计复用的原理进行传输和交换，故ATM完全可以用单一的交换方式，灵活有效地支持频带分布范围极广的各种业务。

在ATM网络中，数据以定长的信元为单位进行传输，信元由信元头和信元体构成，每个信元53个字节，其中信元头5个字节，信元体48个字节。

ATM的参考模型由4层构成，分别是用户层、ATM适配层、ATM层和物理层。图5-11给出了简化后的ATM的参考模型。

图5-11　ATM的参考模型

1）用户层

由用户平面、控制平面和管理平面组成。其各自的功能如下。

（1）用户平面。指用户要求的应用、协议及服务。如通常的数据协议、语音和视频应用等。

（2）控制平面。包含连接建立、维护和拆除等有关功能。

（3）管理平面。负责层次和平面的协调，通过层次管理支持用户平面和管理平面。

2）ATM适配层

负责将用户层的信息转换成ATM网络可用的格式。等用户层把较长的数据分组交给ATM适配层后，ATM适配层按规定的长度将数据分成若干信元体，再传给ATM层。

3）ATM层

基本功能是负责生成信元，它不管信元体的内容，只为信元体生成信元头并附给信元体，以形成标准的信元格式。跨越ATM层到物理层的信息单元只能是53个字节的信元。

4）物理层

负责对信元进行编码，并将其交给物理介质。它汇集了各种被认可的物理线路协议，如SDH（STM-1、STM-4、STM-16）和同步光纤网等。

ATM连接有两种类型，即永久虚电路（PVC）和交换虚电路（SVC）。

●　永久虚电路：其连接由管理员建立和拆除。

●　交换虚电路：通过终端系统和ATM网络以及ATM网络内部的信令协议的操作来建立和拆除。

虽然在这里将ATM技术划在广域网部分来介绍，但ATM却是一种可以将局域网功能、广域网功能、语音、视频和数据集成进一个统一的协议设计。正是它的高度统一性和良好的可扩展性，给计算机网络技术掀开了新的一页。它具有的特点是速度高，支持622Mb/s的传输速率；可扩展性好；较高的传输质量（QoS）；ATM提供了端到端解决方案的潜力，这意味着它的应用可以从桌面到局域网，一直延伸到广域网。

ATM技术适用的范围为多媒体和视频应用；适合构架骨干网；无缝地集成广域网和局域网。ATM的主要缺点是成本较高，不适合于小网络。

6．X.25协议

X.25在本地DTE和远程DTE之间提供一个全双工、同步的透明信道，并定义了三个相互独立的控制层：物理层、数据链路层和分组层，它们分别对应于ISO/OSI的物理层、链路层和网络层，如图5-12所示。

图5-12　X.25层次模型

X.25是在公用数据网上，以分组方式进行操作的DTE（数据终端设备）和DCE（数据通信设备）之间的接口。X.25只是对公用分组交换网络的接口规范说明，并不涉及网络内部实现，它是面向连接的，支持交换式虚电路和永久虚电路。

物理层接口指DTE和网络之间的线路连接，X.25指出可采用V.24、V.35、G.703和X.21等接口；链路层逻辑接口采用链路层协议，负责DTE和DCE之间的初始化、校验，并控制物理链路上的数据传输，用户数据以信息帧在DTE和DCE之间传送；分组层逻辑接口描述了呼叫的建立、保持和拆除的过程，以及数据和控制信息在分组中的格式。而默认的数据分组长度是128个字节。

5.4.4　TCP/IP协议簇

TCP/IP作为Internet的核心协议，通过近20多年的发展已日渐成熟，并被广泛应用于局域网和广域网中，目前已成为事实上的国际标准。作为一个最早的、也是迄今为止发展最为成熟的互联网络协议系统，TCP/IP包含许多重要的基本特性，这些特性主要表现在5个方面：逻辑编址、路由选择、域名解析、错误检测和流量控制以及对应用程序的支持等。

（1）逻辑编址。每一块网卡在出厂时就由厂家分配了一个独一无二的永久性的物理地址。在Internet中，为每台连入因特网的计算机分配一个逻辑地址，这个逻辑地址被称为IP地址。一个IP地址可以包括一个网络ID号，用来标识网络；一个子网络ID号，用来标识网络上的一个子网；另外，还有一个主机ID号，用来标识子网络上的一台计算机。这样，通过这个分配给某台计算机的IP地址，就可以很快地找到相应的计算机。

（2）路由选择。在TCP/IP中包含了专门用于定义路由器如何选择网络路径的协议，即IP数据包的路由选择。

（3）域名解析。虽然TCP/IP采用的是32位的IP地址，但考虑用户的记忆方便，专门设计了一种方便的字母式地址结构，称为域名或DNS（域名服务）名字。将域名映射为IP地址的操作，称为域名解析。域名具有较稳定的特点，而IP地址则较易发生变化。

（4）错误检测与流量控制。TCP/IP具有分组交换确保数据信息在网络上可靠传递的特性，这些特性包括检测数据信息的传输错误（保证到达目的地的数据信息没有发生变化），确认已传递的数据信息已被成功地接收，监测网络系统中的信息流量，防止出现网络拥塞。

1．TCP/IP分层模型

协议是对数据在计算机或设备之间传输时的表示方法进行定义和描述的标准。协议规定了进行传输、检测错误以及传送确认信息等内容。TCP/IP是个协议簇，它包含了多种协议。ISO/OSI模型、TCP/IP的分层模型及协议的对比如图5-13所示。

从图5-13可知，TCP/IP分层模型由4个层次构成，即应用层、传输层、网际层和网络接口层，各层的功能简述如下。

（1）应用层。应用层处在分层模型的最高层，用户调用应用程序来访问TCP/IP互联网络，以享受网络上提供的各种服务。应用程序负责发送和接收数据。每个应用程序可以选择所需要的传输服务类型，并把数据按照传输层的要求组织好，再向下层传送，包括独立的报文序列和连续字节流两种类型。

（2）传输层。传输层的基本任务是提供应用程序之间的通信服务。这种通信又叫端到端的通信。传输层既要系统地管理数据信息的流动，还要提供可靠的传输服务，以确保数据准确而有序地到达目的地。为了这个目的，传输层协议软件需要进行协商，让接收方回送确认信息及让发送方重发丢失的分组。在传输层与网际层之间传递的对象是传输层分组。

图5-13　TCP/IP模型与OSI模型的对比

（3）网际层。网际层又称IP层，主要处理机器之间的通信问题。它接收传输层请求，传送某个具有目的地址信息的分组。该层主要完成如下功能。

①　把分组封装到IP数据报（IP Datagram）中，填入数据报的首部（也称为报头），使用路由算法选择把数据报直接送到目标机或把数据报发送给路由器，然后再把数据报交给下面的网络接口层中对应的网络接口模块。

②　处理接收到的数据报，检验其正确性。使用路由算法来决定是在本地进行处理，还是继续向前发送。如果数据报的目标机处于本机所在的网络，该层软件就把数据报的报头剥去，再选择适当的传输层协议软件来处理这个分组。

③　适时发出ICMP的差错和控制报文，并处理收到的ICMP报文。

（4）网络接口层。网络接口层又称数据链路层，处于TCP/IP协议层之下，负责接收IP数据报，并把数据报通过选定的网络发送出去。该层包含设备驱动程序，也可能是一个复杂的使用自己的数据链路协议的子系统。

2．网络接口层协议

TCP/IP协议不包含具体的物理层和数据链路层，只定义了网络接口层作为物理层与网络层的接口规范。这个物理层可以是广域网，如X.25公用数据网；可以是局域网，如Ethernet、Token-Ring和FDDI等。任何物理网络只要按照这个接口规范开发网络接口驱动程序，都能够与TCP/IP协议集成起来。网络接口层处在TCP/IP协议的最底层，主要负责管理为物理网络准备数据所需的全部服务程序和功能。

3．网际层协议——IP

网际层是整个TCP/IP协议簇的重点。在网际层定义的协议除了IP外，还有ICMP、ARP和RARP等几个重要的协议。

IP所提供的服务通常被认为是无连接的（connectionless）和不可靠的（unreliable）。事实上，在网络性能良好的情况下，IP传送的数据能够完好无损地到达目的地。所谓无连接的传输，是指没有确定目标系统在已做好接收数据准备之前就发送数据。与此相对应的就是面向连接的（connection oriented）传输（如TCP），在该类传输中，源系统与目的系统在应用层数据传送之前需要进行三次握手。至于不可靠的服务，是指目的系统不对成功接收的分组进行确认，IP只是尽可能地使数据传输成功。但是只要需要，上层协议必须实现用于保证分组成功提供的附加服务。

由于IP只提供无连接、不可靠的服务，所以把差错检测和流量控制之类的服务授权给了其他的各层协议，这正是TCP/IP能够高效率工作的一个重要保证。这样，可以根据传送数据的属性来确定所需的传送服务以及客户应该使用的协议。例如，传送大型文件的FTP会话就需要面向连接的、可靠的服务（因为如果稍有损坏，就可能导致整个文件无法使用）。

IP的主要功能包括将上层数据（如TCP、UDP数据）或同层的其他数据（如ICMP数据）封装到IP数据报中；将IP数据报传送到最终目的地；为了使数据能够在链路层上进行传输，对数据进行分段；确定数据报到达其他网络中的目的地的路径。

IP协议软件的工作流程：当发送数据时，源计算机上的IP协议软件必须确定目的地是在同一个网络上，还是在另一个网络上。IP通过执行这两项计算并对结果进行比较，才能确定数据到达的目的地。如果两项计算的结果相同，则数据的目的地确定为本地；否则，目的地应为远程的其他网络。如果目的地在本地，那么IP协议软件就启动直达通信；如果目的地是远程计算机，那么IP必须通过网关（或路由器）进行通信，在大多数情况下，这个网关应当是默认网关。当源IP完成了数据报的准备工作时，它就将数据报传递给网络访问层，网络访问层再将数据报传送给传输介质，最终完成数据帧发往目的计算机的过程。

当数据抵达目的计算机时，网络访问层首先接收该数据。网络访问层要检查数据帧有无错误，并将数据帧送往正确的物理地址。假如数据帧到达目的地时正确无误，网络访问层便从数据帧的其余部分中提取数据有效负载（Payload），然后将它一直传送到帧层次类型域指定的协议。在这种情况下，可以说数据有效负载已经传递给了IP。

有关IP地址的具体情况参见5.5.2节的内容。

4．ARP和RARP

地址解析协议（Address Resolution Protocol, ARP）及反地址解析协议（RARP）是驻留在网际层中的另一个重要协议。ARP的作用是将IP地址转换为物理地址，RARP的作用是将物理地址转换为IP地址。网络中的任何设备，主机、路由器和交换机等均有唯一的物理地址，该地址通过网卡给出，每个网卡出厂后都有不同的编号，这意味着用户所购买的网卡有着唯一的物理地址。另一方面，为了屏蔽底层协议及物理地址上的差异，IP协议又使用了IP地址，因此，在数据传输过程中，必须对IP地址与物理地址进行相互转换。

用ARP进行IP地址到物理地址转换的过程为：当计算机需要与任何其他的计算机进行通信时，首先需要查询ARP高速缓存，如果ARP高速缓存中这个IP地址存在，便使用与它对应的物理地址，直接将数据报发送给所需的物理网卡；如果ARP高速缓存中没有该IP地址，那么ARP便在局域网上以广播方式发送一个ARP请求包。如果局域网上IP地址与某台计算机中的IP地址相一致，那么该计算机便生成一个ARP应答信息，信息中包含对应的物理地址。ARP协议软件将IP地址与物理地址的组合添加到它的高速缓存中，这时即可开始数据通信。

RARP负责物理地址到IP地址的转换。这主要用于无盘工作站上，网络上的无盘工作站在网卡上有自己的物理地址，但无IP地址，因此必须有一个转换过程。为了完成这个转换过程，网络中有一个RARP服务器，网络管理员事先必须把网卡上的IP地址和相应的物理地址存储到IP RARP服务器的数据库中。

5．网际层协议——ICMP

Internet控制信息协议（Internet Control Message Protocol, ICMP）是网际层的另一个比较重要的协议。由于IP是一种尽力传送的通信协议，即传送的数据报可能丢失、重复、延迟或乱序，因此IP需要一种避免差错并在发生差错时报告的机制。ICMP就是一个专门用于发送差错报文的协议。ICMP定义了5种差错报文（源抑制、超时、目的不可达、重定向和要求分段）和4种信息报文（回应请求、回应应答、地址屏蔽码请求和地址屏蔽码应答）。IP在需要发送一个差错报文时要使用ICMP，而ICMP也是利用IP来传送报文的。ICMP是让IP更加稳固、有效的一种协议，它使得IP传送机制变得更加可靠。而且利用ICMP还可以用于测试因特网，以得到一些有用的网络维护和排错的信息。例如，著名的ping工具就是利用ICMP报文进行目标是否可达测试。

6．传输层协议——TCP

TCP（Transmission Control Protocol，传输控制协议），是整个TCP/IP协议族中最重要的协议之一。它在IP提供的不可靠数据服务的基础上，为应用程序提供了一个可靠的、面向连接的、全双工的数据传输服务。

TCP是如何实现可靠性的呢？最主要和最重要的是TCP采用了一个叫重发（retransmission）的技术。具体来说，在TCP传输过程中，发送方启动一个定时器，然后将数据包发出，当接收方收到了这个信息就给发送方一个确认（acknowledgement）信息。而如果发送方在定时器到点之前没收到这个确认信息，就重新发送这个数据包。

利用TCP在源主机想和目的主机之间建立和关闭连接操作时，均需要通过三次握手来确认建立和关闭是否成功。三次握手方式如图5-14所示，它通过“序号／确认号”使得系统正常工作，从而使它们的序号达成同步。TCP建立连接的三次握手过程如下。

图5-14　TCP建立连接的“三次握手”过程

（1）源主机发送一个SYN（同步）标志位为1的TCP数据包，表示想与目标主机进行通信，并发送一个同步序列号（如SEQ=200）进行同步。

（2）目标主机愿意进行通信，则响应一个确认（ACK位置1）。并以下一个序列号为参考进行确认（如201）。

（3）源主机以确认来响应目标主机的TCP包。这个确认中包括它想要接收的下一个序列号（该帧可以含有发送的数据）。至此连接建立完成。

同样，关闭连接也进行三次握手。

7．传输层协议——UDP

用户数据报协议（User Datagram Protocol, UDP）是一种不可靠的、无连接的协议，可以保证应用程序进程间的通信。与同样处在传输层的面向连接的TCP相比较，UDP是一种无连接的协议，它的错误检测功能要弱得多。可以这样说，TCP有助于提供可靠性；而UDP则有助于提高传输的高速率性。例如，必须支持交互式会话的应用程序（如FTP等）往往使用TCP；而自己进行错误检测或不需要错误检测的应用程序（如DNS、SNMP等）则往往使用UDP。

UDP协议软件的主要作用就是将UDP消息展示给应用层，它并不负责重新发送丢失的或出错的数据消息，不对接收到的无序IP数据报重新排序，不消除重复的IP数据报，不对已收到数据报进行确认，也不负责建立或终止连接。而这些问题是由使用UDP进行通信的应用程序负责处理的问题。

TCP虽然提供了一个可靠的数据传输服务，但它是以牺牲通信量来实现的。也就是说，为完成同样一个任务，TCP会需要更多的时间和通信量。这在网络不可靠的时候，通过牺牲一些时间换来达到网络的可靠，但当网络十分可靠的情况下，它又通过浪费带宽来保证可靠性，这时UDP则以十分小的通信量浪费占据优势。

8．应用层协议

随着计算机网络的广泛应用，人们也已经有了许多基本的、相同的应用需求。为了让不同平台的计算机能够通过计算机网络获得一些基本的、相同的服务，也就应运而生了一系列应用级的标准，实现这些应用标准的专用协议被称为应用级协议，相对于OSI参考模型来说，它们处于较高的层次结构，所以也称为高层协议。应用层的协议有NFS、Telnet、SMTP、DNS、SNMP和FTP等，详细情况在Internet服务中介绍。

5.5　Internet及应用

Internet是世界上规模最大，覆盖面最广且最具影响力的计算机互联网络，它是将分布在世界各地的计算机采用开放系统协议连接在一起，用来进行数据传输、信息交换和资源共享。在现阶段，Internet作为未来信息高速公路的雏形，无论在科学研究、教育、金融，还是在商业、军事等部门，其影响都越来越大。

5.5.1　Internet概述

从用户的角度来看，整个Internet在逻辑上是统一的、独立的，在物理上则由不同的网络互连而成。从技术角度看，Internet本身不是某一种具体的物理网络技术，它是能够互相传递信息的众多网络的一个统称，或者说它是一个网间网，只要人们进入了这个互联网，就是在使用Internet。正是由于Internet的这种特性，使得广大Internet用户不必关心网络的连接，而只关心网络提供的丰富资源。

连入Internet的计算机网络种类繁多，形式各异，且分布在世界各地，因此，需要通过路由器（IP网关）并借助各种通信线路或公共通信网络把它们连接起来。由于实现了与公用电话网的互连，个人用户入网十分方便，只要有电话和Modem即可，这也是Internet迅速普及的原因之一。Internet由美国的ARPANET网络发展而来，因此，它沿用了ARPANET使用的TCP/IP协议，由于该协议非常有效且使用方便，许多操作系统都支持它，无论是服务器还是个人计算机都可安装使用。

对于全球性最大的互联网络，总的来说无确定的负责人，它是由各自独立管理的网络互联构成的，而这些网络都各自拥有自己的管理体系和政策法规。因此，没有集中的负责掌管整个Internet的机构。尽管如此，某些政府部门在制定Internet有关政策时实际上起着主导作用。Internet目前的最高国际组织是Internet学会（Internet Society），该学会是一个志愿者组织，也是一个非盈利性的专业化组织，其主要目标是促进Internet的改革与发展。该学会下分Internet体系结构研究会（IAB）和其他几个研究会，IAB下又有工程组（IETF）、许可证管理局（ICRS）、技术研究组（IRTF）和编号管理局（IANA）等。IAB的主要任务是为支持Internet的科研与开发提供服务。

在Internet中，分布着一些覆盖范围很广的大网络，这种网络称为“Internet主干网”，它们一般属于国家级的广域网。例如，我国的CHINANET和CERNET等就是中国的Internet主干网。主干网一般只延伸到一些大城市或重要地方，在那里设立主干网节点。每一个主干网节点可以通过路由器将广域网与局域网联接起来，一个节点还可以通过另外的路由器与其他局域网再互连，由此形成一种网状结构。

5.5.2　Internet地址

无论是在网上检索信息还是发送电子邮件，都必须知道对方的Internet地址，它能唯一确定Internet上每一台计算机、每个用户的位置。也就是说，Internet上每一台计算机、每个用户都有唯一的地址来标识它是谁和在何处，以方便于几千万个用户、几百万台计算机和成千上万的组织。Internet地址格式主要有两种书写形式：域名格式和IP地址格式。

1．域名

域名（Domain Name）通常是用户所在的主机名字或地址。域名格式是由若干部分组成，每个部分又称子域名，它们之间用“.”分开，每个部分最少由两个字母或数字组成。域名通常按分层结构来构造，每个子域名都有其特定的含义。通常情况，一个完整、通用的层次型主机域名由如下4部分组成：

计算机主机名．本地名．组名．最高层域名

从右到左，子域名分别表示不同的国家或地区的名称（只有美国可以省略表示国家的顶级域名）、组织类型、组织名称、分组织名称和计算机名称等。域名地址的最后一部分子域名称为高层域名（或顶级域名），它大致上可以分成两类：一类是组织性顶级域名；另一类是地理性顶级域名。

例如：www.dzkjdx.edu.cn　cn是地理性顶级域名，表示“中国”。

www.263.net　net是组织性顶级域名，表示“网络技术组织机构”。

如果一个主机所在的网络级别较高，它可能拥有的域名仅三部分：本地名．组名．最高层域名。现在，Internet地址管理机构（Internet PCA Registration Authority, IPRA）和（Internet Assigned Number Authority, IANA）负责Internet最高层域名的登记和管理。

2．IP地址

Internet地址是按名字来描述的，这种地址表示方式易于理解和记忆。实际上，Internet中的主机地址是用IP地址来唯一标识的。这是因为Internet中所使用的网络协议是TCP/IP协议，故每个主机必须用IP地址来标识。

每个IP地址都由4个小于256的数字组成，数字之间用“.”分开。Internet的IP地址共有32位，4个字节。它表示时有两种格式：二进制格式和十进制格式。二进制格式是计算机所认识的格式，十进制格式是由二进制格式“翻译”过去的，主要是为了便于使用和掌握。例如，十进制IP地址129.102.4.11的表示方法与二进制的表示方法10000001 01100110 00000100 00001011相同，显然表示成带点的十进制格式则方便得多。

域名和IP地址是一一对应的，域名易于记忆，便于使用，因此得到比较普遍的使用。当用户和Internet上的某台计算机交换信息时，只需要使用域名，网络则会自动地将其转换成IP地址，找到该台计算机。

Internet中的地址可分为5类：A类、B类、C类、D类和E类。各类的地址分配方案如图5-15所示。在IP地址中，全0代表的是网络，全1代表的是广播。

图5-1　各类地址分配方案

A类网络地址占有1个字节（8位），定义最高位为0来标识此类地址，余下7位为真正的网络地址，支持1～126个网络。后面的3个字节（24位）为主机地址，共提供2^24
－2个端点的寻址。A类网络地址第一个字节的十进制值为000～127。

B类网络地址占有2个字节，使用最高两位为10来标识此类地址，其余14位为真正的网络地址，主机地址占后面的2个字节（16位），所以B类全部的地址有（2^14
－2）×（2^16
－2）＝16382×65534个。B类网络地址第一个字节的十进制值为128～191。

C类网络地址占有3个字节，它是最通用的Internet地址。使用最高三位为110来标识此类地址，其余21位为真正的网络地址，因此C类地址支持2^21
－2个网络。主机地址占最后1个字节，每个网络可多达2^8
－2个主机。C类网络地址第一个字节的十进制值为192～223。

D类地址是相当新的。它的识别头是1110，用于组播，例如用于路由器修改。D类网络地址第一个字节的十进制值为224～239。

E类地址为实验保留，其识别头是1111。E类网络地址第一个字节的十进制值为240～255。

网络软件和路由器使用子网掩码（Subnet Mask）来识别报文是仅存放在网络内部还是被路由转发到其他地方。在一个字段内，1的出现表明一个字段包含所有或部分网络地址，0表明主机地址位置。例如，最常用的C类地址使用前三个8位来识别网络，最后一个8位识别主机。因此，子网掩码是255.255.255.0。

子网地址掩码是相对特别的IP地址而言的，如果脱离了IP地址就毫无意义。它的出现一般是跟着一个特定的IP地址，用来为计算这个IP地址中的网络号部分和主机号部分提供依据。换句话说，就是在写一个IP地址后，再指明哪些是网络号部分，哪些是主机号部分。子网掩码的格式与IP地址相同，所有对应网络号的部分用1填上，所有对应主机号的部分用0填上。

A类、B类、C类IP地址类默认的子网掩码如表5-2所示。

表5-2　带点十进制符号表示的默认子网掩码

如果需要将网络进行子网划分，此时子网掩码可能不同于以上默认的子网掩码。例如，138.96.58.0是一个8位子网化的B类网络ID。基于B类的主机ID的8位被用来表示子网化的网络，对于网络138.96.39.0，其子网掩码应为255.255.255.0。

例如，一个B类地址172.16.3.4，为了直观地告诉大家前16位是网络号，后16位是主机号，就可以附上子网掩码255.255.0.0（11111111 11111111 00000000 00000000）。

假定某单位申请的B类地址为179.143.XXX.XXX。如果希望把它划分为14（至少占二进制的4位）个虚拟的网络，则需要占4位主机位，子网使用掩码为255.255.240.0～255.255.255.0来建立子网。每个LAN可有2^12
－2个主机，且各子网可具有相同的主机地址。

假设一个组织有几个相对大的子网，每个子网包括了25台左右的计算机；而又有一些相对较小的子网，每个子网大概只有几台计算机。这种情况下，可以将一个C类地址分成6个子网（每个子网可以包含30台计算机），这样解决了很大的问题。但是出现了一个新的情况，那就是大的子网基本上完全利用了IP地址范围，但是小的子网却造成了许多IP地址的浪费。为了解决这个新的难题，避免任何的IP浪费，就出现了允许应用不同大小的子网掩码来对IP地址空间进行子网划分的解决方案。这种新的方案就叫作可变长子网掩码（VLSM）。

VLSM用一个十分直观的方法来表示，那就是在IP地址后面加上“／网络号及子网络号编址位数”。例如，193.168.125.0/27就表示前27位表示网络号。

例如，给定135.41.0.0/16的基于类的网络ID，所需的配置是为将来使用保留一半的地址，其余的生成15个子网，达到2000台主机。

由于要为将来使用保留一半的地址，完成了135.41.0.0的基于类的网络ID的1-位子网化，生成两个子网135.41.0.0/17和135.41.128.0/17，子网135.41.128.0/17被选作为将来使用所保留的地址部分；135.41.0.0/17被继续生成子网。

为达到划分2000台主机的15个子网的要求，需要将135.41.128.0/17的子网化的网络ID的4-位子网化。这就产生了16个子网（135.41.128.0/21，135.41.136.0/21，…，135.41.240.0/21，135.41.248.0/21），允许每个子网有2046台主机。最初的15个子网化的网络ID（135.41.128.0/21～135.41.240.0/21）被选定为网络ID，从而实现了要求。

现在的IP协议的版本号为4，所以也称之为IPv4，为了方便网络管理员阅读和理解，使用了4个十进制数中间加小数点“.”来表示。但随着因特网的膨胀，IPv4不论从地址空间上，还是协议的可用性上都无法满足因特网的新要求。这样出现一个新的IP协议IPv6，它使用了8个十六进制数中间加小数点“:”来表示。IPv6将原来的32位地址扩展成为128位地址，彻底解决了地址缺乏的问题。

3．NAT技术

因特网面临IP地址短缺的问题。解决这个问题有所谓长期的或短期的两种解决方案。长期的解决方案就是使用具有更大地址空间的IPv6协议，网络地址翻译（Network Address Translators, NAT）是许多短期的解决方案中的一种。

NAT技术最初提出的建议是在子网内部使用局部地址，而在子网外部使用少量的全局地址，通过路由器进行内部和外部地址的转换。NAT的实现主要有两种形式。

第一种应用是动态地址翻译（Dynamic Address Translation）。为此，首先引入存根域的概念，所谓存根域（Stub Domain），就是内部网络的抽象，这样的网络只处理源和目标都在子网内部的通信。任何时候存根域内只有一部分主机要与外界通信，甚至还有许多主机可能从不与外界通信，所以整个存根域只需共享少量的全局IP地址。存根域有一个边界路由器，由它来处理域内与外部的通信。假定：

●　m：需要翻译的内部地址数。

●　n：可用的全局地址数（NAT地址）。

当m∶n翻译满足条件（m≥1 and m≥n）时，可以把一个大的地址空间映像到一个小的地址空间。所有NAT地址放在一个缓冲区中，并在存根域的边界路由器中建立一个局部地址和全局地址的动态映像表，如图5-16所示。这个图显示的是把所有B类网络138.201中的IP地址翻译成C类网络178.201.112中的IP地址。这种NAT地址重用有如下特点。

（1）只要缓冲区中存在尚未使用的C类地址，任何从内向外的连接请求都可以得到响应，并且在边界路由器的动态NAT表中为之建立一个映像表项。

（2）如果内部主机的映像存在，就可以利用它建立连接。

（3）从外部访问内部主机是有条件的，即动态NAT表中必须存在该主机的映像。

动态地址翻译的好处是节约了全局适用的IP地址，而且不需要改变子网内部的任何配置，只需在边界路由器中设置一个动态地址变换表就可以工作了。

图5-16　动态网络地址翻译

另外一种特殊的NAT应用是m∶1翻译，这种技术也叫做伪装（masquerading），因为用一个路由器的IP地址可以把子网中所有主机的IP地址都隐藏起来。如果子网中有多个主机要同时通信，那么还要对端口号进行翻译，所以这种技术更经常被称为网络地址和端口翻译（Network Address Port Translation, NAPT）。在很多NAPT实现中，专门保留一部分端口号给伪装使用，叫做伪装端口号。图5-17中的NAT路由器中有一个伪装表，通过这个表对端口号进行翻译，从而隐藏了内部网络138.201中的所有主机。可以看出，这种方法有如下特点。

（1）出口分组的源地址被路由器的外部IP地址所代替，出口分组的源端口号被一个未使用的伪装端口号所代替。

（2）如果进来的分组的目标地址是本地路由器的IP地址，而目标端口号是路由器的伪装端口号，则NAT路由器就检查该分组是否为当前的一个伪装会话，并试图通过它的伪装表对IP地址和端口号进行翻译。

伪装技术可以作为一种安全手段使用，借以限制外部对内部主机的访问。另外，还可以用这种技术实现虚拟主机和虚拟路由，以便达到负载均衡和提高可靠性的目的。

图5-17　地址伪装

4．IPv6简介

IPv4（IP version 4）标准是20世纪70年代末期制定完成的。20世纪90年代初期，WWW的应用导致因特网爆炸性发展，随着因特网应用类型日趋复杂，终端形式特别是移动终端的多样化，全球独立IP地址的提供已经开始面临沉重的压力。IPv4将不能满足因特网长期发展的需要，必须立即开始下一代IP网络协议的研究。由此，IETF于1992年成立了IPNG（IP Next Generation）工作组；1994年夏，IPNG工作组提出了下一代IP网络协议（IP version 6, IPv6）的推荐版本；1995年夏，IPNG工作组完成了IPv6的协议文本；1995年—1999年完成了IETF要求的协议审定和测试；1999年成立了IPv6论坛，开始正式分配IPv6地址，IPv6的协议文本成为标准草案。

IPv6具有长达128位的地址空间，可以彻底解决IPv4地址不足的问题。由于IPv4地址是32位二进制，所能表示的IP地址个数为2^32
＝4294967296≈40亿个，因而在因特网上约有40亿个IP地址。由32位的IPv4升级至128位的IPv6，因特网中的IP地址从理论上讲会有2^128
＝3.4×10^38
个，如果整个地球表面（包括陆地和水面）都覆盖着计算机，那么IPv6允许每平方米有7×10^23
个IP地址，如果地址分配的速率是每秒分配100万个，则需要10^19
年的时间才能将所有地址分配完毕，可见，在想象得到的将来，IPv6的地址空间是不可能用完的。除此之外，IPv6还采用分级地址模式、高效IP包首部、服务质量、主机地址自动配置、认证和加密等许多技术。

1）IPv6数据包的格式

IPv6数据包有一个40字节的基本首部（base header），其后可允许有0个或多个扩展首部（extension header），再后面是数据。图5-18所示的是IPv6基本首部的格式。每个IPv6数据包都是从基本首部开始。IPv6基本首部的很多字段可以和IPv4首部中的字段直接对应。

图5-18　IPv6基本首部的格式

●　版本号：该字段占4位，说明了IP协议的版本。对IPv6而言，该字段值是0110，也就是十进制数的6。

●　通信类型：该字段占8位，其中优先级字段占4位，使源站能够指明数据包的流类型。首先，IPv6把流分成两大类，即可进行拥塞控制的和不可进行拥塞控制的。每一类又分为8个优先级。优先级的值越大，表明该分组越重要。对于可进行拥塞控制的业务，其优先级为0～7。当发生拥塞时，这类数据包的传输速率可以放慢。对于不可进行拥塞控制的业务，其优先级为8～15。这些都是实时性业务，如音频或视频业务的传输。这种业务的数据包发送速率是恒定的，即使丢掉了一些，也不进行重发。

●　流标号：该字段占20位。所谓流，就是因特网上从一个特定源站到一个特定目的站（单播或多播）的一系列数据包。所有属于同一个流的数据包都具有同样的流标号。源站在建立流时是在2^24
－1个流标号中随机选择一个流标号。流标号0保留作为指出没有采用流标号。源站随机地选择流标号并不会在计算机之间产生冲突，因为路由器在将一个特定的流与一个数据包相关联时，使用的是数据包的源地址和流标号的组合。

从一个源站发出的具有相同非0流标号的所有数据包，都必须具有相同的源地址和目的地址，以及相同的逐跳选项首部（若此首部存在）和路由选择首部（若此首部存在）。这样做的好处是当路由器处理数据包时，只要查一下流标号即可，而不必查看数据包首部中的其他内容。任何一个流标号都不具有特定的意义，源站应将它希望各路由器对其数据包进行的特殊处理写明在数据包的扩展首部中。

●　净负荷长度（payload length）：该字段占16位，指明除首部自身的长度外，IPv6数据包所载的字节数。可见，一个IPv6数据包可容纳64K字节长的数据。由于IPv6的首部长度是固定的，因此没有必要像IPv4那样指明数据包的总长度（首部与数据部分之和）。

●　下一个首部（next header）：该字段占8位，标识紧接着IPv6首部的扩展首部的类型。这个字段指明在基本首部后面紧接着的一个首部的类型。

●　跳数限制（hop limit）：该字段占8位，用来防止数据包在网络中无限期地存在。源站在每个数据包发出时即设定某个跳数限制。每一个路由器在转发数据包时，要先将跳数限制字段中的值减1。当跳数限制的值为0时，就要将此数据包丢弃。这相当于IPv4首部中的生存期字段，但比IPv4中的计算时间间隔要简单些。

●　源站IP地址：该字段占128位，是数据包的发送站的IP地址。

●　目的站IP地址：该字段占128位，是数据包的接收站的IP地址。

2）IPv6的地址表示

一般来讲，一个IPv6数据包的目的地址可以是以下三种基本类型地址之一。

●　单播（unicast）：传统的点对点通信。

●　多播（multicast）：一点对多点的通信，数据包交付到一组计算机中的每一个。IPv6没有采用广播的术语，而是将广播看作多播的一个特例。

●　任播（anycast）：这是IPv6增加的一种类型。任播的目的站是一组计算机，但数据包在交付时只交付给其中的一个，通常是距离最近的一个。

为了使地址的表示简洁些，IPv6使用冒号十六进制记法（colon hexadecimal notation, colon hex），它把每个16位用相应的十六进制表示，各组之间用冒号分隔。例如：

冒号十六进制记法允许0压缩（zero compression），即一连串连续的0可以用一对冒号所取代，例如：

FF05：0：0：0：0：0：0：B3　　可以改成：　FF05：：B3

为了保证0压缩有一个清晰的解释，建议中规定，在任一地址中，只能使用一次0压缩。该技术对已建议的分配策略特别有用，因为会有许多地址包含连续的0串。

另外，冒号十六进制记法可结合有点分十进制记法的后缀。这种结合在IPv4向IPv6的转换阶段特别有用。例如，下面的串是一个合法的冒号十六进制记法：

请注意，在这种记法中，虽然为冒号所分隔的每个值是一个16位的量，但每个点分十进制部分的值则指明一个字节的值。再使用0压缩即可得出：

5.5.3　Internet服务

作为全世界最大的国际性计算机网络的Internet，为全球的科研界、教育界和娱乐界等方方面面提供了极其丰富的信息资源和最先进的信息交流手段。在Internet上，时刻传送着大量的各种各样的信息，从电影、实况转播到最尖端的科学研究等无所不包，当然信息最多的还是科技信息，如计算机软件、科技论文、图书馆／出版社目录、最新科技动态、电子杂志、产品推销和网络新闻等。而这些内容均可由Internet服务来为用户提供。

使用传输控制协议或用户数据报协议时，Internet IP可支持65535种服务，这些服务是通过各个端口到名字实现的逻辑连接。端口分两类：一类是已知端口或称公共端口，端口号为0～1023，这些端口由Internet赋值地址和端口号的组织赋值；另一类是需在IANA注册登记端口号，为1024～65535。

前面介绍Internet网络接口层、网际层协议和传输层协议，本节主要介绍Internet的高层协议，如域名服务、远程登录服务、电子邮件服务、WWW服务和文件传输服务等。

1．域名服务

Internet中的域名地址与IP地址是等价的，它们之间是通过域名服务来完成映射变换的。实际上，DNS是一种分布式地址信息数据库系统，服务器中包含整个数据库的某部分信息，并供客户查询。DNS允许局部控制整个数据库的某些部分，但数据库的每一部分都可通过全网查询得到。

域名系统采用的是客户端／服务器模式，整个系统由解析器和域名服务器组成。解析器是客户方，它负责查询域名服务器、解释从服务器返回来的应答、将信息返回给请求方等工作。域名服务器是服务器方，它通常保存着一部分域名空间的全部信息，这部分域名空间称为区（zone）。一个域名服务器可以管理一个或多个区。域名服务器可以分为主服务器、Caching Only服务器和转发服务器（Forwarding Server）。

域名系统是一个分布式系统，其管理和控制也是分布式的。一个用户A在查找另一用户B时，域名系统的工作过程如下。

（1）解析器向本地域名服务器发出请求查阅用户B的域名。

（2）本地域名服务器向最高层域名服务器发出查询地址的请求。

（3）最高层域名服务器返回给本地域名服务器一个IP地址。

（4）本地域名服务器向组域名服务器发出查询地址的请求。

（5）组域名服务器返回给本地域名服务器一个IP地址。

（6）本地服务器向刚返回的域名服务器发出查询域名地址请求。

（7）IP地址返回给本地域名服务器。

（8）本地域名服务器将该地址返回给解析器。

因此，本地域名服务器为了得到一个IP地址常常需要查询多个域名服务器。于是，在查询地址的同时，本地域名服务器也就得到了许多其他域名服务器的信息，像它们的IP地址、所负责的区域等。本地域名服务器将这些信息连同最终查询到的主机IP地址全部存放在它的Cache中，以便将来参考。当下次解析器再查询与这些域名相关的信息时，就可以直接引用。这样就大大减少了查询时间。

因此，访问主机的时候只需要知道域名，通过DNS服务器将域名变换为IP地址。DNS所用的是UDP端口，端口号为53。

2．远程登录服务

远程登录服务是在Telnet协议的支持下，将用户计算机与远程主机连接起来，在远程计算机上运行程序，将相应的屏幕显示传送到本地机器，并将本地的输入送给远程计算机。由于这种服务基于Telnet协议且使用Telnet命令进行远程登录，故称为Telnet远程登录。

Telnet是基于客户端／服务器模式的服务系统，它由客户端软件、服务器软件以及Telnet通信协议三部分组成。远程计算机又称为Telnet主机或服务器，本地计算机作为Telnet客户端来使用，它起到远程主机的一台虚拟终端（仿真终端）的作用，通过它用户可以与主机上的其他用户一样共同使用该主机提供的服务和资源。

当用户使用Telnet登录远程主机时，该用户必须在这个远程主机上拥有合法的账号和相应的密码，否则远程主机将会拒绝登录。在运行Telnet客户程序后，首先应该建立与远程主机的TCP连接，从技术上讲，就是在一个特定的TCP端口（端口号一般为23）上打开一个套接字，如果远程主机上的服务器软件一直在这个周知的端口上侦听连接请求，则这个连接便会建立起来，此时用户的计算机就成为该远程主机的一个终端，便可以进行联机操作了，即以终端方式为用户提供人机界面。然后将用户输入的信息通过Telnet协议便可以传送给远程主机，主机在周知的TCP端口上侦听到用户的请求并处理后，将处理的结果通过Telnet协议返回给客户程序。最后客户端接收到远程主机发送来的信息，并经过适当的转换显示在用户计算机的屏幕上。

3．电子邮件服务

电子邮件（E-mail）就是利用计算机进行信息交换的电子媒体信件。它是随着计算机网络而出现的，并依靠网络的通信手段实现普通邮件信息的传输。它是最广泛的一种服务。

电子邮件是一种通过计算机网络与其他用户进行联系的快速、简便、高效、价廉的现代化通信手段。要想使用E-mail，首先必须拥有一个电子邮箱，它是由E-mail服务提供者为其用户建立在E-mail服务器磁盘上的专用于存放电子邮件的存储区域，并由E-mail服务器进行管理。用户将使用E-mail客户软件在自己的电子邮箱里收发电子邮件。电子邮件地址的一般格式：用户名＠主机名，例如fqzhang@china.com。

E-mail系统基于客户端／服务器模式，整个系统由E-mail客户端软件、E-mail服务器和通信协议三部分组成。E-mail客户端软件也称用户代理（User Agent），是用户用来收发和管理电子邮件的工具；E-mail服务器主要充当“邮局”的角色，它除了为用户提供电子邮箱外，还承担着信件的投递业务，当用户发送一个电子邮件后，E-mail服务器通过网络若干中间节点的“存储—转发”式的传递，最终把信件投递到目的地（收信人的电子邮箱）；E-mail服务器主要采用SMTP（简单邮件传输协议），本协议描述了电子邮件的信息格式及其传递处理方法，保证被传送的电子邮件能够正确地寻址和可靠地传输，它是面向文本的网络协议，其缺点是不能用来传送非ASCII码文本和非文字性附件，在日益发展的多媒体环境中以及人们关注的邮件私密性方面，更显出它的局限性。后来的一些协议，包括多用途Internet邮件扩充协议（MIME）及增强私密邮件保护协议（PEM），弥补了SMTP的缺点。而SMTP是用在大型多用户、多任务的操作系统环境中，将它用在PC上收信是十分困难的，所以在TCP/IP网络上的大多数邮件管理程序使用SMTP来发信，且采用POP（Post Office Protocol）（常用的是POP3）来保管用户未能及时取走的邮件。

POP协议有两个版本：POP2和POP3。目前使用的POP3既能与STMP共同使用，也可以单独使用，以传送和接收电子邮件。POP协议是一种简单的纯文本协议，每次传输以整个E-mail为单位，不能提供部分传输。

用户要传送E-mail，首先需在联网的计算机上使用邮件软件编好邮件正文，填好邮件收信人的E-mail地址、发信人电子邮件地址（或自动填上）、邮件的主题等内容，然后使用E-mail的发送命令发出。此时，E-mail发送端与接收端的计算机在工作时并不直接进行通信，而是在发信端计算机送出邮件后，先到达自己所注册的邮件服务器主机，再在网络传输过程中经过多个计算机和路由器的中转，到达目的地的邮件服务器主机，送进收信人的电子邮箱，最后当邮件的接收者上网并启动电子邮件管理程序，它就会自动检查邮件服务器中的电子邮箱，若发现新邮件，便会下载到自己的计算机上，完成接收邮件的任务。

简单邮件传送协议和用于接收邮件的POP3均是利用TCP端口。SMTP所用的端口号是25，POP3所用的端口号是110。

4．WWW（World Wide Web，万维网）服务

万维网是一种交互式图形界面的Internet服务，具有强大的信息连接功能，是目前Internet中最受欢迎的、增长速度最快的一种多媒体信息服务系统。

万维网是基于客户端／服务器模式的信息发送技术和超文本技术的综合，WWW服务器把信息组织为分布的超文本，这些信息节点可以是文本、子目录或信息指针。WWW浏览程序为用户提供基于超文本传输协议（Hyper Text Transfer Protocol, HTTP）的用户界面，WWW服务器的数据文件由超文本标记语言（Hyper Text Markup Language, HTML）描述，HTML利用统一资源定位器（URL）的指标是超媒体链接，并在文本内指向其他网络资源。

超文本传输协议是一个Internet上的应用层协议，是Web服务器和Web浏览器之间进行通信的语言。所有的Web服务器和Web浏览器必须遵循这一协议，才能发送或接收超文本文件。HTTP是客户端／服务器体系结构，提供信息资源的Web节点（即Web服务器），可称作HTTP服务器，Web浏览器则是HTTP服务器的客户。WWW上的信息检索服务系统就是遵循HTTP运行的。在HTTP的帮助下，用户可以只关心要检索的信息，而无须考虑这些信息存储在什么地方。

在Internet上，万维网整个系统由Web服务器、Web浏览器（Browser）和HTTP通信协议三部分组成。Web服务器提供信息资源；Web浏览器将信息显示出来；HTTP是为分布式超媒体信息系统而设计的一种网络协议，主要用于域名服务器和分布式对象管理，它能够传送任意类型数据对象，以满足Web服务器与客户端之间多媒体通信的需要，从而成为Internet中发布多媒体信息的主要协议。

统一资源定位器是在WWW中标识某一特定信息资源所在位置的字符串，是一个具有指针作用的地址标准。在WWW上查询信息，必不可少的一项操作是在浏览器中输入查询目标的地址，这个地址就是URL，也称Web地址，俗称“网址”，一个URL指定一个远程服务器域名和一个Web页。换言之，每个Web页都有唯一的URL。URL也可指向FTP、WAIS和gopher服务器代表的信息。通常，用户只需要了解和使用主页的URL，通过主页再访问其他页。当用户通过URL向WWW提出访问某种信息资源时，WWW的客户服务器程序自动查找资源所在的服务器地址，一旦找到，立即将资源调出供用户浏览。

使用WWW的浏览程序（例如Internet Explore、Netscape和Mosaic等），网页的超文本链接将引导用户找到所需要的信息资源。

如果已经是Internet的用户，只要在自己的计算机上运行一个客户程序（WWW浏览器），并给出需访问的URL地址，就可以尽情浏览这些来自远方或近邻的各种信息。WWW工作过程为：首先通过局域网或通过电话拨号连入Internet，并在本地计算机上运行WWW浏览器程序，然后根据想要获得的信息来源，在浏览器的指定位置输入WWW地址，并通过浏览器向Internet发出请求信息，此时网络中的IP路由器和服务器将按照地址把信息传递到所要求的WWW服务器中，而WWW服务器不断在一个周知的TCP端口（端口号为80）上侦听用户的连接请求，当服务器接收到请求后，找到所要求的WWW页面，最后服务器将找到的页面通过Internet传送回用户的计算机，浏览器接收传来的超文本文件，转换并显示在计算机屏幕上。

一个URL（Web地址）包括以下几部分：协议、主机域名、端口号（任选）、目录路径（任选）和一个文件名（任选）。其格式为：

其中，scheme指定服务连接的方式（协议），通常有下列几种。

●　file：本地计算机上的文件。

●　ftp：FTP服务器上的文件。

●　gopher：Gopher服务器上的文件。

●　http：WWW服务器上的超文本文件。

●　New：一个USenet的新闻组。

●　telnet：一个Telnet站点。

●　wais：一个WAIS服务器。

●　mailto：发送邮件给某人。

在地址的冒号之后通常是两个反斜线，表示后面是指定信息资源的位置，其后是一个可选的端口号，地址的最后部分是路径或文件名。如果端口号默认，表示使用与某种服务方式对应的标准端口号。根据查询要求不同，给出的URL中目录路径这一项可有可无。如果在查询中要求包括文件路径，那么在URL中就要具体指出要访问的文件名称。

下面是一些URL的例子：

5．文件传输服务

文件传输协议用来在计算机之间传输文件。由于Internet是一座装满了各种计算机文件的宝库，其中有免费和共享的软件、各种图片、声音、图像和动画文件，还有书籍和参考资料等，如果希望将它们下载到你的计算机上，其中最主要的方法之一是通过文件传输协议来实现，因此它是Internet中广为使用的一种服务。

通常，一个用户需要在FTP服务器中进行注册，即建立用户账号，在拥有合法的登录用户名和密码后，才有可能进行有效的FTP连接和登录。对于Internet中成千上万个FTP服务器来说，这就给提供FTP服务的管理员带来很大的麻烦，即需要为每一个使用FTP的用户提供一个账号，这样做显然是不现实的。实际上，Internet的FTP服务是一种匿名（anonymous）FTP服务，它设置了一个特殊的用户名——anonymous，供公众使用，任何用户都可以使用这个用户名与提供这种匿名FTP服务的主机建立连接，并共享这个主机对公众开放的资源。

匿名FTP的用户名是anonymous，而密码通常是guest或者是使用者的E-mail地址。当用户登录到匿名FTP服务器后，其工作方式与常规FTP相同。通常，出于安全的目的，大多数匿名FTP服务器只允许下载（download）文件，而不允许上传（upload）文件。也就是说，用户只能从匿名FTP服务器复制所需的文件，而不能将文件复制到匿名FTP服务器上。此外，匿名FTP服务器中的文件还加入一些保护性措施，确保这些文件不能被修改和删除，同时也可以防止计算机病毒的侵入。

FTP是基于客户端／服务器模式的服务系统，它由客户端软件、服务器软件和FTP通信协议3部分组成。FTP客户端软件运行在用户计算机上，在用户装入FTP客户端软件后，便可以通过使用FTP内部命令与远程FTP服务器采用FTP通信协议建立连接或文件传送；FTP服务器软件运行在远程主机上，并设置一个名叫anonymous的公共用户账号，向公众开放。

FTP在客户端与服务器的内部建立两条TCP连接：一条是控制连接，主要用于传输命令和参数（端口号为21）；另一条是数据连接，主要用于传送文件（端口号为20）。FTP服务器不断在21号端口上侦听用户的连接请求，当用户使用用户名anonymous和密码guest或者用户E-mail地址进行登录时，用户即发出连接请求，这样控制连接便建立起来，此时，用户名和密码将通过控制连接发送给服务器；服务器接收到这个请求后，便进行用户识别，然后向客户回送确认或拒绝的应答信息；用户看到登录成功的信息后，便可以发出文件传输的命令；服务器从控制连接上接收到文件名和传输命令（如get）后，便在20号端口发起数据连接，并在这个连接上将文件名所指明的文件传输给客户。只要用户不使用close或者其他命令关闭连接，便可以继续传输其他文件。

5.6　网络安全

随着网络的发展，网络的安全性显得非常重要。这是由于怀有恶意的攻击者窃取、修改网络上传输的信息，通过网络冒充合法用户非法进入远程主机，获取存储在主机中的机密信息，或者占用网络资源，阻止其他用户使用等。这些问题的产生对网络运营部门和用户的信息安全构成了威胁，影响了计算机网络的进一步推广应用。因此，如何对计算机网络上的各种非法行为进行主动控制和有效防御，是计算机网络安全亟待解决的问题。

5.6.1　网络安全概述

计算机网络安全是指计算机、网络系统的硬件、软件以及系统中的数据受到保护，不因偶然的或恶意的原因而遭到破坏、更改、泄露，确保系统能连续和可靠地运行，使网络服务不中断。网络安全从本质上来讲，就是网络上的信息安全。它涉及的领域相当广泛，这是因为在目前的各种通信网络中存在着各种各样的安全漏洞和威胁。从广义上来说，凡是涉及到网络上信息的保密性、完整性、可用性、真实性和可控性的相关技术和理论，都是网络安全所要研究的领域。

网络的安全之所以能受到威胁，主要原因有以下几个方面。

（1）计算机存储和处理是有关国家安全的政治、经济、军事和国防的情况以及一些部门、机构、组织的秘密信息或是个人的敏感信息、隐私，因此便成为某类人攻击的目标。

（2）随着Internet的发展，存取控制、逻辑连接的数目不断地增加，软件规模不断地膨胀，很容易使系统存在缺陷。

（3）在网络中，信息是从一台计算机的存储系统流向另一台计算机的存储系统，在大多数的情况下，信息离开信息源后必须经过多个中间节点才能到达目的地。在整个传输过程中，信息的发送者和接收者只能对发送和接收过程加以控制，而对中间传输过程则无权进行有效地控制。如果信息传输路由中存在不可信或具有攻击者的中间节点，信息的安全性就会受到严重的威胁。信息可能会被修改、破坏或泄露，因此，计算机网络的运行机制存在着严重的安全隐患。

（4）计算机网络的运行机制是协议控制机制，不同的节点之间的信息交换是按照事先定义好的固定机制，通过交换协议数据单元完成的。对于每个节点来说，通信即意味着对一系列从网络上到达的协议数据单元进行响应。根据以上的分析，这些从网上到达的协议数据单元的真实性是无法保证的。同时，由于协议本身固有的安全漏洞或协议实现中产生的安全漏洞也会造成许多安全问题。

在现有的网络环境中，由于存在不同的操作系统、不同厂家的硬件平台，故增加了网络安全的复杂性，其中有技术上和管理上的诸多原因。一个好的安全的网络应该是由主机系统、应用和服务、路由、网络、网络管理及管理制度等诸多因素决定的。系统、各种服务以及应用软件的漏洞随着时间的推延会越来越多地被发现，然后被修补，随着系统的升级会有许多新的漏洞又出现，再进行修补。因此，这是个长期的循环往复的过程。

网络安全涉及的主有内容如下。

（1）运行系统安全。即保证信息处理和传输系统的安全。包括计算机系统机房环境和系统设备的保护，计算机结构设计上的安全性考虑，硬件系统的可靠安全与运行，计算机操作系统和应用软件的安全，数据库系统的安全，电磁信息泄漏的防护等。

（2）信息系统的安全。包括用户口令鉴别、用户存取权限限制、方式控制、安全审计、安全问题跟踪、计算机病毒防治和数据加密。

（3）信息传播的安全。信息传播后果的安全，包括信息过滤、不良信息的过滤等。它侧重于防止和控制非法、有害的信息传播的后果。

（4）信息内容的安全。即狭义的信息安全。它侧重于信息的保密性、真实性、完整性。避免攻击者利用系统的安全漏洞进行窃听、冒充和诈骗等有损于合法用户的行为。本质上是保护用户的利益和隐私。

信息系统对安全的基本需求如下。

（1）保密性。保护资源免遭非授权用户“读出”。包括传输信息的加密、存储信息加密和防电磁泄露。

（2）完整性。保护资源免遭非授权用户“写入”。包括数据完整性、软件完整性、操作系统完整性、内存及磁盘完整性、信息交换的真实性和有效性。

（3）可用性。保护资源免遭破坏或干扰。防止病毒入侵和系统瘫痪，防止信道拥塞及拒绝服务，防止系统资源被非法抢占。

（4）可控性。对非法入侵提供检测与跟踪并能干预其入侵行为。

（5）可核查性。可追查安全事故的责任人。对违反安全策略的事件提供审计手段，能记录和追踪他们的活动。

网络的安全威胁来自于下列5类。

（1）物理威胁。物理威胁是指计算机硬件和存储介质不受到偷窃、废物搜寻及歼敌活动的威胁。这里的废物搜寻者就像捡破烂者，不过他在废纸篓中搜寻的是机密信息。

（2）网络攻击。计算机网络的使用对数据造成了新的安全威胁。攻击者可通过网络上存在着电子窃听、入侵拨号入网、冒名顶替等方式进行入侵攻击、偷窃与篡改。

（3）身份鉴别。由于身份鉴别通常是用设置口令的手段实现的，入侵者可通过口令圈套、密码破译等方法扰乱身份鉴别。

（4）编程威胁。所谓编程威胁，是指通过病毒进行攻击的一种方法。由于病毒是一种能进行自我复制的代码，在网间不断传播更具有危害性。

（5）系统漏洞。系统漏洞也称为系统陷阱或代码漏洞，这通常源于操作系统设计者有意设置的，目的是为了使用户在失去对系统的访问权时，仍有机会进入系统。入侵者可使用扫描器发现系统陷阱，从而进行攻击。

5.6.2　网络的信息安全

网络安全涉及到许多内容，如系统的软硬件安全，对网内的服务器与PC使用开机热启动密码、带复杂口令的屏幕保护且启动屏保时间不要太长、尽量不要使用文件共享功能（如需要，应设置访问控制权限和密码）、对重要文件设标识与验证置读写口令等。严格控制管理员级账户的使用范围、随时更新系统各种密码、规定可以访问的用户数。网络的信息安全归纳起来主要就是信息的存储安全和传输安全。

1．信息的存储安全

信息的存储安全包括信息使用的安全（如用户的标识与验证、用户存取权限限制、安全问题跟踪等）、计算机病毒防治、系统安全监控、数据的加密和防止非法的攻击等。

1）用户的标识与验证

用户的标识与验证主要是限制访问系统的人员。它是访问控制的基础，是对用户身份的合法性验证。方法有两种，一是基于人的物理特征的识别，包括签名识别法、指纹识别法和语音识别法；二是基于用户所拥有特殊安全物品的识别，包括智能IC卡识别法、磁条卡识别法。

2）用户存取权限限制

用户存取权限限制主要是限制进入系统的用户所能做的操作。存取控制是对所有的直接存取活动通过授权进行控制以保证计算机系统安全保密机制，是对处理状态下的信息进行保护。一般有两种方法：隔离控制法和限制权限法。

（1）隔离控制法。隔离控制法是在电子数据处理成分的周围建立屏障，以便在该环境中实施存取规则。隔离控制技术的主要实现方式包括物理隔离方式、时间隔离方式、逻辑隔离方式和密码技术隔离方式等。其中，物理隔离方式各过程使用不同的物理目标，是一种有效的方式。传统的多网环境一般通过运行两台计算机实现物理隔离。现在我国已经生产出了拥有自主知识产权的涉密计算机，它采用双硬盘物理隔离技术，通过运行一台计算机，即可在物理隔离的状态下切换信息网和公共信息网，实现一机双网或一机多网的功能。还有另外一种方式就是加装隔离卡，一块隔离卡带一块硬盘、一块网卡，连同本机自带的硬盘网卡，使用不同的网络环境。当然，物理隔离方式对于系统的要求比较高，必须采用两整套互不相关的设备，其人力、物力、财力的投入都是比较大的。但也是很有效的方式，因为两者就如两条平行线，永不交叉，自然也就安全了。

（2）限制权限法。限制权限法是有效地限制进入系统的用户所进行的操作。即对用户进行分类管理，安全密级授权不同的用户分在不同类别；对目录、文件的访问控制进行严格的权限控制，防止越权操作；放置在临时目录或通信缓冲区的文件要加密，用完尽快移走或删除。

3）系统安全监控

系统必须建立一套安全监控系统，全面监控系统的活动，并随时检查系统的使用情况，一旦有非法入侵者进入系统，能及时发现并采取相应措施，确定和堵塞安全及保密的漏洞。应当建立完善的审计系统和日志管理系统，利用日志和审计功能对系统进行安全监控。管理员还应当经常作到如下方面。

（1）监控当前正在进行的进程，正在登录的用户情况。

（2）检查文件的所有者、授权、修改日期情况和文件的特定访问控制属性。

（3）检查系统命令安全配置文件、口令文件、核心启动运行文件、任何可执行文件的修改情况。

（4）检查用户登录的历史记录和超级用户登录的记录。如发现异常，及时处理。

4）计算机病毒防治

计算机网络服务器必须加装网络病毒自动检测系统，以保护网络系统的安全，防范计算机病毒的侵袭，并且必须定期更新网络病毒检测系统。

由于计算机病毒具有隐蔽性、传染性、潜伏性、触发性和破坏性等特点，所以需要建立计算机病毒防治管理制度。

（1）经常从软件供应商网站下载、安装安全补丁程序和升级杀毒软件。

（2）定期检查敏感文件。对系统的一些敏感文件定期进行检查，保证及时发现已感染的病毒和黑客程序。

（3）使用高强度的口令。尽量选择难于猜测的口令，对不同的账号选用不同的口令。

（4）经常备份重要数据，要做到每天坚持备份。

（5）选择、安装经过公安部认证的防病毒软件，定期对整个硬盘进行病毒检测、清除工作。

（6）可以在计算机和因特网之间安装使用防火墙，提高系统的安全性。

（7）当计算机不使用时，不要接入因特网，一定要断掉连接。

（8）重要的计算机系统和网络一定要严格与因特网物理隔离。

（9）不要打开陌生人发来的电子邮件，无论它们有多么诱人的标题或者附件。同时也要小心处理来自于熟人的邮件附件。

（10）正确配置系统和使用病毒防治产品。正确配置系统，充分利用系统提供的安全机制，提高系统防范病毒的能力，减少病毒侵害事件。了解所选用防病毒产品的技术特点，正确配置以保护自身系统的安全。

5）数据的加密

数据的加密主要是防止非法窃取或调用，包括文件信息的加密、数据库数据的安全与加密、磁介质加密等。

（1）文件信息的加密。一是文件加密，即对文件的代码或数据本身进行的数据源加密。二是文件名加密，就是利用文件名屏幕显示形式的变换，使得实际注册的文件名与显示的不相符或者根本不显示，造成无法访问文件。

（2）数据库数据的安全与加密。通过对数据库系统的管理和对数据库数据的加密来实现，包括用户身份的识别和确认；访问操作的鉴别和控制；审计和跟踪；数据库外加密；数据库内加密等。

（3）磁介质加密。加密的目的在于防复制。磁介质加密的主要方法包括固化部分程序、激光穿孔加密、掩膜加密和芯片加密、修改磁介质参数表等。

6）计算机网络安全

计算机网络安全主要是指计算机网络抵御来自外界侵袭等应采取的安全措施。它是网络信息安全的最外一层防线。目前主要通过采用安全防火墙系统、安全代理服务器和安全加密网关等来实现。主要包括网络边界的安全和网络内部的安全控制及防范。

（1）网络边界的安全。网络边界主要是指本单位（或部门）的网络与外界网络或Internet互联的出口边界。其安全主要是针对经边界进出访问和传输数据包时要采取的控制和防范措施。政府各部门的计算机网络应当采用统一的国际互联网出口，以便加强管理。计算机网络与Internet或外界其他网络接入口处必须设置安全防火墙系统，该防火墙要具有加密功能或安全加密网关。要定期扫描网络的安全漏洞，及时消除网络安全的隐患。Internet或外界其他网络上的授权用户要通过安全防火墙或安全加密网关远程进入政府网络时，必须配备电子印章认证系统，只有认证系统通过的授权用户才可进入。

计算机网络系统一般不要设置拨号访问服务器和提供远程Modem接入，如确需设置，必须采用如下措施：设置访问控制服务器，对拨号上网的用户身份、电话号码等进行验证；要求拨号用户采用比较安全的口令，并确保不把用户名和口令外传给任何其他人；在拨号访问服务器和网络之间设置安全防火墙，对远程访问进行控制和监测；对拨号上网的电话号码严格保密。

（2）网络内部的安全控制和防范。网络内部安全是指应采取防范措施以控制外界远程用户（或网络）对网络内部数据存取。常用的技术手段包括网络安全监测报警系统、数据加／解密卡和电子印章系统等。具体应采取以下措施：在网络中应采取对信息进行相应级别的数据源加密；对信息所需采用的各种加解密设备采用统一的加密算法和密钥管理，并具有权限分级，以适合不同级别的用户存取，同时密钥必须定期更换；加装网络安全监测报警系统，定期扫描网络的安全漏洞，一旦有非法用户进入网络读取信息或篡改网络系统配置，则自动报警；必须对计算机网络用户读取信息进行身份认证，并由此获得相应身份的读取权限，以适应不同级别的用户读取不同级别的信息；未经或通不过系统认证的用户，将无权读取信息，网络边界安全设备将禁止信息传出网络；信息从网络边界传输出去以前，必须经过相应的数据源加密，否则将无法通过网络边界安全设备。

2．信息的传输安全

信息的传输加密是面向线路的加密措施，有链路加密、节点加密和端—端加密三种。

（1）链路加密。链路加密只对两个节点之间（不含信息源和目的地两个端点本身）的通信信道线路上所传输的信息进行加密保护。它是一种链式连接的加密方式，属于公共加密。其缺点是每个节点要配置加密单元（信道加密机），相邻节点必须使用相同的密钥，节点的数据是明文。

（2）节点加密。节点加密的加、解密都在节点中进行，即每个节点里装有加、解密的保护装置，用于完成一个密钥向另一个密钥的转换。这样，节点中的数据不会出现明文。但由于每个节点要加装安全单元或保护装置，因此需要公共网络提供配合。

（3）端—端加密。端—端加密为系统网络提供从信息源到目的地传送的数据的加密保护。可以是从主机到主机，终端到终端，终端到主机或到处理进程，或从数据的处理进程到处理进程，而不管数据在传送中经过了多少中间节点，数据不被解密。用户或主机都可独自采用这种加密技术而不会影响别的用户或主机，这比较适于在分组交换网中采用。

加密措施是保护信息的最后防线，被公认为是保护信息传输唯一实用的方法。在物理安全不足的地方，加密是保护存储信息的十分有效而经济的方法。对信息进行加密保护是在密钥的控制下，通过密码算法将敏感的机密明文数据变换成不可懂的密文数据，称为加密（Encryption）。对一些重要的口令、数据、电子邮件用加密软件进行加密后，再发送或保存。信息即使被截获，攻击者也要耗费时间、精力去解密，从而为采取补救措施赢得了时间。

信息的传输加密技术是指发送方用加密密钥通过加密设备或算法，将信息加密后发送出去。接收方在收到密文后，用解密密钥将密文解密，恢复为明文。如果传输中有人窃取，他只能得到无法理解的密文，从而对信息起到保密作用。基本的加密算法有两种：对称密钥加密和非对称密钥加密，用于保证电子商务中数据的保密性、完整性、真实性和非抵赖服务。

对称密钥加密也叫私有密钥加密（Private Key Encryption），即发送和接收数据的双方必须使用相同的／对称的密钥对明文进行加密和解密运算。最著名的对称密钥加密标准是数据加密标准。DES是一种使用56个数据位的密钥来操作64位数据块的块加密算法，由IBM公司推出，可同时对大量数据进行快速加密。DES算法曾经过广泛的分析和测试，被认为是一种非常安全的系统。采用这种方法的主要问题是密钥的生成、注入、存储、管理和分发等很复杂，特别是随着用户的增加，密钥的需求量成倍增加。在网络通信中，大量密钥的分配是一个难以解决的问题。对称密钥加密的特点是简单，但用户必须也只能让接收人知道自己使用的密钥，双方必须共同保守密钥，任何一方失误均会导致密钥的泄露。目前已有一些比DES算法更安全的对称密钥加密算法，如IDEA算法，RC2、RC4算法和Skipjack算法等。

非对称密钥加密也叫公开密钥加密（Public Key Encryption），由美国斯坦福大学赫尔曼教授于1977年提出的。它主要指每个人都有一对唯一对应的密钥：公开密钥和私有密钥，公钥对外公开，私钥由个人秘密保存。用其中一把密钥来加密，就只能用另一把密钥来解密。加密密钥对外是公开的，使任何用户都可将传送给此用户的信息用公开密钥加密发送，而用户唯一保存的私人密钥是保密的，也只有它能将密文复原、解密。虽然解密密钥理论上可由加密密钥推算出来，但这种算法设计在实际上是不可能的，或者虽然能够推算出来，但要花费很长的时间而成为不可行的。所以将加密密钥公开也不会危害密钥的安全。公开密钥加密技术解决了密钥的发布和管理问题。使用公开密钥技术，进行数据通信的双方可以安全地确认对方身份和公开密钥，提供通信的可鉴别性。非对称加密算法主要有RSA、DSA、Diffie-Hellman、PKCS和PGP等。

5.6.3　防火墙技术

所谓防火墙（Firewall），是建立在内外网络边界上的过滤封锁机制，它认为内部网络是安全和可信赖的，而外部网络被认为是不安全和不可信赖的。防火墙的作用是防止不希望的、未经授权的数据包进出被保护的内部网络，通过边界控制强化内部网络的安全策略。它的实现有多种形式，但原理很简单，可以把它想象为一对开关，其中一个用来阻止传输，另一个用来允许传输。防火墙作为网络安全体系的基础和核心控制设备，贯穿于受控网络通信主干线，对通过受控干线的任何通信行为进行安全处理，如控制、审计、报警和反应等，同时也承担着繁重的通信任务。由于其自身处于网络系统中的敏感位置，自身还要面对各种安全威胁，因此，选用一个安全、稳定和可靠的防火墙产品，其重要性不言而喻。

在网络层，防火墙被用来处理信息在内外网络边界的流动，它可以确定来自哪些地址的信息可以通过或者禁止哪些目的地址的主机。在传输层，这个连接可以被端到端的加密，也就是进程到进程的加密。在应用层，它可以进行用户级的身份认证、日志记录和账号管理等。因此，防火墙技术简单来说，就是一套身份认证、加密、数字签名和内容检查集成为一体的安全防范措施。所有来自Internet的传输信息和内部网络发出的传输信息都要穿过防火墙，由防火墙进行分析，以确保它们符合站点设定的安全策略，以提供一种内部节点或网络与Internet的安全屏障。

1．防火墙的分类

防火墙技术经历了包过滤、应用代理网关和状态检测三个发展阶段。包过滤型的防火墙通常直接转发报文，它对用户完全透明，速度较快；应用代理网关防火墙是通过服务器建立连接的，可以有更强的身份验证和注册功能；状态检测防火墙是在其核心部分建立状态连接表，并将进出网络的数据当成一个个的会话，利用状态表跟踪每一个会话状态。状态检测对每一个包的检查不仅根据规则表，更考虑了数据包是否符合会话所处的状态，因此提供了完整的对传输层的控制能力。

1）包过滤型防火墙

包过滤防火墙一般有一个包检查块（通常称为包过滤器），数据包过滤可以根据数据包头中的各项信息来控制站点与站点、站点与网络、网络与网络之间的相互访问，但无法控制传输数据的内容，因为内容是应用层数据，而包过滤器处在网络层和数据链路层（即TCP和IP层）之间。通过检查模块，防火墙能够拦截和检查所有出站和进站的数据，它首先打开包，取出包头，根据包头的信息确定该包是否符合包过滤规则，并进行记录。对于不符合规则的包，应进行报警并丢弃该包。

包过滤防火墙工作在网络层，对数据包的源及目地IP具有识别和控制作用，对于传输层，也只能识别数据包是TCP还是UDP及所用的端口信息。由于只对数据包的IP地址、TCP/UDP协议和端口进行分析，如果一条规则阻止包传输或接收，则此包便不被允许通过，否则该包可以被继续处理。包过滤防火墙的处理速度较快，并且易于配置。

包过滤防火墙的优点是防火墙对每条传入和传出网络的包实行低水平控制；每个IP包的字段都被检查，例如源地址、目的地址、协议和端口等；防火墙可以识别和丢弃带欺骗性源IP地址的包；包过滤防火墙是两个网络之间访问的唯一来源；包过滤通常被包含在路由器数据包中，所以不必额外的系统来处理这个特征。

包过滤防火墙的缺点是不能防范黑客攻击，因为网管不可能区分出可信网络与不可信网络的界限；不支持应用层协议，因为它不认识数据包中的应用层协议，访问控制粒度太粗糙；不能处理新的安全威胁。

2）应用代理网关防火墙

应用代理网关防火墙彻底隔断内网与外网的直接通信，内网用户对外网的访问变成防火墙对外网的访问，然后再由防火墙转发给内网用户。所有通信都必须经应用层代理软件转发，访问者任何时候都不能与服务器建立直接的TCP连接，应用层的协议会话过程必须符合代理的安全策略要求。

应用代理网关的优点是可以检查应用层、传输层和网络层的协议特征，对数据包的检测能力比较强。其缺点如下。

（1）难于配置。由于每个应用都要求单独的代理进程，这就要求网管能理解每项应用协议的弱点，并能合理地配置安全策略。由于配置繁琐，难于理解，容易出现配置失误，最终影响内网的安全防范能力。

（2）处理速度非常慢。断掉所有的连接，由防火墙重新建立连接，理论上可以使应用代理防火墙具有极高的安全性。但是实际应用中并不可行，因为对于内网的每个Web访问请求，应用代理都需要开一个单独的代理进程，它要保护内网的Web服务器、数据库服务器、文件服务器、邮件服务器及业务程序等，这就需要建立一个个的服务代理，以处理客户端的访问请求。这样，应用代理的处理延迟会很大，内网用户的正常Web访问不能及时得到响应。

总之，应用代理防火墙不能支持大规模的并发连接，对速度要求高的行业不能使用这类防火墙。另外，防火墙核心要求预先内置一些已知应用程序的代理，使得一些新出现的应用在代理防火墙内被无情地阻断，不能很好地支持新应用。

3）状态检测技术防火墙

状态检测技术防火墙结合了代理防火墙的安全性和包过滤防火墙的高速度等优点，在不损失安全性的基础上将代理防火墙的性能提高了10倍。

Internet上使用的是TCP/IP，TCP的每个可靠连接均需要经过“客户端同步请求”、“服务器应答”和“客户端再应答”三次握手。例如，最常用到的Web浏览、文件下载和收发邮件等都要经过这三次握手。这反映出数据包并不是独立的，而是前后之间有着密切的状态联系，基于这种状态变化，引出了状态检测技术。

状态检测防火墙摒弃了包过滤防火墙仅考查数据包的IP地址等几个参数，而不关心数据包连接状态变化的缺点，在防火墙的核心部分建立状态连接表，并将进出网络的数据当成一个个的会话，利用状态表跟踪每一个会话状态。状态检测对每一个包的检查不仅根据规则表，更考虑了数据包是否符合会话所处的状态，因此提供了完整的对传输层的控制能力。状态检测防火墙在提高安全防范能力的同时也改进了流量处理速度。因为它采用了一系列优化技术，使防火墙性能大幅度提升，能应用在各类网络环境中，尤其是在一些规则复杂的大型网络上。

2．典型防火墙的体系结构

一个防火墙系统通常是由过滤路由器和代理服务器组成。过滤路由器是一个多端口的IP路由器，它能够拦截和检查所有出站和进站的数据。它首先打开IP包，取出包头，根据包头的信息（如IP源地址、IP目标地址）确定该包是否符合包过滤规则（如对包报头进行语法分析，阻止或允许包传输或接收），并进行记录。对于符合规则的包，则应该进行转发，否则应进行报警并丢弃该包。代理服务防火墙使用了与包过滤器不同的方法。代理服务器使用一个客户程序与特定的中间节点（防火墙）连接，然后中间节点与期望的服务器进行实际连接。与包过滤器所不同的是，使用这种类型的防火墙，内部与外部网络之间不存在直接连接。因此，即使防火墙发生了问题，外部网络也无法获得与被保护网络的连接。代理提供了详细的注册及审计功能，这大大提高了网络的安全性，也为改进现有软件的安全性能提供了可能。它是基于特定协议的，如FTP、HTTP等。为了通过代理支持一个新的协议，必须改进代理服务器以适应新协议。典型防火墙的体系结构包括包过滤路由器、双宿主主机、被屏蔽主机和被屏蔽子网等类型。

1）包过滤路由器

包过滤路由器又称屏蔽路由器，是最简单也是最常用的防火墙。它一般作用在网络层，对进出内部网络的所有信息进行分析，并按照一定的安全策略（过滤规则）对进出内部网络的信息进行限制。包过滤的核心就是安全策略，即包过滤算法的设计。包过滤型防火墙往往可用一台过滤路由器来实现，对所接收的每个数据包作允许拒绝的决定。如图5-19所示。

图5-19　包过滤路由器防火墙

采用包过滤路由器的防火墙优点在于速度快、实现方便。缺点是安全性能差；不同操作系统环境下TCP和UDP端口号所代表的应用服务协议类型有所不同，故兼容性差；没有或只有较少的日志记录能力。

2）双宿主主机

双宿主主机结构是围绕着至少具有两个网络接口的双宿主主机（又称堡垒主机）而构成的，每一个接口都连接在物理和逻辑上分离的不同网段，代理服务器软件在双宿主主机上运行，如图5-20所示。双宿主主机内外的网络均可与双宿主主机实施通信，但内外网络之间不可直接通信，内外网络之间的IP数据流被双宿主主机完全切断。主机结构采用主机取代路由器执行安全控制功能，受保护网除了看到堡垒主机外，不能看到其他任何系统。同时，堡垒主机不转发TCP/IP通信报文，网络中的所有服务都必须由此主机的相应代理程序来支持。

图5-20　双宿主主机防火墙

双宿主主机防火墙的优点是堡垒主机运行的系统软件可用于维护系统日志、硬件备份日志和远程日志等，有利于网络管理员的日后检查。其缺点是由于双宿主主机是唯一隔开内部网和外部因特网之间的屏障，若入侵者得到了双宿主主机的访问权，内部网络就会被入侵，所以为了保证内部网的安全，双宿主主机首先要禁止网络层的路由功能，还应具有强大的身份认证系统，尽量减少防火墙上用户的账户数。

3）屏蔽主机网关

屏蔽主机网关防火墙是由过滤路由器和应用网关组成。过滤路由器的作用是进行包过滤；应用网关的作用是代理服务，即在内部网络与外部网络之间建立两道安全屏障。屏蔽主机网关防火墙的结构如图5-21所示。

图5-21　屏蔽主机网关防火墙

对于这种防火墙系统，堡垒主机配置在内部网络上，而包过滤路由器则放置在内部网络和Internet之间。在路由器上进行规则配置，使得外部系统只能访问堡垒主机，去往内部系统上其他主机的信息全部被阻塞。由于内部主机与堡垒主机处于同一个网络，内部系统是否允许直接访问Internet，或者是要求使用堡垒主机上的代理服务来访问Internet由机构的安全策略来决定。对路由器的过滤规则进行配置，使得其只接受来自堡垒主机的内部数据包，就可以强制内部用户使用代理服务。

屏蔽主机网关防火墙的优点是安全等级较高。可以提供公开的信息服务的服务器，如Web、FTP等，可以放置在由包过滤路由器和堡垒主机共用的网段上。如果要求有特别高的安全特性，可以让堡垒主机运行代理服务，使得内部和外部用户在与信息服务器通信之前，必须先访问堡垒主机。如果较低的安全等级已经足够，则将路由器配置为让外部用户直接去访问公共的信息服务器。缺点是配置工作复杂。过滤路由器是否正确配置是这种防火墙安全与否的关键，过滤路由器的路由表应当受到严格的保护，否则如果遭到破坏，则数据包就不会被路由到堡垒主机上。

4）被屏蔽子网

被屏蔽子网防火墙系统是由两个包过滤路由器和一个应用网关（堡垒主机）组成。包过滤路由器分别位于周边网与内部网、周边网与外部网之间，而应用网关居于两个包过滤路由器的中间，形成了一个“非军事区（DMZ）”，建立了一个最安全的防火墙系统。如图5-22所示。

图5-22　被屏蔽子网防火墙

对于进来的信息，外面的这个路由器用于防范通常的外部攻击（如源地址欺骗和源路由攻击），并管理Internet到DMZ网络的访问。它只允许外部系统访问堡垒主机（还可能有信息服务器）。里面的这个路由器提供第二层防御，只接受源于堡垒主机的数据包，负责的是管理DMZ到内部网络的访问。对于去往Internet的数据包，里面的路由器管理内部网络到DMZ网络的访问。它允许内部系统只访问堡垒主机（还可能有信息服务器）。外面的路由器上的过滤规则要求使用代理服务（只接受来自堡垒主机的去往Internet的数据包）。

被屏蔽子网防火墙系统的优势如下。

（1）入侵者必须突破三个不同的设备（外部路由器、堡垒主机和内部路由器）才能侵袭内部网络。

（2）由于外部路由器只能向Internet通告DMZ网络的存在，Internet上的系统不需要有路由器与内部网络相对。这样，网络管理员就可以保证内部网络是“不可见”的，并且只有在DMZ网络上选定的系统才对Internet开放。

（3）由于内部路由器只向内部网络通告DMZ网络的存在，内部网络上的系统不能直接通往Internet，这样就保证了内部网络上的用户必须通过驻留在堡垒主机上的代理服务才能访问Internet。

（4）包过滤路由器直接将数据引向DMZ网络上所指定的系统，消除了堡垒主机双宿的必要。

（5）内部路由器在作为内部网络和Internet之间最后的防火墙系统时，能够支持比双宿堡垒主机更大的数据包吞吐量。

（6）由于DMZ网络是一个与内部网络不同的网络，NAT可以安装在堡垒主机上，从而避免在内部网络上重新编址或重新划分子网。

第6章　多媒体基础知识

多媒体是对多种媒体的融合，是能够同时采集、处理、编辑、存储和展示两个或以上不同类型信息媒体的技术，这些信息媒体包括文字、声音、图形、图像、动画和活动影像等。本章主要介绍多媒体声音、图形图像、动画与视频、多媒体网络及多媒体计算机方面的基础知识。

6.1　多媒体的基本概念

6.1.1　多媒体基本概念

媒体（Media）通常包括两方面的含义：一是指信息的物理载体（即存储和传递信息的实体），如手册、磁盘、光盘、磁带以及相关的播放设备等；二是指承载信息的载体，即信息的表现形式（或者说传播形式），如文字、声音、图像、动画和视频等，即CCITT定义的存储媒体和表示媒体。

1．媒体的分类

表示媒体又可以分为三种类型：视觉类媒体（位图图像、矢量图形、图表、符号、视频和动画）、听觉类媒体（音响、语音和音乐）和触觉类媒体（点、位置跟踪；力反馈与运动反馈）。视觉和听觉类媒体是信息传播的内容，触觉类媒体是实现人机交互的手段。

媒体的概念范围相当广泛，按照国际电话电报咨询委员会（Consultative Committee on International Telephone and Telegraph, CCITT）的定义，媒体可以归类如下。

（1）感觉媒体（Perception Medium）。指直接作用于人的感觉器官，使人产生直接感觉的媒体。如引起听觉反应的声音，引起视觉反应的图像等。

（2）表示媒体（representation Medium）。指传输感觉媒体的中介媒体，即用于数据交换的编码。如图像编码（JPEG、MPEG）、文本编码（ASCII、GB2312）和声音编码等。

（3）表现媒体（Presentation Medium）。指进行信息输入和输出的媒体。如键盘、鼠标、扫描仪、话筒和摄像机等为输入媒体；显示器、打印机和喇叭等为输出媒体。

（4）存储媒体（Storage Medium）。指用于存储表示媒体的物理介质，如硬盘、软盘、磁盘、光盘、ROM及RAM等。

（5）传输媒体（Transmission Medium）。指传输表示媒体的物理介质，如电缆、光缆和电磁波等。

为了使不同的媒体能够有机地连接起来，往往按照实际需要建立一种链接机制或结构，把这种链接机制或结构称为超媒体（HyperMedia）。在计算机中，超媒体是一个信息存储和检索系统，它把文字、图形、图像、动画、声音和视频等媒体集成为一个相关的基本信息系统。通常，如果信息主要是以文字的形式表示，那么称为超文本；如果还包含有图形、影视、动画、音乐或其他媒体，一般称为超媒体。因特网的WWW应用是超媒体技术的最好例子。

2．多媒体的特征

在计算机领域中，“多媒体”常常被当作为“多媒体技术”的同义词。多媒体技术就是指利用计算机技术把文本、图形、图像、声音、动画和电视等多种媒体综合起来，使多种信息建立逻辑连接，并能对它们进行获取、压缩、加工处理、存储，集成为一个具有交互性的系统。多媒体技术的内涵和范围极其广泛，所涉及的技术也极为广泛，其主要特性如下。

（1）多样性。主要表现在信息媒体的多样化。多样性使得计算机处理的信息空间范围扩大，不再局限于数值、文本或特殊对待的图形和图像，可以借助于视觉、听觉和触觉等多感觉形式实现信息的接收、产生和交流。

（2）集成性。主要表现在多媒体信息（文字、图形、图像、语音和视频等信息）的集成和操作这些媒体信息的软件和设备的集成。多媒体信息的集成是将各种信息媒体按照一定的数据模型和组织结构集成为一个有机的整体。操作媒体信息的软件和设备的集成是将与多媒体相关的各种硬件和软件集成为一个理想的环境，使得充分共享和操作使用多媒体信息。

（3）交互性。这是多媒体应用有别于传统信息交流媒体的主要特点之一。传统信息交流媒体只能单向地、被动地传播信息，而多媒体技术引入交互性后则可实现人对信息的主动选择、使用、加工和控制。通过交互与反馈，达到更加有效地控制和使用信息，为人们提供发挥创造力的环境，增强了人们的参与感，同时也为多媒体技术应用开辟了更加广阔的领域。

（4）非线性。多媒体技术的非线性特点将改变人们传统循序性的读写模式。以往人们读写方式大都采用章、节、页的框架，循序渐进地获取知识，而多媒体技术将借助超文本链接的方法，把内容以一种更灵活、更具变化的方式呈现给读者。

（5）实时性。是指在人的感官系统允许的情况下进行多媒体处理和交互。当人们给出操作命令时，相应的多媒体信息都能够得到实时控制。

（6）信息使用的方便性。用户可以按照自己的需要、兴趣、任务要求、偏爱和认知特点来使用信息，获取图、文、声等信息表现形式。

（7）信息结构的动态性。用户可以按照自己的目的和认知特征重新组织信息，即增加、删除或修改节点，重新建立链接等。

6.1.2　虚拟现实基本概念

虚拟现实（virtual reality, VR）将是继多媒体、计算机网络之后，最具有应用前景的一种技术。它可应用于建模与仿真、科学计算可视化、设计与规划、教育与训练、医学、艺术与娱乐等方面。虚拟现实技术是一项综合的技术，涉及计算机科学、电子学、心理学、计算机图形学、人机接口技术、传感技术及人工智能技术等。这种技术的特点在于，运用计算机对现实世界进行全面仿真，创建与现实社会类似的环境，通过多种传感设备使用户“投入”到该环境中，实现用户与该环境直接进行自然交互。

1．虚拟现实技术的主要特征

虚拟现实是将现实世界的多维信息映射到计算机的数字空间生成相应的虚拟世界，主要包括基本模型构建、空间跟踪、声音定位、视觉跟踪和视点感应等关键技术。其主要特征有如下三个方面。

（1）多感知。就是说除了一般计算机所具有的视觉感知外，还有听觉感知、力觉感知、触觉感知、运动感知，甚至包括味觉感知和嗅觉感知等。理想的虚拟现实就是应该具有人所具有的感知功能。

（2）沉浸（又称临场感）。是指用户感到作为主角存在于模拟环境中的真实程度。理想的模拟环境应该达到使用户难以分辨真假的程度。

（3）交互。是指用户对模拟环境内物体的可操作程度和从环境得到反馈的自然程度（包括实时性）。例如，用户可以用手去直接抓取环境中的物体，这时手有握着东西的感觉，并可以感觉物体的重量，视场中的物体也随着手的移动而移动。

在虚拟环境中获取视觉、听觉、力觉和触觉等感官认知等关键技术，是保证虚拟世界中的事物所产生的各种刺激以尽可能自然的方式反馈给用户。

（1）视觉感知。虚拟环境中大部分具有一定形状的物体或现象，可以通过多种途径使用户产生真实感很强的视觉感知。CRT显示器、大屏幕投影、多方位电子墙、立体眼镜和头盔显示器（HMD）等是VR系统中常见的显示设备。不同的头盔显示器具有不同的显示技术，根据光学图像被提供的方式，头盔显示设备可分为投影式和直视式。

（2）听觉感知。听觉是仅次于视觉的感知途径，虚拟环境的声音效果，可以弥补视觉效果的不足，增强环境逼真度。用户所感受的三维立体声音，有助于用户在操作中对声音定位。

（3）力觉和触觉感知。能否让参与者产生“沉浸”感的关键因素之一是参与者能否在操纵虚拟物体的同时，感受到虚拟物体的反作用力，从而产生触觉和力觉感知。例如，当用手扳动虚拟驾驶系统的汽车档位杆时，手能感觉到档位杆的震动和松紧。力觉感知主要由计算机通过力反馈手套、力反馈操纵杆对手指产生运动阻尼，从而使用户感受到作用力的方向和大小。

2．虚拟现实技术的分类

虚拟现实技术出现了多样化的发展趋势，虚拟实现技术不仅仅是指那些戴着头盔和手套等技术，而且还应该包括一切与之有关的具有自然模拟、逼真体验的技术与方法，它的根本目标就是达到真实体验和基于自然技能的人机交互。能够达到或者部分达到这样目标的系统就称为虚拟现实系统。根据用户参与VR的不同形式以及沉浸的程度不同，可以把各种类型的虚拟现实技术大致划分为4类。

（1）桌面虚拟现实。桌面虚拟现实利用个人计算机和低级工作站进行仿真，将计算机的屏幕作为用户观察虚拟境界的一个窗口。通过各种输入设备实现与虚拟现实世界的充分交互，这些外部设备包括鼠标、追踪球和力矩球等。它要求参与者使用输入设备，通过计算机屏幕观察360°范围内的虚拟境界，并操纵其中的物体，但这时参与者缺少完全的沉浸，因为它仍然会受到周围现实环境的干扰。桌面虚拟现实的最大特点是缺乏真实的现实体验，但是成本也相对较低，因而，应用比较广泛。常见的桌面虚拟现实技术有基于静态图像的虚拟现实（QuickTimeVR）、虚拟现实造型语言（VRML）、桌面三维虚拟现实和MUD等。

（2）完全沉浸的虚拟现实。高级虚拟现实系统提供完全沉浸的体验，使用户有一种置身于虚拟境界之中的感觉。它利用头盔式显示器或其他传感设备，把参与者的视觉、听觉和其他感觉封闭起来，并提供一个新的、虚拟的感觉空间，利用位置跟踪器、数据手套、其他手控输入设备和声音等使得参与者产生一种身临其境、全心投入和沉浸其中的感觉。常见的沉浸式系统有：基于头盔式显示器的系统、投影式虚拟现实系统、远程存在系统等。

（3）增强现实性的虚拟现实。增强现实性的虚拟现实不仅是利用虚拟现实技术来模拟现实世界、仿真现实世界，而且要利用它来增强参与者对真实环境的感受，也就是增强现实中无法感知或不方便的感受。

（4）分布式虚拟现实。分布式虚拟现实系统是基于网络的虚拟环境，在这个环境中，位于不同物理环境位置的多个用户或多个虚拟环境通过网络相联结，或者多个用户同时参加一个虚拟现实环境，通过计算机与其他用户进行交互，并共享信息。在分布式虚拟现实系统中，多个用户可通过网络对同一虚拟世界进行观察和操作，以达到协同工作的目的。

6.2　声音

6.2.1　基本概念

1．声音信号

声音是通过空气传播的一种连续的波，称为声波。声波在时间和幅度上都是连续的模拟信号，通常称为模拟声音（音频）信号。

1）声音的感觉

人们对声音的感觉主要有音量、音调和音色三个指标。

（1）音量（也称响度）。声音的强弱程度，取决于声音波形的幅度，即取决于振幅的大小和强弱。

（2）音调。人对声音频率的感觉表现为音调的高低，取决于声波的基频。基频越低，给人的感觉越低沉，频率高则声音尖锐。

（3）音色。由混入基音（基波）的泛音（谐波）所决定，每种声音又都有其固定的频率和不同音强的泛音，从而使得它们具有特殊的音色效果。人们能够分辨具有相同音高的钢琴和小号声音，就是因为它们具有不同的音色。一个声波上的谐波越丰富，音色越好。

声音信号由许多频率不同的信号组成，人耳能听到的音频信号（audio）的频率范围是20～20kHz。可听声包括如下种类。

●　话音（也称语音）。人的说话声，频率范围通常为300～3400Hz。

●　音乐。由乐器演奏形成（规范的符号化声音），其带宽可达到20～20kHz。

●　其他声音。如风声、雨声、鸟叫声和汽车鸣笛声等，它们起着效果声或噪声的作用，其带宽范围也是20～20kHz。

声音信号的两个基本参数是幅度和频率。幅度是指声波的振幅，通常用动态范围表示，一般用分贝（dB）为单位来计量。频率是指声波每秒钟变化的次数，用Hz表示。人们把频率小于20Hz的声波信号称为亚音信号（也称次音信号）；频率范围为20Hz～20kHz的声波信号称为音频信号；高于20kHz的信号称为超音频信号（也称超声波）。

2）声音信号的数字化

声音信号是一种模拟信号，计算机要对它进行处理，必须将它转换成为数字声音信号，即用二进制数字的编码形式来表示声音。最基本的声音信号数字化方法是取样-量化法，它分成如下三个步骤。

（1）采样。采样是把时间连续的模拟信号转换成时间离散、幅度连续的信号。在某些特定的时刻获取声音信号幅值叫做采样，由这些特定时刻采样得到的信号称为离散时间信号。一般都是每隔相等的一小段时间采样一次，为了不产生失真，采样频率不应低于声音信号最高频率的两倍。因此，语音信号的采样频率一般为8kHz，音乐信号的采样频率则应在40kHz以上。采样频率越高，可恢复的声音信号分量越丰富，其声音的保真度越好。

（2）量化。量化处理是把在幅度上连续取值（模拟量）的每一个样本转换为离散值（数字量）表示，因此量化过程有时也称为A/D转换（模数转换）。量化后的样本是用二进制数来表示的，二进制数位数的多少反映了度量声音波形幅度的精度，称为量化精度，也称为量化分辨率。例如，每个声音样本若用16位（2字节）表示，则声音样本的取值范围是0～65536，精度是1/65536；若只用8位（1字节）表示，则样本的取值范围是0～255，精度是1/256。量化精度越高，声音的质量越好，需要的存储空间也越多；量化精度越低，声音的质量越差，而需要的存储空间越少。

（3）编码。经过采样和量化处理后的声音信号已经是数字形式了，但为了便于计算机的存储、处理和传输，还必须按照一定的要求进行数据压缩和编码，即选择某一种或者几种方法对它进行数据压缩，以减少数据量，再按照某种规定的格式将数据组织成为文件。

经过数字化处理之后的数字声音的主要参数如表6-1所示。

表6-1　数字化处理之后的数字声音的主要参数

2．波形声音

波形声音信息是一个用来表示声音振幅的数据序列，它是通过对模拟声音按一定间隔采样获得的幅度值，再经过量化和编码后得到的便于计算机存储和处理的数据格式。声音信号数字化后，其数据传输率（每秒位数）与信号在计算机中的实时传输有直接关系，而其总数据量又与计算机的存储空间有直接关系。未经压缩的数字音频数据传输率可按下式计算：

其中，数据传输率以每秒位（b/s）为单位；采样频率以Hz为单位；量化以位（b）为单位。

波形声音经过数字化后所需占用的存储空间可用如下公式计算：

【例6.1】若语音信号的带宽通常为300～3400Hz，量化精度为8位，单声道输出，计算每秒钟及每小时的数据量。

解：据题意，数字化时的取样频率为8kHz，根据上述公式每秒钟的数据量为：

1小时数字语音的数据量大约是28MB。

【例6.2】若CD（Compact Disc，高密盘）盘片上所存储的立体声高保真数字音乐的带宽为20～20000Hz，采样频率为44.1kHz，量化精度为16位，双声道，计算1小时的数据量。

解：每秒钟的数据量为1411.2kb/s（176.4KB/s），1小时的数据量大约是635MB。

可见，数字波形声音数据量非常大，因此在编码的时候常常要采用压缩的方式来压缩数字数据以减少存储空间和提高传输效率（降低传输带宽）。根据统计分析结果，语音信号中包含有大量的冗余信息，再加上还可以利用人的听觉感知特性，因此，产生了许多能满足实际应用需求的压缩算法。一个好的数据压缩算法通常应能满足下列需求。

（1）压缩倍数高，压缩后的数据率低。

（2）解码后的信号失真小，质量高。

（3）算法简单，执行速度快，延迟时间短。

（4）编码器／解码器的成本低。

3．数字语音压缩方法

数字语音的数据压缩方法很多，从原理上可分为如下三类。

（1）波形编码。这是一种直接对取样、量化后的波形进行压缩处理的方法。例如，脉冲编码调制（PCM）、自适应差分脉冲编码（ADPCM）和子带编码（SBC）等。波形编码的特点是通用性强，不仅适应于数字语音的压缩，而且对所有使用波形表示的数字声音都有效；可获得高质量的语音，但很难达到很高的压缩比。

（2）参数编码（也称为模型编码）。这是一种基于声音生成模型的压缩方法（Production Model-Based Compression），从语音波形信号中提取生成的话音参数，使用这些参数通过话音生成模型重构出话音。例如，线性预测编码（LPC）和声码器（vocoder）等。它的优点是能达到很高的压缩比。缺点是信号源必须已知，受声音生成模型的限制，质量还不理想。

（3）混合编码。波形编码虽然可提供高质量的语音，但数据率比较高，很难低于16kb/s；参数编码的数据率虽然可降低到3kb/s甚至更低，但它的音质根本不能与波形编码相比。混合编码是上述两种方法的结合，它既能达到高的压缩比，又能保证一定的质量，但算法相对复杂一些。例如，码激励线性预测（CELP）和混合激励线性预测（MELP）等。

基于波形的压缩编码方法虽然数据率比较高，但能保证高质量地重建语音，且算法简单、易实现，能较好地保持原有声音的特点，便于编辑处理，在多媒体计算机和多媒体文档中有广泛的应用。

数字语音压缩编码有多种国际标准，如G.711、G.721、G.726、G.727、G.722、G.728、G.729A、G.723.1和IS96（CDMA）等。

数字语音由于频带比较窄，又可以通过语音生成模型进行比较好的模拟，因此经过压缩编码后码率比较低，存储与传输问题不大。而对于有高保真度要求的全频带声音，由于其带宽达到20kHz以上，又有双声道甚至多声道的要求，数据量相当可观，对压缩编码的要求更高。全频带声音的压缩编码方法与数字语音的处理方法不同，它不但依据波形本身的相关性，而且还利用人的听觉系统特性来达到压缩声音的目的，这种压缩编码称为感知声音编码（Perceptual Audio Coding）。在国际标准MPEG中，先后为视频图像伴音的数字宽带声音制定了MPEG-1 Audio、MPEG-2 Audio、MPEG-2AAC和MPEG-4 Audio等多种数据压缩编码的标准。MPEG处理的是10～20000Hz频率范围的声音信号，数据压缩的主要依据是人耳的听觉特性，特别是人耳存在着随声音频率变化的听觉域，以及人耳的听觉掩蔽特性。

4．声音合成

个人计算机和多媒体系统中的声音，除了数字波形声音之外，还有一类是使用符号表示的，由计算机合成的声音，包括语音合成和音乐合成。

1）语音合成

语音合成目前主要指从文本到语音的合成，也称为文语转换。采用文语转换的方法输出语音，应预先建立语音参数数据库、发音规则库等。需要输出语音时，系统按需求先合成语音单元，再按语音学规则或语言学规则连接成自然的语流。文语转换的参数数据库不随发音时间增长而加大，但规则库却随语音质量的要求而增大。语音合成有多方面的应用，例如，海量查询与声讯服务、航班动态查询、电话报税、有声E-mail等，这些业务都需要以准确、清晰的语音通过电话进行操作提示并提供查询结果。一般来说，对合成的语音要求能够做到可听懂、自然、延迟时间短、速度可控制等。

文语转换原理上一般分成两步，第一步先将文字序列转换成音韵序列，第二步再由语音合成器生成语音波形（如图6-1所示）。其中第一步涉及语言学处理，例如分词、字音转换等，以及一整套有效的韵律控制规则；第二步需要使用语音合成技术，能按要求实时合成出高质量的语音流。

图6-1　文语转换过程示意

从合成采用的技术来说，语音合成可分为发音参数合成、声道模型参数合成和波形编辑合成。

（1）发音参数合成。这种方法对人的发音过程进行直接模拟，它定义了唇、舌、声带的相关参数，如开口度、舌高度、舌位置和声带张力等。由这些发音参数估计声道截面积函数，进而计算声波。但由于人发音生理过程复杂，理论计算与物理模拟之间的差异，合成语音的质量目前还不理想。

（2）声道模型参数合成。这种方法基于声道截面积函数或声道谐振特性合成语音，如共振峰合成器、LPC合成器。这类合成器的比特率低，音质适中。为改善音质，发展了混合编码技术，主要手段是改善激励，如码本激励、多脉冲激励和长时预测规则码激励等。这样比特率有所增大，同时音质得到提高。

（3）波形编辑合成。波形编辑合成技术是直接把语音波形数据库中的波形相互拼接在一起，输出连续语流。这种语音合成技术用原始语音波形代替参数，而且这些语音波形取自自然语言的词或句子，它隐含了声调、重音、发音速度的影响，合成的语言清晰自然。其质量普遍高于参数合成。

2）音乐合成

音乐是用乐谱进行描述，由乐器演奏而成的。乐谱的基本组成单元是音符（notes），最基本的音符有7个，所有不同音调的音符少于128个。

音符代表的是音乐，音乐与噪声的区别主要在于它们是否有周期性。音乐的波形随着时间作周期性变化，噪声则不然。音乐的要素有音调、音色、响度和持续时间。音调指声波的基频，基频低，声音低沉；基频高，声音高昂。不同乐器有不同的音色，音色是由声音的频谱决定的。响度即声音的强度。一首乐曲中每一个乐音的持续时间是变化的，从而形成旋律。音乐可以使用电子学原理合成出来（生成相应的波形），各种乐器的音色也可以进行模拟。

5．MIDI

MIDI（Musical Instrument Digital Interface，乐器数字接口），泛指数字音乐的国际标准。MIDI标准规定了电子乐器与计算机之间连接的电缆硬件以及电子乐器之间、乐器与计算机之间传送数据的通信协议的规范；规定了音乐的数字表示，包含音符、定时和乐器指派等规范。由于MIDI标准定义了计算机音乐程序、合成器及其他电子设备交换信息和电子信号的方式，所以可以解决不同电子乐器之间不兼容的问题。

MIDI设备是指符合MIDI规范的设备。通过MIDI接口，不同MIDI设备之间可进行信息交换。带有MIDI接口以及专用的MIDI电缆计算机、合成器和其他MIDI设备连接在一起，即可构成计算机音乐系统。数据由MIDI设备的键盘产生，可通过声音合成器还原为声音。通过计算机可以控制乐器的输出，并能接收、存储和处理经过编码的音乐数据。

MIDI文件是指计算机中用于存储和交换MIDI消息的一种数据文件，它由一系列的MIDI消息组成。它不仅包含着与音乐演奏有关的消息，如音符、速度和表情等，而且还包含了大量与音响有关的控制信息，如声像、效果和均衡等。MIDI文件中包含了多达16个通道的演奏定义，包括每个通道的演奏音符信息，如键、音长、音量和力度（击键时，键达到最低位置的速度）等。标准MIDI文件格式采用的文件扩展名为.mid，它是音序软件的文件交换标准，也是商业音乐作品发行的标准。

MIDI音乐与高保真的波形声音相比，虽然在音质方面还有一些差距，也无法合成出所有各种不同的声音（如语音），且生成的音乐质量与使用的音源硬件有关，但它的数据量极少（比CD-DA少3个数量级，比MP3少2个数量级），又易于编辑修改，还可以与波形声音同时播放。

6.2.2　声音文件格式

数字声音在计算机中存储和处理时，其数据必须以文件的形式进行组织，所选用的文件格式必须得到操作系统和应用软件的支持。如同文本文件一样，在因特网上和各种不同计算机以及应用软件中使用的声音文件格式也互不相同。下面通过声音文件的特征后缀名来逐一介绍。

（1）Wave文件（.WAV）。Microsoft公司的音频文件格式，它来源于对声音模拟波形的采样。用不同的采样频率对声音的模拟波形进行采样可以得到一系列离散的采样点，以不同的量化位数（8位或16位）把这些采样点的值转换成二进制数，然后存入磁盘，这就产生了声音的WAV文件，即波形文件。利用该格式记录的声音文件能够和原声基本一致，质量非常高，但文件数据量大。

（2）Module文件（.MOD）。该格式的文件里存放乐谱和乐曲使用的各种音色样本，具有回放效果明确，音色种类无限等优点。

（3）MPEG文件（.MP3）。现在最流行的声音文件格式，因其压缩率大，在网络可视电话通信方面应用广泛，但和CD唱片相比，音质不能令人非常满意。

（4）RealAudio文件（.RA）。这种格式具有强大的压缩量和极小的失真，它也是为了解决网络传输带宽资源而设计的，因此主要目标是压缩比和容错性，其次才是音质。

（5）MIDI文件（.MID/.RMI）。它是目前较成熟的音乐格式，实际上已经成为一种产业标准，其科学性、兼容性和复杂程度等各方面当然远远超过本文前面介绍的所有标准（除交响乐CD、Unplug CD外，其他CD往往都是利用MIDI制作出来的），General MIDI就是最常见的通行标准。作为音乐工业的数据通信标准，MIDI能指挥各音乐设备的运转，而且具有统一的标准格式，能够模仿原始乐器的各种演奏技巧甚至无法演奏的效果，而且文件的长度非常小。.RMI可以包括图片标记和文本。

（6）Voice文件（.VOC）。Creative公司波形音频文件格式，也是声霸卡（sound blaster）使用的音频文件格式。每个VOC文件由文件头块（header block）和音频数据块（data block）组成。文件头包含一个标识版本号和一个指向数据块起始的指针。数据块分成各种类型的子块。

（7）Sound文件（.SND）。Sound文件是NeXT Computer公司推出的数字声音文件格式，支持压缩。

（8）Audio文件（.AU）。Sun Microsystems公司推出的一种经过压缩的数字声音文件格式，是因特网上常用的声音文件格式。

（9）AIFF文件（.AIF）。Apple计算机的音频文件格式。Windows的Convert工具可以把AIF格式的文件换成Microsoft的WAV格式的文件。

（10）CMF文件（.CMF）。Creative公司的专用音乐格式，与MIDI差不多，音色、效果上有些特色，专用于FM声卡，但其兼容性也很差。

6.3　图形和图像

6.3.1　基础知识

1．基本概念

彩色是创建图像的基础，在计算机上使用彩色没有什么特殊之处，只不过是它有一套特定的记录和处理彩色的技术。因此，要理解图像处理软件中所出现的各种有关彩色的术语，首先要具备基本的彩色理论知识。

彩色是通过光被人们感知的，物体由于内部物质的不同，受光线照射后，产生光的分解现象，一部分光线被吸收，其余的被反射或投射出来，成为人们所见的物体的彩色。所以，彩色和光有密切关系，同时还与被光照射的物体有关，并与观察者有关。

1）色彩三要素

彩色光作用于人眼，使之产生彩色视觉。为了能确切地表示某一彩色光的度量，可以用亮度、色调和色饱和度三个物理量来描述，并称之为色彩三要素。

（1）亮度。亮度是描述光作用于人眼时引起的明暗程度感觉，是指彩色明暗深浅程度。一般来说，对于发光物体，彩色光辐射的功率越大，亮度越高；反之，亮度越低。对于不发光的物体，其亮度取决于吸收或者反射光功率的大小。

（2）色调。色调是指颜色的类别，如红色、绿色、蓝色等不同颜色就是指色调。由光谱分析可知，不同波长的光呈现不同的颜色，人眼看到一种或多种波长的光时所产生的彩色感觉，反映出颜色的类别。某一物体的色调取决于它本身辐射的光谱成分或在光的照射下所反射的光谱成分对人眼刺激的视觉反应。

（3）色饱和度。色饱和度是指某一颜色的深浅程度（或浓度）。对于同一种色调的颜色，其饱和度越高，则颜色越深，如深红、深绿和深蓝等；其饱和度越低，则颜色越淡，如淡红、淡绿和淡黄等。高饱和度的深色光可掺入白色光被冲淡，降为低饱和度的淡色光。因此，色饱和度可认为是某色调的纯色掺入白色光的比例。

2）三基色原理

从理论上讲，任何一种颜色都可以用三种基本颜色按不同比例混合得到。自然界常见的各种颜色光，都可由红（Red）、绿（Green）、蓝（Blue）三种颜色光按不同比例相配而成。同样，绝大多数颜色光也可以分解成红、绿、蓝三种颜色光，这就是色度学中最基本的三基色原理。当然，三基色的选择不是唯一的，可以选择其他三种颜色为三基色。但是，三种颜色必须是相互独立的，即任何一种颜色都不能由其他两种颜色合成。由于人眼对红、绿、蓝这三种颜色光最敏感，因此由这三种颜色相配所得的彩色范围也最广，所以一般都选这三种颜色作为基色。把三种基色光按不同比例相加称之为相加混色，由红、绿、蓝三基色进行相加混色的情况如下。

红色＋绿色＝黄色

红色＋蓝色＝品红

绿色＋蓝色＝青色

红色＋绿色＋蓝色＝白色

红色＋青色＝绿色＋品红＝蓝色＋黄色＝白色

凡是两种色光混合而成白光，则这两种色光互为补色。

3）彩色空间

彩色空间是指彩色图像所使用的颜色描述方法，也称为彩色模型。在PC和多媒体系统中，表示图形和图像的颜色常常涉及不同的彩色空间，如RGB彩色空间、CMY彩色空间和YUV彩色空间等。不同的彩色空间对应不同的应用场合，各有其特点。因此，数字图像的生成、存储、处理及显示时对应不同的彩色空间。从理论上讲，任何一种颜色都可以在上述彩色空间中精确地进行描述。

（1）RGB彩色空间。计算机中的彩色图像一般都用R、G、B分量表示，彩色显示器通过发射出三种不同强度的电子束，使屏幕内侧覆盖的红、绿、蓝荧光材料发光而产生色彩。这种彩色的表示方法称为RGB彩色空间表示法。通过三个分量的不同比例，在显示屏幕可合成任意所需要的颜色。

（2）CMY彩色空间。RGB彩色空间中，不同颜色的光是通过相加混合实现的，而彩色打印的纸张是不能发射光线的，因而彩色打印机就不能采用RGB颜色来打印，它只能使用能够吸收特定的光波而反射其他光波的油墨或颜料来实现。用油墨或颜料进行混合得到的彩色称为相减混色。之所以称为相减混色，是因为减少（吸收）了人眼识别颜色所需要的反射光。根据三基色原理，油墨或颜料的三基色是青（cyan）、品红（magenta）和黄（yellow）。可以用这三种颜色的油墨或颜料按不同比例混合成任何一种由油墨或颜料表现的颜色，这种彩色表示方法称为CMY彩色空间。

（3）YUV彩色空间。在现代彩色电视系统中，通常采用三管彩色摄像机或彩色CCD摄像机，它把摄得的彩色图像信号，经过分色、放大和校正得到R、G、B三基色，再经过矩阵变换得到亮度信号Y、色差信号U（R-Y）和V（B-Y），最后发送端将这三个信号分别进行编码，用同一信道发送出去。这就是常用的YUV彩色空间。电视图像一般都是采用Y、U、V分量表示，即一个亮度分量（Y）和两个色差（U和V）分量表示。由于亮度和色度是分离的，解决了彩色和黑白显示系统的兼容问题，如果只有Y分量而没有U、V分量，那么所表示的图像是黑白灰度图像。

2．计算机中的图形数据表示

在计算机中的图形数据有两种常用的表示形式，一种称为几何图形或矢量图形，简称图形；另一种称为点阵图像或位图图像。

（1）矢量图形。矢量图形是用一系列计算机指令来描述和记录的一幅图的内容，即通过指令描述构成一幅图的所有直线、曲线、圆、圆弧、矩形等图元的位置、维数和形状，也可以用更为复杂的形式表示图像中的曲面、光照、材质等效果。矢量图法实质上是用数学的方式（算法和特征）来描述一幅图形图像，在处理图形图像时根据图元对应的数学表达式进行编辑和处理。在屏幕上显示一幅图形图像时，首先要解释这些指令，然后将描述图形图像的指令转换成屏幕上显示的形状和颜色。编辑矢量图的软件通常称为绘图软件，如适于绘制机械图、电路图的AutoCAD软件等。

（2）位图图像。位图图像是指用像素点来描述的图。图像一般是用摄像机或扫描仪等输入设备捕捉实际场景画面，离散化为空间、亮度、颜色（灰度）的序列值，即把一幅彩色图或灰度图分成许许多多的像素（点），每个像素用若干二进制位来指定该像素的颜色、亮度和属性。位图图像在计算机内存中由一组二进制位组成，这些位定义图像中每个像素点的颜色和亮度。屏幕上一个点也称为一个像素，显示一幅图像时，屏幕上的一个像素也就对应于图像中的某一个点。根据组成图像的像素密度和表示颜色、亮度级别的数目，又可将图像分为二值图（黑白图）和彩色图两大类，彩色图还可以分为真彩色图、伪彩色图等。图像适合于表现比较细腻，层次较多，色彩较丰富，包含大量细节的图像，并可直接、快速地在屏幕上显示出来。

3．图像的获取

将现实世界的景物或物理介质上的图文输入计算机的过程称为图像的获取（capturing）。在多媒体应用中的基本图像可通过不同的方式获得，一般来说，可以直接利用数字图像库的图像；可以利用绘图软件创建图像；可以利用数字转换设备采集图像。

1）利用数字图像库

在CD-ROM光盘和因特网上的图像库中，图像的内容较丰富，图像尺寸和图像深度可选的范围也较广。而且图像的质量完全可以满足一般用户的要求，但图像的内容也许不具备用户的创意设计。用户可根据需要选择已有的图像，或再做进一步的编辑和处理。

2）利用绘图软件创建图像

目前Windows环境的大部分图像编辑软件都具有一定的绘图功能。这些软件大多具有较强的功能和很好的图形用户接口，还可以利用鼠标、画笔及画板来绘制各种图形，并进行彩色、纹理、图案等的填充和加工处理。对于一些小型的图形、图标、按钮等，直接制作很方便，但这不足以描述自然景物和人像。也有一些较专业的绘画软件，通过数字化画板和画笔在屏幕上绘画。这种软件要求绘画者具有一定的美术知识及创意基础。

3）利用数字转换设备采集图像

数字转换设备可以把采集到的图像转换成计算机能够记录和处理的数字图像数据。例如，对印刷品、照片或照相底片等进行扫描，用数字相机或数字摄像机对选定的景物进行拍摄等。从现实世界中获取数字图像所使用的设备通称为图像获取设备。一幅彩色图像可以看做二维连续函数f（x，y），其彩色f是坐标（x，y）的函数，从二维连续函数到离散的矩阵表示，同样包含采样、量化和编码的数字化过程。数字转换设备获取图像的过程实质上是信号扫描和数字化的过程，它的处理步骤大体分为如下三步。

（1）采样。在x，y坐标上对图像进行采样（也称为扫描），类似于声音信号在时间轴上的采样要确定采样频率一样，在图像信号坐标轴上的采样也要确定一个采样间隔，这个间隔即为图像分辨率。有了采样间隔，就可以逐行对原始图像进行扫描。首先设y坐标不变，对x轴按采样间隔得到一行离散的像素点xn
及相应的像素值。使y坐标也按采样间隔由小到大变化，就可以得到一个离散的像素矩阵［xn
，yn
］，每个像素点有一个对应的色彩值。简单地说，将一幅画面划分为M×N个网格，每个网格称为一个取样点，用其亮度值来表示。这样，一幅连续的图像就转换为以取样点值组成的一个阵列（矩阵）。

（2）量化。将扫描得到的离散的像素点对应的连续色彩值进行A/D转换（量化），量化的等级参数即为图像深度。这样，像素矩阵中的每个点（xn
，yn
）都有对应的离散像素值fn
。

（3）编码。把离散的像素矩阵按一定方式编成二进制码组。最后，把得到的图像数据按某种图像格式记录在图像文件中。

4．图像的属性

描述一幅图像需要使用图像的属性。图像的属性包含分辨率、像素深度、真／伪彩色、图像的表示法和种类等。

1）分辨率

经常遇到的分辨率有两种，即显示分辨率和图像分辨率。图像分辨率与显示分辨率是两个不同的概念，图像分辨率确定的是组成一幅图像的像素数目，而显示分辨率确定的是显示图像的区域大小。它们之间的关系为：图像分辨率大于显示分辨率时，在屏幕上只能显示部分图像；图像分辨率小于显示分辨率时，图像只占屏幕的一部分。

（1）显示分辨率。显示分辨率是指显示屏上能够显示出的像素数目。例如，显示分辨率为1024×768表示显示屏分成768行（垂直分辨率），每行（水平分辨率）显示1024个像素，整个显示屏就含有796432个显像点。屏幕能够显示的像素越多，说明显示设备的分辨率越高，显示的图像质量越高。

（2）图像分辨率。图像分辨率是指组成一幅图像的像素密度，也是用水平和垂直的像素表示，即用每英寸多少点（dpi）表示数字化图像的大小。例如，用200dpi来扫描一幅2×2.5英寸的彩色照片，那么得到一幅400×500个像素点的图像。它实质上是图像数字化的采样间隔，由它确立组成一幅图像的像素数目。对同样大小的一幅图，如果组成该图的图像像素数目越多，则说明图像的分辨率越高，图像看起来就越逼真；相反，图像显得越粗糙。因此，不同的分辨率会造成不同的图像清晰度。

2）图像深度

图像深度是指存储每个像素所用的位数，它也是用来度量图像的色彩分辨率的。像素深度确定彩色图像的每个像素可能有的颜色数，或者确定灰度图像的每个像素可能有的灰度级数。它决定了彩色图像中可出现的最多颜色数，或灰度图像中的最大灰度等级。如一幅图像的图像深度为b位，则该图像的最多颜色数或灰度级为2b
种。显然，表示一个像素颜色的位数越多，它能表达的颜色数或灰度级就越多。例如，只有1个分量的单色图像，若每个像素有8位，则最大灰度数目为2^8
＝256。一幅彩色图像的每个像素用R、G、B三个分量表示，若三个分量的像素位数分别为4，4，2，则最大颜色数目为24＋4＋2
＝2^10
＝1024，就是说像素的深度为10位，每个像素可以是2^10
种颜色中的一种。表示一个像素的位数越多，它能表达的颜色数目就越多，它的深度就越深。

3）真彩色和伪彩色

真彩色（true color）是指组成一幅彩色图像的每个像素值中，有R、G、B三个基色分量，每个基色分量直接决定显示设备的基色强度，这样产生的彩色称为真彩色。例如，用RGB8∶8∶8方式表示一幅彩色图像，也就是R、G、B分量都用8位来表示，可生成的颜色数就是2^24
种，每个像素的颜色就是由其中的数值直接决定的。这样得到的色彩可以反映原图像的真实色彩，一般认为是真彩色。通常，在一些场合把RGB8∶8∶8方式表示的彩色图像称为真彩色图像或全彩色图像。

为了减少彩色图形的存储空间，在生成图像时，对图像中不同色彩进行采样，产生包含各种颜色的颜色表，即彩色查找表。图像中每个像素的颜色不是由三个基色分量的数值直接表达，而是把像素值作为地址索引在彩色查找表中查找这个像素实际的R、G、B分量，将图像的这种颜色表达方式称为伪彩色。需要说明的是，对于这种伪彩色图像的数据除了保存代表像素颜色的索引数据外，还要保存一个色彩查找表（调色板）。彩色查找表可以是一个预先定义的表，也可以是对图像进行优化后产生的色彩表。常用的256色的彩色图像使用了8位的索引，即每个像素占用一个字节。

5．图形图像转换

图形和图像之间在一定的条件下可以转换，如采用光栅化（点阵化）技术可以将图形转换成图像；采用图形跟踪技术可以将图像转换成图形。一般可以通过硬件（输入输出设备）或软件实现图形和图像之间的转换。

（1）图形和图像的硬件转换。一张工程图纸，一般认为它是图形，其实在它被输入计算机以前还不能称为图形或图像，当将它用扫描仪输入到Photoshop，它就变成图像信息（点位图）；当用数字化仪将它输入到AutoCAD后，它就变成图形信息（矢量图）。也就是说，同一个对象既可以被作为图形处理，也可以作为图像处理。那么到底哪种过程更有效？要看被处理的对象性质和要达到的处理结果。当然，可以先将它用扫描仪扫进计算机，变成图像信息，再用一定的软件（如Corel-Trace，Photoshop的轮廓跟踪）人工或自动地勾勒出它的轮廓，这个过程称为向量化，也就是图像转换为图形的过程。这个过程必然会丢失许多细节，所以通常适用于工程绘图领域。

（2）图形和图像的软件转换。图形和图像都是以文件的形式存放在计算机存储器中，可以通过应用软件实现文件格式之间的转换，达到图形和图像之间的转换。随着图形和图像处理技术的发展，出现了很多较好的格式转换软件，如CorelDraw软件，它几乎提供所有文件格式之间的转换。转换并不表示可以任意互换，实际上许多转换是不可逆的，转换的次数越多，丢失的信息就越多，特别是图形和图像之间的转化。例如，当将一个BMP格式文件转化为GIF、TIFF等格式时问题还不大，但如果将它转化为DIF等格式时就丢失了许多细节，甚至像一个矩形中间的填充色块，都用几条线表示。

6．图像的压缩编码

扫描生成一幅图像时，实际上就是按一定的图像分辨率和一定的图像深度对模拟图片或照片进行采样，从而生成一幅数字化的图像。图像的图像分辨率越高，图像深度越深，则数字化后的图像效果越逼真，图像数据量越大。如果按照像素点及其深度映射的图像数据大小采样，可用下面的公式估算数据量：

图像数据量＝图像的总像素×图像深度／8（B）

其中，图像的总像素为图像的水平方向像素乘以垂直方向像素数。

例如，一幅640×480的256色图像，其文件大小约为：

可见，数字图像的数据量也很大，需要很大的存储空间存储数据图像。更重要的是，在现代通信中，特别是因特网上开展的各种应用中，图像传输速度是一项很重要的指标。以使用拨号接入因特网的家庭用户为例，假设数据传输速度为56kb/s，则理想情况下，传输一幅分辨率为640×480的6.5万色的未经压缩的图像大约需要1～2分钟。因此，采用压缩编码技术，减少图像的数据量是提高网络传输速度的重要手段。

数据压缩可分成两类：有损压缩和无损压缩。

（1）有损压缩。有损压缩方法利用人眼视觉对图像中的某些频率成分不敏感的特性，采用一些高效的有限失真数据压缩算法，允许压缩过程中损失一定的信息。采用有损压缩后的数据进行图像重建时，重建后的图像与原始图像虽有一定的误差，但并不影响人们对图像含义的正确理解。而且换来了较大的压缩比，大幅度减少了图像信息中的冗余信息。

（2）无损压缩。无损压缩利用数据的统计冗余进行压缩，可以保证在数据压缩和还原过程中，图像信息没有损耗或失真，图像还原（解压缩）时可完全恢复，即重建后的图像与原始图像完全相同。例如，在多媒体应用中常用行程长度编码、增量调制编码和霍夫曼编码等。

①行程长度编码（Run-Length Encoding, RLE）。某些图像往往有许多颜色相同的图块。在这些图块中，许多连续的扫描行都具有同一种颜色，或者同一扫描行上有许多连续的像素都具有相同的颜色值。在这些情况下就可以不需要存储每一个像素的颜色值，而仅仅存储一个像素值以及具有相同颜色的像素数目。这种编码称为行程编码。具有同一颜色的连续像素的数目称为行程长度。其压缩率的大小取决于图像本身。如果图像中具有相同颜色的横向色块越大，这样的图像块数目越多，压缩比就越大；反之就越小。

②增量调制编码（delta modulation encoding, DM）。自然图像往往有在比较大的范围内，图像的颜色虽不完全一致，但变化不大的特点。因此，在这些区域中，相邻像素的像素值相差很小，具有很大的相关性。在一幅图像中，除了轮廓特别明显的地方以外，大部分区域都具有这种特点。增量调制编码就利用图像相邻像素值的相关性来压缩每个像素值的位数，以达到减少存储容量的目的。增量调制编码压缩图像时，不存储扫描行上每个像素的实际值，仅存储每一行上第一个像素的实际值。其后，依次存储每一个像素的像素值与前一个像素值之差，即增量值。

③霍夫曼（Huffman）编码。大多数图像常常包含单色的大面积图块，而且某些颜色比其他颜色出现的更频繁，因此可以采用霍夫曼编码方式。霍夫曼编码的基本方法是先对图像数据扫描一遍，计算出各种像素出现的概率，按概率的大小指定不同长度的唯一码字，由此得到一张该图像的霍夫曼码表。编码后的图像数据记录的是每个像素的码字，而码字与实际像素值的对应关系记录在码表中。码表是附在图像文件中的。实际应用中，霍夫曼编码常与其他编码方法结合使用，以获得更大的压缩比。

7．多媒体数据压缩编码的国际标准

计算机中使用的图像压缩编码方法有多种国际标准和工业标准。目前使用相当广泛的编码及压缩标准有JPEG、MPEG和H.261。

（1）JPEG（Joint Photographic Experts Group）。JPEG是一个由ISO和IEC两个组织机构联合组成的专家组，负责制定静态和数字图像数据压缩编码标准，这个专家组地区性的算法称为JPEG算法，并且成为国际上通用的标准，因此又称为JPEG标准。JPEG是一个适用范围很广的静态图像数据压缩标准，既可用于灰度图像又可用于彩色图像。JPEG专家组开发了两种基本的压缩算法，一种是以离散余弦变换（Discrete Cosine Transrorm, DCT）为基础的有损压缩算法，另一种是采用以预测技术为基础的无损压缩算法。使用有损压缩算法时，在压缩比为25∶1的情况下，压缩后还原得到的图像与原始图像相比较，人们难于察觉它们之间的区别，因此得到了广泛的应用。例如，在V-CD和DVD-Video电视图像压缩技术中，就使用JPEG的有损压缩算法来取消同方向上的冗余数据。为了在保证图像质量的前提下进一步提高压缩比，JPEG专家组制定了ISO/IEC 15444 JPEG2000（简称JP2000）标准，这个标准中采用了小波变换（Wavelet）算法。

（2）MPEG（Moving Pictures Experts Group）。MPEG动态图像压缩标准是一个由ISO和IEC两个组织机构联合组成的活动图像专家组，于1990年形成了一个标准草案，将MPEG标准分成两个阶段：第一阶段（MPEG-1）是针对传输率为1～1.5Mb/s的普通电视质量的视频信号的压缩；第二阶段（MPEG-2）的目标则是对每秒30帧的720×572分辨率的视频信号进行压缩。在扩展模式下，MPEG-2可以对分辨率达1440×1152的高清晰度电视（HDTV）的信号进行压缩。MPEG标准分成MPEG视频、MPEG音频和视频音频同步三个部分。1999年发布了MPEG-4多媒体应用标准，目前推出了MPEG-7多媒体内容描述接口标准等。每个新标准的产生都极大地推动了数字视频的发展和更广泛的应用。

（3）H.261。H.261视频通信编码标准是由国际电话电报咨询委员会于1998年提出的电话／会议电视的建议标准，该标准又称为P×64K标准，其中P是取值为1～30的可变参数；P＝1或2时支持1/4通用中间格式（Quarter Common Intermediate Format, QCIF）的帧率较低的视频电话传输；P≤6时支持通向中间格式（Common Intermediate Format, CIF）的帧率较高的电视会议数据传输。P×64K视频压缩算法也是一种混合编码方案，即基于DCT的变换编码和带有运动预测差分脉冲编码调制（DPCM）的预测编码方法的混合。在低传输率时（P＝1或P＝2，即64kb/s或128kb/s），除QCIF外还可以使用亚帧技术，即每间隔一帧（或数帧）处理一帧，压缩比例可达50∶1左右。CCITT推出的H.263标准用于低位速率通信的电视图像编码。

6.3.2　图形、图像文件格式

数字图像在计算机中存储时，其文件格式繁多，下面简单介绍几种常用的文件格式。

（1）BMP文件（.BMP）。Windows操作系统采用的图像文件格式，在Windows环境下运行的所有图像处理软件几乎都支持BMP图像文件格式。它是一种与设备无关的位图格式，目的是为了让Windows能够在任何类型的显示设备上输出所存储的图像。BMP采用位映射存储格式，除了图像深度可选以外，一般不采用其他任何压缩，所以占用的存储空间较大。BMP文件的图像深度可选1位、4位、8位及24位，即有黑白、16色、256色和真彩色之分。

（2）GIF文件（.GIF）。CompuServe公司开发的图像文件格式，它以数据块为单位来存储图像的相关信息。GIF文件格式采用了LZW（Lempel-Ziv Welch）无损压缩算法按扫描行压缩图像数据。它可以在一个文件中存放多幅彩色图像，每一幅图像都由一个图像描述符、可选的局部彩色表和图像数据组成。如果把存储于一个文件中的多幅图像逐幅读出来显示到屏幕上，可以像播放幻灯片那样显示或者构成简单的动画效果。GIF的图像深度为1～8位，即最多支持256种色彩的图像。

GIF文件格式定义了两种数据存储方式，一种是按行连续存储，存储顺序与显示器的显示顺序相同；另一种是按交叉方式存储，由于显示图像需要较长的时间，使用这种方法存放图像数据，用户可以在图像数据全部收到之前浏览这幅图像的全貌，而不觉得等待时间太长。目前，GIF文件格式在HTML文档中得到广泛使用。

（3）TIFF文件（.TIF）。由Aldus和Microsoft公司为扫描仪和桌面出版系统研制开发的一种通用的图像文件格式。TIFF是电子出版CD-ROM中的一个重要的图像文件格式。TIFF格式非常灵活易变，它又定义了4类不同的格式：TIFF-B适用于二值图像；TIFF-G适用于黑白灰度图像；TIFF-P适用于带调色板的彩色图像；TIFF-R适用于RGB真彩图像。无论在视觉上还是其他方面，都能把任何图像编码成二进制形式而不丢失任何属性。

（4）PCX文件（.PCX）。PC Paintbrush（PC画笔）的图像文件格式。PCX的图像深度可选为1位、4位、8位，对应单色、16色及256色，不支持真彩色。PCX文件采用RLE行程编码，文件体中存放的是压缩后的图像数据。因此，将采集到的图像数据写成PCX格式文件时，要对其进行RLE编码；而读取一个PCX文件时，首先要对其进行解码，才能进一步显示和处理。

（5）PNG文件格式。作为GIF的替代品而开发的，它能够避免使用GIF文件所遇到的常见问题。它从GIF那里继承了许多特征，增加了一些GIF文件所没有的特性。用来存储灰度图像时，灰度图像的深度可达16位；存储彩色图像时，彩色图像的深度可达48位。在压缩数据时，它采用了一种在LZ77算法基础上改进的无损压缩算法。

（6）JPEG文件（.JPG）。采用一种有损压缩算法，其压缩比约为5∶1～50∶1，甚至更高。对一幅图像按JPEG格式进行压缩时，可以根据压缩比与压缩效果要求选择压缩质量因子。JPG格式文件的压缩比例很高，非常适用于要处理大量图像的场合，它是一种有损压缩的静态图像文件存储格式，压缩比例可以选择，支持灰度图像、RGB真彩色图像和CMYK真彩色图像。

（7）Targe文件（.TGA）。用于存储彩色图像，可支持任意大小的图像，最高彩色数可达32位。专业图形用户经常使用TGA点阵格式保存具有真实感的三维有光源图像。

（8）WMF文件（.WMF）。只使用在Windows中，它保存的不是点阵信息，而是函数调用信息。它将图像保存为一系列GDI（图形设备接口）的函数调用，在恢复时，应用程序执行源文件（即执行一个个函数调用）在输出设备上画出图像。WMF文件具有设备无关性，文件结构好，但是解码复杂，其效率比较低。

（9）EPS文件（.EPS）。用PostScript语言描述的ASCⅡ图形文件，在PostScript图形打印机上能打印出高品质的图形，能够表示32位图形（图像）。EPS文件格式分为Photoshop EPS格式和标准EPS格式，其中标准EPS格式又可分为图形格式和图像格式。

（10）DIF文件（.DIF）。AutoCAD中的图形，它以ASCII方式存储图像，表现图形在尺寸大小方面十分精确，可以被CorelDraw、3DS等软件调用编辑。

（11）CDR文件（.CDR）。CorelDraw的文件格式，所有CorelDraw应用程序均能使用的图形（图像）文件。

6.4　动画和视频

6.4.1　基础知识

1．基本概念

动画是将静态的图像、图形及图画等按一定时间顺序显示而形成连续的动态画面。从传统意义上说，动画是通过在连续多格的胶片上拍摄一系列画面，并将胶片以一定的速度放映，从而产生动态视觉的技术和艺术。电影放映的标准是每秒放映24帧（画面），每秒遮挡24次，刷新率是每秒48次。一般来说，动画是一种动态生成一系列相关画面的处理方法，其中的每一幅与前一幅略有不同。计算机动画是在传统动画的基础上，使用计算机图形图像技术而迅速发展起来的一门高新技术。计算机动画是采用连续播放静止图像的方法产生景物运动的效果，即使用计算机产生图形、图像运动的技术。计算机动画的原理与传统动画基本相同，只是在传统动画的基础上把计算机技术用于动画的处理和应用，并可以达到传统动画所达不到的效果。动画的内容不仅实体在运动，而且色调、文理、光影效果也可以不断改变。计算机生成的动画不仅可记录在胶片上，而且还可以记录在磁带、磁盘和光盘上，放映时不仅使用计算机显示器显示，而且可以使用电视机屏幕显示以及使用投影仪投影到银幕的方法显示。在多媒体应用中，计算机动画可以十分简单，也可以十分复杂，从某个对象、物体的运动到电视广告、动画片等。

动画的本质是运动。根据运动的控制方式可将计算机动画分为实时动画和逐帧动画两种。实时动画是用算法来实现物体的运动；逐帧动画是在传统动画基础上引申而来的，也即通过一帧一帧显示动画的图像序列而实现运动的效果。根据视觉空间的不同，计算机动画又有二维动画和三维动画之分。

1）实时动画

实时动画采用各种算法来实现运动物体的运动控制。采用的算法有运动学算法、动力学算法、反向运动学算法、反向动力学算法和随机运动算法等。在实时动画中，计算机对输入的数据进行快速处理，并在人眼察觉不到的时间内将结果随时显示出来。实时动画的响应时间与许多因素有关，如动画图像大小、动画图像复杂程度、运算速度快慢（计算机）以及图形的计算是采用软件还是硬件等。

2）矢量动画

矢量动画是由矢量图衍生出的动画形式。矢量图是利用数学函数来记录和表示图形线条、颜色、尺寸和坐标等属性，矢量动画通过各种算法实现各种动画效果，如位移、变形和变色等。也就是说，矢量动画是通过计算机的处理，使矢量图产生运动效果形成的动画。使用矢量动画，可以使一个物体在屏幕上运动，并改变其形状、大小、颜色、透明度、旋转角度以及其他一些属性参数。矢量动画采用实时绘制的方式显示一幅矢量图，当图形放大或缩小时，都保持光滑的线条，不会影响质量，也不会改变文件的容量。

3）二维动画

二维动画是对传统动画的一个改进，它不仅具有模拟传统动画的制作功能，而且可以发挥计算机所特有的功能，如生成的图像可以拷贝、粘贴、翻转、放大缩小、任意移位以及自动计算等。图形、图像技术都是计算机动画处理的基础。图像是指用像素点组成的画面，而图形是指几何形体组成的画面。在二维动画处理中，图像技术有利于绘制实际景物，可用于绘制关键帧、画面叠加、数据生成；图形技术有利于处理线条组成的画面，可用于自动或半自动的中间画面生成。计算机在二维动画中的作用包括输入和编辑关键帧；计算和生成中间帧；定义和显示运动路径；产生特技效果；实现画面与声音的同步；控制运动系列的记录等。二维动画的处理主要包括两个基本步骤：屏幕绘画和动画生成。屏幕绘画主要是使用图像处理软件完成，有时为了自动或半自动生成动画，也采用图形方法来描述画面。动画生成以屏幕绘画的结果（作为关键帧）为基础进行生成处理，最终完成动画创作。

4）三维动画

三维画面中的景物有正面，也有侧面和反面，调整三维空间的视点，能看到不同的内容。二维画面则不然，无论怎么看，画面的内容是不变的。三维与二维动画的区别主要在于采用不同的方法获得动画中的景物运动效果。三维动画的制作过程不同于传统动画制作。根据剧情的要求，首先要建立角色、实物和景物的三维数据模型，再对模型进行光照着色（真实感设计），然后使模型动起来，即模型可以在计算机控制下在三维空间中运动，或近或远，或旋转或移动，或变形或变色等，最后对运动的模型重新生成图像再刷新屏幕，形成运动图像。

建立三维动画物体模型称为造型，也就是在计算机内生成一个具有一定形体的几何模型。在计算机中大致有三种形式来记录一个物体的模型。

（1）线框模型。用线条来描述一个形体，一般包括顶点和棱边。例如，用8条线来描述一个立方体。

（2）表面模型。用面的组合来描述形体，如用6个面来描述一个立方体。

（3）实体模型。任何一个物体都可以分解成若干个基本形体的组合，如一个立方体可以分解为各种形体的组合。这种用基本形体组合物体的模型就是实体模型。

三维动画的处理需要综合使用上述三种模型。一般情况下，先用线框模型进行概念设计，再将线框模型处理成表面模型以方便显示，使用实体模型进行动画处理。同一形体的三种模型可以相互转换。

物体模型只有通过光和色的渲染，才能产生自然界中常见的真实物体效果，这在动画中称为着色（真实感设计）。对物体着色是产生真实感图形图像的重要过程，它涉及到物体的材质、纹理以及照射的光源等方面。

（1）材质。描述任何物体，除了造型以外，还必须有一定的附加特征（属性）来指明它的外在特性。物体的外在特性在很大程度上取决于构成它的材料。一般把材料的性质简称为材质，不同的材质表现出的质感是不同的。材质主要用来说明物体对入射光线作出的反应。一般来说，光线或者被反射、吸收，或者被透射、折射。反射的色光正是该物体呈现的颜色，而透射、折射产生的色光与材质有很大关系。

（2）纹理。纹理是物体表面细节，大多数物体的表面具有纹理。有了纹理可以改变物体的外观，甚至改变其形状。物体的纹理一般分为两种：一种是颜色纹理，如墙面贴纸、陶器上的图案等。颜色纹理取决于物体表面的光学性质。另一种是几何纹理，如人的皮肤、橘子的褶皱等。几何纹理与物体表面的微观几何形状有关。

（3）光源。给一个场景着色时必须知道有关光源的特性，如光源的位置、颜色、亮度和方向等，这些信息要由用户通过照明模型设定。决定光照射到物体表面并形成颜色的方法称为浓淡处理。浓淡处理要用到物体表面的几何材质信息，并对入射光进行考察，从而找出表面反射的色光。在进行浓淡处理时，一般对每个物体表面的每一个点分别进行处理，而且仅考虑来自光源的光照效果，而不考虑来自其他物体发出的光的影响。

三维动画处理的基本目的是控制形体模型的运动，获得运动显示效果。其处理过程中涉及建立线框模型、表面模型和实体模型。此外，一个好的三维动画应用系统能够将形体置于指定的灯光环境中，使形体的色彩在灯光下生成光线反映和阴影效果。运动物体不仅表现为几何位置改变，还带有光、色、受力、碰撞以及物体本身的变形等。动画控制也称为运动模拟。首先，计算机要确定每个物体的位置和相互关系，建立其运动轨迹和速度，选择运动形式（平移、旋转和扭曲等）。然后，需确定物体形体的变态方式和变异速度。光源确定好了以后，调整拍摄的位置、方向、运动轨迹及速度，就可以显示观看画面效果。

三维动画最终要生成一幅幅二维画面，并按一定格式记录下来，这个过程称为动画生成。动画生成后，可以在屏幕上播放，也可以录制在光盘或录像带上。

2．模拟视频

1）模拟视频原理

电视是当代最有影响的多媒体信息传播工具，在综合文、图、声、像等作为信息传播媒体这一点上完全与多媒体系统相同，不同的是电视系统不具备交互性，传播的信号是模拟信号。电视信号记录的是连续的图像或视像以及伴音（声音）信号。电视信号通过光栅扫描的方法显示在荧光屏（屏幕）上，扫描从荧光屏的顶部开始，一行一行地向下扫描，直至荧光屏的最底部，然后返回到顶部，重新开始扫描。这个过程产生的一个有序的图像信号的集合，组成了电视图像中的一幅图像，称为一帧，连续不断的图像序列就形成了动态视频图像。水平扫描线所能分辨出的点数称为水平分辨率，一帧中垂直扫描的行数称为垂直分辨率。一般来说，点越小，线越细，分辨率越高。每秒钟所扫描的帧数就是帧频，一般在每秒25帧时人眼就不会感觉到闪烁。彩色电视系统采用相加混色，使用RGB作为三基色进行配色，产生R、G、B三个输出信号。RGB信号可以分别传输，也可以组合起来传输。根据亮色度原理，任何彩色信号都可以分解为亮度和色度。

2）彩色电视的制式

电视信号的标准也称为电视的制式，目前世界各地使用的标准不完全相同，制式的区分主要在于其帧频的不同、分辨率的不同、信号带宽及载频的不同、彩色空间的转换关系不同等。世界上现行的彩色电视制式主要有NTSC制、PAL制和SECAM制三种，如表6-2所示。

表6-2　彩色数字电视制式

美国、加拿大、日本、韩国、中国台湾、菲律宾等国家和地区采用NTSCM制式；西德、英国、中国、中国香港、新西兰等国家和地区采用PAL制式；法国、东欧、中东一带采用SECAM制式。

我国电视制式（PAL）采用625行隔行扫描光栅，分两场扫描。行扫描频率为15625Hz，周期为64µs；场扫描频率为50Hz，周期为20ms；帧频是25Hz，周期为40ms。在发送电视信号时，每一行中传送图像的时间是52.2µs，对应行扫描的正程时间，其余的11.8µs不传送图像，对应行扫描的逆程时间加入行消隐信号和行同步信号，这样不影响行扫描发送或显示图像信息。每一场扫描的行数为625/2行，其中25行作场回扫，不传送图像。

采用隔行扫描比采用逐行扫描所占用的信号传输带宽要减少一半，这样有利于信道的利用；有利于信号传输和处理。采用每秒25帧的帧频（25Hz）能以最少的信号容量有效地满足人眼的视觉残留特性。采用50Hz的场频是因为我国的电网频率为50Hz，采用50Hz的场刷新频率可有效地去掉电网信号的干扰。

3．数字视频

视频信息是指活动的、连续的图像序列。一幅图像称为一帧，帧是构成视频信息的基本单元。在多媒体应用系统中，视频以其直观和生动等特点得到广泛的应用。视频与动画一样，是由一幅幅帧序列组成，这些帧以一定的速率播放，使观看者得到连续运动的感觉。计算机的数字视频是基于数字技术的图像显示标准，它能将模拟视频信号输入到计算机进行数字化视频编辑制成数字视频。全屏幕视频是指显示的视频图像充满整个屏幕，能以30帧／秒的速度刷新画面，使画面不会产生闪烁和不连贯的现象。电视机、激光视盘和摄像机等都可提供丰富多彩的模拟视频信号，常常需要把这些信号与计算机图形图像结合在一个共同的空间，通过处理达到最佳的效果，然后输出到计算机的显示器或其他电视设备上。模拟视频信号进入计算机，首先需要解决模拟视频信息的数字化问题。与音频数字化一样，视频数字化的目的是将模拟信号经模数转换和彩色空间变换等过程，转换成计算机可以显示和处理的数字信号。由于电视和计算机的显示机制不同，因此要在计算机上显示视频图像需要作许多处理。例如，电视是隔行扫描，计算机的显示器通常是逐行扫描；电视是亮度（Y）和色度（C）的复合编码，而PC的显示器工作在RGB空间；电视图像的分辨率和显示屏的分辨率各不相同等。这些问题在电视图像数字化过程中都需考虑。一般，对模拟视频信息进行数字化采取如下方式。

（1）先从复合彩色电视图像中分离出彩色分量，然后数字化。目前市场上的大多数电视信号都是复合的全电视信号，如录像带、激光视盘等存储设备上的电视信号。对这类信号的数字化，通常是将其分离成YUV、YIQ或RGB彩色空间的分量信号，然后用三个A/D转换器分别进行数字化。这种方式称为复合数字化。

（2）先对全彩色电视信号数字化，然后在数字域中进行分离，以获得YUV、YIQ或RGB分量信号。用这种方法对电视图像数字化时，只需一个高速A/D转换器。这种方式称为分量数字化。

视频信息数字化的过程比声音复杂一些，它是以一幅幅彩色画面为单位进行的。分量数字化方式是较多使用的一种方式。电视信号使用的彩色空间是YUV空间，即每幅彩色画面有亮度（Y）和色度（U、V）三个分量，对这三个分量需分别进行取样和量化，得到一幅数字图像。由于人眼对色度信号的敏感程度远不如对亮度信号那么灵敏，所以色度信号的取样频率可以比亮度信号的取样频率低一些，以减少数字视频的数据量。目前使用的色度信号取样格式如表6-3所示。

表6-3　色度信号取样格式

CCIR601标准推荐使用4∶2∶2格式，并对采样频率、采样结构和彩色空间转换等都做了严格的规定。使用4∶2∶2格式时，亮点样本Y用13.5MHz采样频率，色度样本Cr
、Cb
用6.75MHz的采样频率。

4．数字视频标准

国际无线电咨询委员会（International Radio Consultative Committee, CCIR）制定的广播级质量数字电视编码标准，即CCIR601标准，为PAL、NTSC和SECAM电视制式之间确定了共同的数字化参数。该标准规定了彩色电视图像转换成数字图像所使用的采样频率、采样结构和彩色空间转换等。这个标准对多媒体的开发和应用十分重要。

（1）采样频率。CCIR为PAL、NTSC和SECAM电视制式制定的共同的电视图像采样频率标准为：采样频率＝13.5MHz。

（2）分辨率。PAL和SECAM制式的亮度信号，每一扫描行采集864个样本点；而对于NTSC制式的亮度信号，每一扫描行采集858个样本点。CCIR 601规定对所有制式，每一扫描行的有效样本点数均为720个。

（3）数据量。CCIR 601规定，每个样本点都按8位数字化，即有256个等级。但实际上亮度信号占220级，色度信号占225级，其他位作同步、编码等控制使用。

5．视频压缩编码

数字图像数据的数据量大，而数字视频信息的数据量就更加突出。例如，每帧352×240像素点，图像深度16位的图像，其数据量约为1.3Mb，每秒30帧，其数据量就高达40Mb/s，这么大的数据量无论是传输、存储还是处理，都是极大的负担。为了解决这个问题，必须对数字视频信息进行压缩编码处理。

视频压缩的目标是在尽可能保证视觉效果的前提下减少视频数据率。视频是连续的静态图像，其压缩编码算法与静态图像的压缩编码算法有某些共同之处。但是视频还有其自身的特性，在压缩时必须考虑其运动特性。由于视频信息中各画面内部有很强的信息相关性，相邻画面又有高度的相容性（连贯性），再加上人眼的视觉特性，因此数字视频的数据量可压缩几十倍甚至几百倍。视频信息压缩编码的方法很多，一般在选择或设计视频压缩编码算法时需要掌握一些视频压缩的基本概念。

1）无损压缩与有损压缩

视频压缩中无损和有损压缩的概念与静态图像基本类似。无损压缩是指压缩前和解压缩后的数据完全一致。多数的无损压缩都采用RLE行程编码算法。这种算法特别适合于由计算机生成的图像，它们一般具有连续的色调。但是，无损算法一般对数字视频和自然图像的压缩效果不理想，因为其色调细腻，不具备大块的连续色调。

有损压缩意味着解压缩后的数据与压缩前的数据不一致。在压缩的过程中要丢失一些人眼和人耳所不敏感的图像或声音信息，而且丢失的信息不可恢复。几乎所有高压缩的算法都采用有损压缩，这样才能达到低数据率的目标。丢失的数据率与压缩比有关，压缩比越小，丢失的数据越多，解压缩后的效果一般越差。此外，某些有损压缩算法采用多次重复压缩的方式，这样还会引起额外的数据丢失。

2）帧内和帧间压缩

帧内压缩也称为空间压缩。同一景物表面上各采样点的颜色之间往往存在着连贯性，但是基于离散像素采样来表示景物颜色的方式通常没有利用景物表面颜色的空间连贯性，从而产生了空间冗余。当压缩一帧视频时，仅考虑本帧的数据而不考虑相邻帧之间的冗余信息，这实际上与静态图像压缩类似。由于帧内压缩时各个帧之间没有相互关系，所以压缩后的视频数据仍可以以帧为单位进行编码。帧内压缩一般达不到很高的压缩（很小的压缩比）。

视频具有运动的特性，故还可以采用帧间压缩的方法。采用帧间压缩基于许多视频或动画的连续前后两帧具有很大的相关性，或者说前后两帧信息变化很小的特点。例如，在演示一个球在静态背景前滚动的视频片段中，连续两帧中的大部分图像是基本不变的（背景不变），即连续的视频其相邻帧之间具有冗余信息。根据这一特性，压缩相邻帧之间的冗余就可以进一步提高压缩量，减小压缩比。帧间压缩也称为时间压缩，它通过比较时间轴上不同帧之间的数据进行压缩。帧差值算法是一种典型的时间压缩法，它通过比较本帧与相邻帧之间的差异，仅记录本帧与其相邻帧的差值，这样可以大大减少数据量。例如，如果一段视频不包含大量超常的剧烈运动景象，而是由一帧一帧的正常运动构成，采用这种算法就可以达到很好的效果。

3）对称和不对称编码

对称性是压缩编码的一个关键特征。对称意味着压缩和解压缩占用相同的计算处理能力和时间。对称算法适合实时压缩和传送视频，如视频会议应用就是以采用对称的压缩编码算法为好。而在电子出版和其他多媒体应用中，一般把视频预先压缩处理好，而后再播放，因此可以采用不对称编码。不对称或非对称意味着压缩时需要花费大量的处理能力和时间，而解压缩时则能较好地实时回放，即以不同的速度进行压缩和解压缩。一般地说，压缩一段视频的时间比回放（解压缩）该视频的时间要多得多。例如，压缩3分钟的视频片段可能需要10多分钟的时间，而该片段实时回放只需3分钟。

目前，国际标准化组织制定的有关视频（及其伴音）压缩编码的几种标准及其应用范围如表6-4所示。

表6-4　压缩编码的标准

6.4.2　视频文件格式

常用的视频文件格式如下。

（1）GIF文件（.GIF）。GIF（Graphics Interchange Format）是CompuServe公司推出的一种高压缩比的彩色图像文件。GIF格式采用无损压缩方法中效率较高的LZW算法，主要用于图像文件的网络传输。考虑到网络传输的实际情况，GIF图像格式除了一般的逐行显示方式之外，还增加了渐显方式。也就是说，在图像传输过程中，用户可以先看到图像的大致轮廓，然后随着传输过程的继续而逐渐看清图像的细节部分，从而适应了用户的观赏心理。目前因特网上大量采用的彩色动画文件多为GIF格式。

（2）Flic文件（.FLI/.FLC）。Flic是Autodesk公司在其出品的Autodesk Animator/Animator Pro/3D Studio等2D/3D动画制作软件中采用的彩色动画文件格式。其中.FLI是最初的基于320×200分辨率的动画文件格式，.FLC是.FLI的进一步扩展，采用了更高效的数据压缩技术，其分辨率也不再局限于320×200。Flic文件采用行程编码（RLE）算法和Delta算法进行无损的数据压缩，具有较高的数据压缩率。

（3）AVI文件（.AVI）。AVI（Audio Video Interleaved）是Microsoft公司开发的一种符合RIFF文件规范的数字音频与视频文件格式，Windows 95/98、OS/2等多数操作系统直接支持。AVI格式允许视频和音频交错在一起同步播放，支持256色和RLE压缩，但AVI文件并未限定压缩标准。因此，AVI文件格式只是作为控制界面上的标准，不具有兼容性，用不同压缩算法生成的AVI文件，必须使用相同的解压缩算法才能播放出来。AVI文件目前主要应用在多媒体光盘上，用来保存电影、电视等各种影像信息，有时也出现在因特网上，供用户下载、欣赏新影片的片段。

（4）Quick Time文件（.MOV/.QT）。Quick Time是Apple公司开发的一种音频、视频文件格式，用于保存音频和视频信息，具有先进的视频和音频功能，被Apple Mac OS、Windows 95/98/NT等主流平台支持。Quick Time文件支持25位彩色，支持RLE、JPEG等领先的集成压缩技术，提供150多种视频效果，并配有提供了200多种MIDI兼容音响和设备的声音装置。新版本的Quick Time进一步扩展了原有功能，包含了基于Internet应用的关键特性，能够通过Internet提供实时的数字化信息流、工作流与文件回放功能。此外，Quick Time还采用了Quick Time VR（QTVR）技术的虚拟现实技术，用户通过鼠标或键盘的交互式控制，可以观察某一地点周围360°的景象，或者从空间任何角度观察某一物体。Quick Time以其领先的多媒体技术和跨平台特性；较小的存储空间要求；技术细节的独立性以及系统的高度开放性，得到广泛的认可和应用。

（5）MPEG文件（.MPEG/.MPG/.DAT）。MPEG文件格式是运动图像压缩算法的国际标准，它包括MPEG视频、MPEG音频和MPEG系统（视频、音频同步）三个部分。MPEG压缩标准是针对运动图像设计的，其基本方法是单位时间内采集并保存第一帧信息，然后只存储其余帧对第一帧发生变化的部分，从而达到压缩的目的。MPEG的平均压缩比为50∶1，最高可达200∶1，压缩效率非常高，同时图像和音响的质量也非常好，并且在PC上有统一的标准格式，兼容性相当好。

（6）RealVideo文件（.RM）。RealVideo文件是Real Networks公司开发的一种新型流式视频文件格式，它包含在Real Networks公司所制定的音频视频压缩规范RealMideo中，主要用来在低速率的广域网上实时传输活动视频影像，可以根据网络数据传输速率的不同而采用不同的压缩比率，从而实现影像数据的实时传输和实时播放。RealVideo除了可以以普通的视频文件形式播放之外，还可以与RealVideo服务器相配合，在数据传输过程中边下载边播放视频影像，而不必像大多数视频文件那样，必须先下载然后才能播放。

6.5　多媒体网络

计算机网络是将多个计算机连接起来以实现计算机通信以及广泛实现多媒体信息共享，在网络上的分布式与协作操作就不可避免。多媒体空间的合理分布和有效的协作操作将极大地缩小个体与群体、局部与全球的工作差距。超越时空限制、充分利用信息、协同合作、相互交流、节约大量的时间和经费等是多媒体的基本目标。

网络具备传播信息的强大功能，并且在实际生活中扮演了媒体的角色。其次，国内各种机构都已开辟网上传播的新领域，网络报纸杂志、网络广播娱乐、网上教育、电子商务等业务应运而生，因特网逐渐达到了作为大众传播媒体的标准。

6.5.1　超文本与超媒体

1．超文本的概念

超文本是一种文本，与一般的文本文件的差别主要是组织方式不同，它是将文本中遇到的一些相关内容通过链接组织在一起，用户可以很方便地阅览这些相关内容。超文本是一种文本管理技术，它以节点为单位组织信息，在节点与节点之间通过表示它们之间关系的链加以连接，构成特定内容的信息网络。节点、链和网络是超文本所包含的三个基本要素。

（1）节点。超文本中存储信息的单元，由若干个文本信息块（可以是若干屏、窗口、文件或小块信息）组成。节点大小按需要而定。

（2）链。建立不同节点（信息块）之间的联系。每个节点都有若干个指向其他节点或从其他节点指向该节点的指针，该指针称为链。链通常是有向的，即从链源（源节点）指向链宿（目的节点）。链源可以是热字、热区、图元、热点或节点等。一般链宿都是节点。

（3）网络。由节点和链组成的一个非单一、非顺序的非线性网状结构。

文本中的词、短语、图像、声音剪辑或影视剪辑之间的链接，或者其他的文件、超文本文件的链接，称为超链接（热链接）。词、短语、图像、声音剪辑或影视剪辑和其他的文件通常被称为对象或者文档元素，因此超链接是对象之间或者文档元素之间的链接。建立相互链接的这些对象不受空间位置的限制，它们可以在同一个文件内也可以在不同的文件之间，还可以通过因特网与世界上任何一台联网计算机上的文件建立链接关系。

2．超媒体的概念

用超文本方式组织和处理多媒体信息就是超媒体。超媒体不仅包含文字，而且还可以包含图形、图像、动画、声音和影视图像片断，这些媒体之间也是用超链接组织的，而且它们之间的链接也是错综复杂的。超媒体与超文本之间的不同是，超文本主要是以文字的形式表示信息，建立的链接关系主要是文句之间的链接关系。超媒体除使用文本外，还使用图形、图像、动画、声音或影视片断等多种媒体来表示信息，建立的链接关系是图形、图像、动画、声音或影视片断等多种媒体之间的链接关系。

网页是Web站点上的文档。在Web网页上，为了区分有链接关系和没有链接关系的文档元素，对有链接关系的文档元素通常用不同颜色或下划线来表示。

6.5.2　流媒体

流媒体是指在网络中使用流式传输技术的连续时基媒体，而流媒体技术是指把连续的影像和声音信息经过压缩处理之后放到专用的流服务器上，让浏览者一边下载一边观看、收听，而不需要等到整个多媒体文件下载完成就可以即时观看和收听的技术。流媒体融合了多种网络以及音视频技术，在网络中要实现流媒体技术，必须完成流媒体的制作、发布、传播和播放等环节。

（1）流媒体系统通过某种流媒体技术，完成流媒体文件的压缩生成，经过服务器发布，然后在客户端完成流媒体文件的解压播放的整个过程。因此，一个流媒体系统一般由三部分组成：流媒体开发工具，用来生成流式格式的媒体文件；流媒体服务器组件，用来通过网络服务器发布流媒体文件；流媒体播放器，用于客户端对流媒体文件的解压和播放。目前应用比较广泛的流媒体系统主要有Windows Media系统、Real System系统和Quick Time系统等。

（2）流媒体一般采用建立在用户数据报协议之上的实时传输协议和实时流协议（RTP/RTSP）来传输实时的影音数据。RTP是针对多媒体数据流的一种传输协议，它被定义为在一对一或一对多的传输情况下工作，提供时间信息和实现流同步。RTSP定义了一对多的应用程序如何有效地通过IP网络传送多媒体数据。

（3）流式文件格式与多媒体压缩文件有所不同，编码的目的是为了适合在网络环境中边下载边播放。将压缩文件编码成流式文件，还需增加许多附加信息。为了使客户端接收到的数据包可以重新有序地播放，在实际的网络应用环境中，并不包含流媒体数据文件，而是流媒体发布文件，例如.RAM、.ASX等，它们本身不提供压缩格式，也不描述影视数据，其作用是以特定的方式安排影视数据的播放。不同流媒体系统具有不同的流式文件格式，例如，Real System系统具有的文件格式有RM，.RA，.RP和.RT；Windows Media系统具有的文件格式是.ASF和.ASX。

（4）浏览器是通过因特网邮件来识别各种不同的简单文件格式。Web浏览器都是基于HTTP的，HTTP中都内建有MIME。Web浏览器能够通过HTTP中内建的MIME来标记Web上的多媒体文件格式，包括各种流式文件。

（5）媒体播放器是一个应用软件，主要功能是用于播放多种格式的音频、视频序列。可以作为单独的应用程序运行，或作为一个复合文档中的嵌入对象。

多媒体网络技术（Multimedia Networking）在因特网上有很多应用，大致可分成两类：一类是以文本为主的数据通信，包括文件传输、电子邮件、网络新闻和Web等；另一类是以声音和视频图像为主的通信，通常把任何一种声音通信或图像通信的网络应用称为多媒体网络应用。

通常，声音或视频文件放在Web服务器上，由Web服务器通过HTTP把文件传送给用户。也可将声音或视频文件放在声音／视频流式播放服务器（Streaming Server）上，由流式播放服务器通过流放协议把文件传送给用户。流式播放服务器简称流放服务器。

目前声音和视频点播应用还没有完全直接集成到现在的Web浏览器中，所以一般采用媒体播放器来播放声音、音乐、动画和影视。典型的媒体播放器具有解压缩、消除抖动、错误纠正、用户播放控制等功能。现在可以将多媒体应用插件（Plug in）嵌入浏览器内部，与浏览器软件协同工作。这种技术把媒体播放器的用户接口软件放在Web客户端的用户界面上，浏览器在当前Web页面上保留屏幕空间，并且由媒体播放器来管理。客户端可使用多种方法来读取声音和影视文件，其中常见的方法有如下三种。

1）通过Web浏览器把声音／影视文件从Web服务器传送给媒体播放器

客户端读取声音／影视文件的最简方法是将声音／影视文件放到Web服务器上，然后通过浏览器把文件传送给媒体播放器，其过程如下。

（1）Web浏览器与Web服务器建立TCP连接，然后提交HTTP请求，请求传送声音／影视文件。

（2）Web服务器给Web浏览器发送响应请求消息以及请求的声音／影视文件。

（3）Web浏览器检查HTTP响应消息中内容的类型，调用相应的媒体播放器，然后把声音／影视文件或者是指向文件的指针传递给媒体播放器。

（4）媒体播放器播放声音／影视文件。

这种方法，媒体播放器必须通过Web浏览器才能从Web服务器上得到声音／影视文件，需要把整个文件下载到浏览器之后才把它传送给媒体播放器。这样就产生了播放延迟，影响了播放。因此，存在较大的延迟问题。

2）直接把声音／影视文件从Web服务器传送给媒体播放器

采用媒体播放器与Web服务器直接建立链接的方法，可以改进通过Web浏览器产生的延迟。在Web服务器和媒体播放器之间建立直接的TCP连接，可以实现把声音／影视文件直接传送给媒体播放器，其实现方法如下。

（1）用户通过超级链接以请求传送声音／影视文件。

（2）这个超级链接不是直接指向声音／影视文件，而是指向一个播放说明文件，这个文件包含有实际的声音／影视文件的地址（URL）。播放说明文件被封装在HTTP消息中。

（3）Web浏览器接收HTTP响应消息中内容的类型，调用相应的媒体播放器，然后把响应消息中的播放说明文件传送给媒体播放器。

（4）媒体播放器直接与Web服务器建立TCP连接，然后把传送声音／影视文件的HTTP请求消息发送到TCP连接上。

（5）在HTTP响应消息中把声音／影视文件传送给该媒体播放器并开始播放。

这种方法依然使用HTTP传送文件，不容易获得满意的交互性能（用户与Web服务器），如暂停、从头开始播放。

3）通过多媒体流放服务器将声音／影视文件传送给媒体播放器

采用Web服务器和流放服务器，将声音／影视文件直接传送给媒体播放器的方法，Web服务器用于Web页面服务，流放服务器用于声音／影视文件服务。其结构如图6-2所示。采用这种结构，媒体播放器向流放服务器请求传送文件，而不是向Web服务器请求传送文件，媒体播放器和流放服务器之间使用流放协议进行通信，声音／影视文件可以使用UDP（用户数据包协议）直接从流放服务器传送给媒体播放器。

图6-2　声音／影视文件传送给媒体播放器

6.6　多媒体计算机系统

一般的多媒体系统是由多媒体硬件系统和多媒体软件系统组成。也即将多媒体信息和计算机交互式控制相结合，由对媒体信号的获取、生成、存储、处理和传输数字化所组成的一个完整的系统。

通常将具有对多种媒体进行处理能力的计算机称为多媒体计算机（Multimedia Personal Computer, MPC）。随着芯片设计技术的发展，已将多媒体和通信功能集成到了CPU芯片中，形成了专用的多媒体微处理器，使得处理音频和视频就如处理数字和文字一样快捷。为了加快多媒体信息处理的速度，Micro Unity、Philips等公司将媒体处理器与通用的CPU结合，扩展了CPU的多媒体处理和通信功能。Intel公司推出了带有MMX技术的处理器。MMX技术提供了面向多媒体和通信功能的特性，并保持了微处理器的体系结构。另一方面，运用多媒体专用芯片和板卡，集成以PC为中心的组合平台。目前PC的多媒体功能大都通过附加插件和设备实现，如音频卡、视频卡、3D图形卡、网络卡以及CD-ROM驱动器、扫描仪、数码相机等。

6.6.1　多媒体计算机硬件系统

多媒体硬件系统可以看成是在PC的基础上进行了硬件扩充，以适应多媒体信息处理功能的需求。计算机硬件及声像等媒体输入输出设备构成了多媒体硬件平台。

多媒体计算机的主要硬件除了常规的硬件，如主机、软盘驱动器、硬盘驱动器、显示器和打印机之外，还要有音频信息处理硬件、视频信息处理硬件及光盘驱动器等。

（1）音频卡（Sound Card）。又称声卡，用于处理音频信息，它可以把话筒、录音机和电子乐器等输入的声音信息进行模数转换（A/D）、压缩等处理，也可以把经过计算机处理的数字化的声音信号通过还原（解压缩）、数模转换（D/A）后用音箱播放出来，或者用录音设备记录下来。

音频卡的关键技术包括数字音频、音乐合成（FM合成和波形表合面）和MIDI（数字音乐的国际标准，是指乐器数字接口）和音效。数字音频部分具有44.1kHz的采样率，8位以上的分辨率；具有录音和播放声音信号的功能；同时具有压缩采样信号的能力，最常用的压缩方法是自适应脉冲编码调制。数字音频的实现有不同的方法和芯片，大多数采用的是CODEC芯片，它具有硬件压缩功能；部分采用的是DSP＋ADC（数字信号处理芯片＋A/D转换器）方法，它利用软件的方法压缩数字音频信号。波形表合成使用DSP技术，它要求大容量的ROM，以获得高质量的演奏效果。

（2）视频卡（Video Card）。也称为显示卡，是基于PC的一种多媒体视频信号处理平台，用来支持视频信号的输入与输出。它可以采集视频源、声频源和激光视盘机、录像机、摄像机等设备的信息，经过编辑或特技等处理而产生非常精美的画面。还可以对这些画面进行捕捉、数字化、冻结、存储、压缩和输出等操作。对画面的修整、像素显示调整、缩放功能等都是视频卡支持的标准功能。视频卡可细分为视频捕捉卡、视频处理卡、视频播放卡以及TV编码器等专用卡，其功能是连接摄像机、VCR影碟机、TV等设备，以便获取、处理和表现各种动画和数字化视频媒体。

（3）光盘驱动器。光盘驱动器分为只读光盘驱动器（CD-ROM、DVD-ROM）和可读写光盘驱动器（CD-R、CD-RW），可读写光盘驱动器又称为刻录机。CD-ROM及DVD-ROM光盘驱动器目前是多媒体应用的主要设备，主要用于存储大量的影像、声音、动画、程序数据和高分辨率图像信息。CD-ROM可为多媒体计算机提供650MB的存储容量，DVD-ROM的存储量更大，双面可达17GB。CD-ROM驱动器价格便宜，对广大用户来说已经是必须配置的设备。CD-ROM的速度和兼容性也是重要的技术指标。单倍速（150Kb/s）是播放CD-Audio的最低要求，而十倍速的数据传输率可达1500Kb/s。

（4）扫描仪。扫描仪用于把摄影作品、绘画作品或其他印刷材料上的文字和图像，甚至实物，扫描输入到计算机中，进而对这些图像信息进行加工处理、管理、使用、存储和输出。扫描仪是获取图像的一种较简单的方法，现在已成为较流行的图像输入设备。扫描仪的种类很多，常用的有手持式扫描仪、滚筒式扫描仪和平板式扫描仪等。

（5）光学字符阅读器。光学字符阅读器是一种文字自动输入的设备。它通过扫描或摄像等光学输入方法从纸介质上获取文字的图像信息，利用各种模式识别算法分析文字的形态特征，判断文字的标准编码，并按通用格式存储在文本文件中。

（6）触摸屏。触摸屏是一种随多媒体技术发展而使用的输入设备。当手指点在屏幕上的菜单、图符等图形／图像按钮时，产生触摸信号，该信号经过变换后成为计算机可以处理操作的命令，从而实现人机交互。

（7）数字化仪。数字化仪是一种图形输入设备，它由平板加上连接的手动定位装置组成，主要用于输入地图、气象图等线型图。可以通过手动定位笔方便地获得每个线段的起始坐标，从而实现线型图输入。

（8）操纵杆。操纵杆是一种提供位置信息的输入设备，可支持其他定点输入设备，将它们（数字化仪、光笔等）输入的位置和按键信息提供给应用程序。在多媒体计算机上，操纵杆常用作游戏控制器，用来操纵电子游戏。它可以以模拟信号方式工作，也可以以数字信号方式工作，并可兼容各种游戏模式。

（9）绘图仪、投影仪和激光视盘播放器。绘图仪是一种图形输出设备；投影仪是一种将计算机输出的视频信号投影到幕布上的设备；激光视盘播放器（影碟机）是一种视频播放设备。

6.6.2　多媒体计算机软件系统

多媒体计算机软件系统主要包括多媒体操作系统、多媒体应用软件的开发工具和多媒体应用软件。

1．多媒体操作系统

多媒体操作系统必须具备对多媒体环境下的各个任务进行管理和调度；支持多媒体应用软件运行；对多媒体声像及其他多媒体信息的控制和实时处理；支持多媒体的输入输出及相应的软件接口；对多媒体数据和多媒体设备的管理和控制以及图形用户界面管理等功能。也就是说，它能够像一般操作系统处理文字、图形、文件那样去处理音频、图像、视频等多媒体信息；并能够对CD-ROM驱动器、录像机、MIDI设备、数码相机和扫描仪等各种多媒体设备进行控制和管理。传统计算机所使用的操作系统或多或少地都支持多媒体，例如，Windows 3.1在支持多媒体方面要比DOS强，但它们还不是真正意义上的多媒体操作系统。随着多媒体技术的发展，使得传统的操作系统增加了许多适应多媒体的内容，并研制出具备多媒体功能的操作系统。Apple公司的Quick Time以及当前流行的Windows系列产品Windows 98、Windows me、Windows 2000和Windows xp等都具备多媒体功能。

2．多媒体创作工具软件

多媒体创作工具是在多媒体操作系统之上的系统软件，它提供了建立多媒体节目的构件和框架的功能，可以实现媒体的组接和交互跳转功能。通常，多媒体创作工具软件是用于开发多媒体应用程序的应用工具软件，帮助应用开发人员提高开发工作效率。它们大体上都是一些应用程序生成器，将各种媒体素材按照超文本节点和链结构的形式进行组织，形成多媒体应用系统。

多媒体创作工具软件按照组织方式与数据管理大致上可分为表6-5所示的4类。

表6-5　多媒体创作工具软件分类

3．多媒体素材编辑软件

在多媒体应用中，很重要的一个环节是制作所需要的各种媒体素材。多媒体素材编辑软件用于采集、整理和编辑各种媒体数据。多媒体编辑软件主要包括如下部分。

（1）文本工具。其功能主要是文字处理（编辑、排版和识别等），常用的字处理工具有WPS、Notebook（记事本）、Writer（写字板）、Word和OCR（光学字符识别）等。

（2）图形／图像工具。主要功能包括显示图形／图像、图形／图像编辑、图像压缩、图像捕捉、图形／图像素材库，常用的图形／图像处理工具有PhotoShop、Illustrator、PhotoDeluxe、PageMaker（中文版）、CorelDraw、AutoCAD（中文版）和3DS Max等。

（3）动画工具。主要包括动画显示、动画编辑和动画素材库等功能，常用的动画工具软件有GIF Construction Set、Xara3D。

（4）视频工具有主要包括显示视频、视频编辑、视频压缩、视频捕捉和视频素材库等功能，常用的动画工具软件有Media Studio Pro（中文版）、Premiere。

（5）音频工具。主要包括音频播放、音频编辑、音频录制和声音素材库4个功能，常用的音频工具软件有CoolEditPro、GoldWave和Cake Walk Pro Audio。

（6）播放工具。主要用于显示、浏览或播放图像、音频、视频等多媒体数据，常用的播放工具软件有Media Player（媒体播放器）、ACDSee。

4．多媒体应用软件

多媒体应用软件是实现用户的应用程序及演示软件，它是直接面向用户或信息发送和接收的软件。这类直接与用户接口，用户只要根据应用软件所给出的操作命令，通过最简单的操作便可使用这些软件（根据多媒体系统终端用户要求而开发的应用软件）。例如，特定的专业信息管理系统；语音／Fax／数据传输调制管理应用系统；多媒体监控系统；CD-ROM光盘的播放软件；各种多媒体CAI软件；各种游戏软件等。在人们探讨应用多媒体技术解决自己面临的应用实际问题时，设计建造出各式各样的应用软件系统，使最终用户运用多媒体系统能够方便、易学、好用。除面向终端用户而制定的应用软件外，另一类是面向某一个领域的用户应用软件系统，这是面向大规模用户的系统产品，如多媒体会议系统、点播电视服务（VOD）医用、家用、军用和工业应用等。

第7章　数据库技术基础

数据库技术是研究数据库的结构、存储、设计、管理和应用的一门软件学科。数据库系统本质上是一个用计算机存储信息的系统。数据库管理系统是位于用户与操作系统之间的一层数据管理软件，其基本目标是提供一个可以方便、有效地存取数据库信息的环境。数据库就是信息的集合，它是收集计算机数据的仓库或容器，系统用户可以对这些数据执行一系列操作。设计数据库系统的目的是为了管理大量信息，给用户提供数据的抽象视图，即系统隐藏有关数据存储和维护的某些细节。对数据的管理涉及到信息存储结构的定义，信息操作机制的提供，安全性保证，以及多用户对数据的共享问题。

本章主要介绍一些背景知识和基本概念，使读者了解数据库的基本内容，形成数据库系统的总体框架，了解数据库系统在计算机系统中的地位以及数据库系统的功能。

7.1　基本概念

7.1.1　数据库与数据库管理系统

数据是描述事物的符号记录，它具有多种表现形式，可以是文字、图形、图像、声音和语言等。信息是现实世界事物的存在方式或状态的反映。信息具有可感知、可存储、可加工、可传递和可再生等自然属性，信息已是社会各行各业不可缺少的资源，这也是信息的社会属性。数据是信息的符号表示，而信息是具有特定释义和意义的数据。

数据库系统（DataBase System, DBS）是一个采用了数据库技术，有组织地、动态地存储大量相关联数据，方便多用户访问的计算机系统。广义上讲，DBS是由数据库、硬件、软件和人员组成。

（1）数据库（DataBase, DB）。是统一管理的、长期储存在计算机内的，有组织的相关数据的集合。其特点是数据间联系密切、冗余度小、独立性较高、易扩展，并且可为各类用户共享。

（2）硬件。构成计算机系统的各种物理设备，包括存储数据所需的外部设备。硬件的配置应满足整个数据库系统的需要。

（3）软件。其中包括操作系统、数据库管理系统及应用程序。数据库管理系统（DataBase Management System, DBMS）是数据库系统的核心软件，要在操作系统的支持下工作，解决如何科学地组织和储存数据，如何高效地获取和维护数据的系统软件。其主要功能包括数据定义功能、数据操纵功能、数据库的运行管理和数据库的建立与维护。

（4）人员。人员主要有4类。第一类为系统分析员和数据库设计人员，系统分析员负责应用系统的需求分析和规范说明，他们和用户及数据库管理员一起确定系统的硬件配置，并参与数据库系统的概要设计；数据库设计人员负责数据库中数据的确定、数据库各级模式的设计。第二类为应用程序员，负责编写使用数据库的应用程序，这些应用程序可对数据进行检索，建立，删除或修改。第三类为最终用户，他们应用系统的接口或利用查询语言访问数据库。第四类用户是数据库管理员（Data Base Administrator, DBA），负责数据库的总体信息控制。DBA的具体职责包括决定数据库中的信息内容和结构；决定数据库的存储结构和存取策略；定义数据库的安全性要求和完整性约束条件；监控数据库的使用和运行；数据库的性能改进、数据库的重组和重构，以提高系统的性能。

7.1.2　DBMS的功能

DBMS主要是实现对共享数据有效地组织、管理和存取，因此DBMS应具有如下几个方面的功能。

1．数据定义

DBMS提供数据定义语言（Data Definition Language, DDL），用户可以对数据库的结构描述，包括外模式、模式和内模式的定义；数据库的完整性定义；安全保密定义，如口令、级别和存取权限等。这些定义存储在数据字典中，是DBMS运行的基本依据。

2．数据库操作

DBMS向用户提供数据操纵语言（Data Manipulation Language, DML），实现对数据库中数据的基本操作，如检索、插入、修改和删除。DML分为两类：宿主型和自含型。所谓宿主型，是指将DML语句嵌入某种主语言（如C、COBOL等）中使用；自含型是指可以单独使用DML语句，供用户交互使用。

3．数据库运行管理

数据库在运行期间多用户环境下的并发控制、安全性检查和存取控制、完整性检查和执行、运行日志的组织管理、事务管理和自动恢复等是DBMS的重要组成部分。这些功能可以保证数据库系统的正常运行。

4．数据组织、存储和管理

DBMS分类组织、存储和管理各种数据，包括数据字典、用户数据和存取路径等。要确定以何种文件结构和存取方式在存储级上组织这些数据，以提高存取效率。实现数据间的联系、数据组织和存储的基本目标是提高存储空间的利用率。

5．数据库的建立和维护

数据库的建立和维护包括数据库的初始建立、数据的转换、数据库的转储和恢复、数据库的重组和重构、性能监测和分析等。

6．其他功能

如DBMS与网络中其他软件系统的通信功能，一个DBMS与另一个DBMAS或文件系统的数据转换功能等。

7.1.3　DBMS的特征及分类

1．DBMS的特征

通过DBMS来管理数据具有如下特点。

（1）数据结构化且统一管理。数据库中的数据由DBMS统一管理。由于数据库系统采用复杂的数据模型表示数据结构，数据模型不仅描述数据本身的特点，还描述数据之间的联系。数据不再面向某个应用，而是面向整个应用系统。数据易维护、易扩展，数据冗余明显减少，真正实现了数据的共享。

（2）有较高的数据独立性。数据的独立性是指数据与程序独立，将数据的定义从程序中分离出去，由DBMS负责数据的存储，应用程序关心的只是数据的逻辑结构，无须了解数据在磁盘上的数据库中的存储形式，从而简化应用程序，大大减少应用程序编制的工作量。数据的独立性包括数据的物理独立性和数据的逻辑独立性。

（3）数据控制功能。DBMS提供了数据控制功能，以适应共享数据的环境。数据控制功能包括对数据库中数据的安全性、完整性、并发和恢复的控制。

①　数据库的安全性保护。数据库的安全性（security）是指保护数据库以防止不合法的使用所造成的数据泄漏、更改或破坏。这样，用户只能按规定对数据进行处理，例如，划分了不同的权限，有的用户只能有读数据的权限，有的用户有修改数据的权限，用户只能在规定的权限范围内操纵数据库。

②　数据的完整性。数据库的完整性是指数据库正确性和相容性，是防止合法用户使用数据库时向数据库加入不符合语义的数据。保证数据库中数据是正确的，避免非法的更新。

③　并发控制。在多用户共享的系统中，许多用户可能同时对同一数据进行操作。DBMS的并发控制子系统负责协调并发事务的执行，保证数据库的完整性不受破坏，避免用户得到不正确的数据。

④　故障恢复。数据库中的4类故障是事务内部故障、系统故障、介质故障及计算机病毒。故障恢复主要是指恢复数据库本身，即在故障引起数据库当前状态不一致后，将数据库恢复到某个正确状态或一致状态。恢复的原理非常简单，就是要建立冗余（redundancy）数据。换句话说，确定数据库是否可恢复的方法就是其包含的每一条信息是否都可以利用冗余地存储在别处的信息重构。冗余是物理级的，通常认为逻辑级是没有冗余的。

2．DBMS分类

DBMS通常可分为如下三类。

（1）关系数据库系统（Relation DataBase Systems, RDBS）。是支持关系模型的数据库系统。在关系模型中，实体以及实体间的联系都是用关系来表示。在一个给定的现实世界领域中，相应于所有实体及实体之间联系的关系的集合构成一个关系数据库，也有型和值之分。关系数据库的型也称为关系数据库模式，是对关系数据库的描述，是关系模式的集合。关系数据库的值也称为关系数据库，是关系的集合。关系数据库模式与关系数据库通常统称为关系数据库。在计算机方式下常见的FOXPRO和ACCESS等DBMS，严格地讲不能算是真正的关系型数据库，对许多关系类型的概念并不支持，但它却因为简单实用、价格低廉，目前拥有很大的用户市场。

（2）面向对象的数据库系统（Object-Oriented DataBase System, OODBS）。是支持以对象形式对数据建模的数据库管理系统，这包括对对象的类、类属性的继承、子类的支持。面向对象数据库系统主要有两个特点：一是面向对象数据模型能完整描述现实世界的数据结构，能表达数据间的嵌套、递归的联系；二是具有面向对象技术的封装性和继承性，提高了软件的可重用性。

（3）对象关系数据库系统（Object-Oriented Relation DataBase System, ORDBS）。是在传统的关系数据模型基础上，提供元组、数组、集合一类更为丰富的数据类型以及处理新的数据类型操作的能力，这样形成的数据模型被称为“对象关系数据模型”，基于对象关系数据模型的DBS称为对象关系数据库系统。

7.1.4　数据库系统的体系结构

数据库系统是数据密集型应用的核心，其体系结构受数据库运行所在的计算机系统的影响很大，尤其是受计算机体系结构中的联网、并行和分布的影响。站在不同的角度或不同层次上看，数据库系统体系结构也不同。站在最终用户的角度看，数据库系统体系结构分为集中式、分布式、C/S（客户端／服务器）和并行结构。站在数据库管理系统的角度看，数据库系统体系结构一般采用三级模式结构。

1．集中式数据库系统

分时系统环境下的集中式数据库系统结构诞生于20世纪60年代中期。当时的硬件和操作系统决定了分时系统环境下的集中式数据库系统结构成为早期数据库技术的首选结构。在这种系统中，不但数据是集中的，数据的管理也是集中的，数据库系统的所有功能，从形式的用户接口到DBMS核心都集中在DBMS所在的计算机上，如图7-1所示。目前大多数关系DBMS的产品也是从这种系统结构开始发展的，目前这种系统还在使用。

图7-1　集中式服务器结构

2．客户端／服务器体系结构

随着网络技术的迅猛发展，很多现代软件都采用客户端／服务器体系结构，在这种结构中，一个处理机（客户端）的请求被送到另一个处理机（服务器）上执行。其主要特点是客户端与服务器CPU之间的职责明确，客户端主要负责数据表示服务，而服务器主要负责数据库服务。

采用客户端／服务器结构后，数据库系统功能分为前端和后端。前端主要包括图形用户界面、表格生成和报表处理等工具；后端负责存取结构、查询计算和优化、并发控制以及故障恢复等。前端与后端通过SQL或应用程序来接口。ODBC（开放式数据库互连）和JDBC（Java程序数据库连接）标准定义了应用程序和数据库服务器通信的方法，也即定义了应用程序接口，应用程序用它来打开与数据库的连接、发送查询和更新以及获取返回结果等。

数据库服务器一般可分为事务服务器和数据服务器。

（1）事务服务器。事务服务器也称查询服务器。它提供一个接口，使得客户端可以发出执行一个动作的请求，服务器响应客户端请求，并将执行结果返回给客户端。用户端可以用SQL，也可以通过应用程序或使用远程过程调用机制来表达请求。一个典型的事务服务器系统包括多个在共享内存中访问数据的进程，包括服务器进程、锁管理进程、写进程、监视进程和检查点进程。

（2）数据服务器。数据服务器系统使得客户端可以与服务器交互，以文件或页面为单位对数据进行读取或更新。数据服务器与文件服务器相比提供更强的功能，所支持的数据单位可比文件还要小，如页、元组或对象；提供数据的索引机制和事务机制，使得客户端或进程发生故障时数据也不会处于不一致状态。

3．并行数据库系统

并行体系结构的数据库系统是多个物理上连在一起的CPU，而分布式系统是多个地理上分开的CPU。并行体系结构的数据库类型分为共享内存式多处理器和无共享式并行体系结构。

图7-2　共享式多处理器体系结构

1）共享内存式多处理器

共享内存式多处理器是指一台计算机上同时有多个活动的CPU，它们共享单个内存和一个公共磁盘接口，如图7-2所示。这种并行体系结构最接近于传统的单CPU处理器结构，其设计的主要挑战是用N个CPU来得到N倍单CPU的性能。但是，因为不同的CPU对公共内存的访问是平等的，这样可能会导致一个CPU被访问的数据被另一个CPU修改，所以必须要有特殊的处理。然而，由于内存访问采用的是一种高速机制，这种机制很难保证进行内存划分时不损失效率，所以这些共享内存访问问题会随着CPU个数的增加而变得难以解决。

2）无共享式并行体系结构

无共享式并行体系结构是指一台计算机上同时有多个活动的CPU，并且它们都有自己的内存和磁盘，如图7-3所示，图中粗线表示高速网络。在不产生混淆的情况下，也称为并行数据库系统。各个承担数据库服务责任的CPU划分它们自身的数据，通过划分的任务以及通过每秒兆位级的高速网络通信完成事务查询。

4．分布式数据库系统

分布式DBMS包括物理上分布、逻辑上集中的分布式结构和物理上分布、逻辑上分布的分布式数据库结构两种。前者的指导思想是把单位的数据模式（称为全局数据模式）按数据来源和用途，合理分布在系统的多个节点上，使大部分数据可以就地或就近存取。数据在物理上分布后，由系统统一管理，使用户不感到数据的分布。后者一般由两部分组成：一是本节点的数据模式，二是本节点共享的其他节点上有关的数据模式。节点间的数据共享由双方协商确定。这种数据库结构有利于数据库的集成、扩展和重新配置。

图7-3　无共享式并行体系结构

7.1.5　数据库的三级模式结构

实际上，数据库的产品很多，它们支持不同的数据模型，使用不同的数据库语言，建立在不同的操作系统上。数据的存储结构也各不相同，但体系结构基本上都具有相同的特征，采用“三级模式和两级映像”。如图7-4所示。

图7-4　数据库系统体系结构

数据库系统采用三级模式结构，这是数据库管理系统内部的系统结构。数据库有“型”和“值”的概念，“型”是指对某一数据的结构和属性的说明，“值”是型的一个具体赋值。

数据库系统设计员可在视图层、逻辑层和物理层对数据抽象，通过外模式、概念模式和内模式来描述不同层次上的数据特性。

1．概念模式

概念模式也称模式，是数据库中全部数据的逻辑结构和特征的描述，它由若干个概念记录类型组成，只涉及到型的描述，不涉及到具体的值。概念模式的一个具体值称为模式的一个实例，同一个模式可以有很多实例。

概念模式反映的是数据库的结构及其联系，所以是相对稳定的；而实例反映的是数据库某一时刻的状态，所以是相对变动的。

需要说明的是，概念模式不仅要描述概念记录类型，还要描述记录间的联系、操作、数据的完整性和安全性等要求。但是，概念模式不涉及到存储结构、访问技术等细节。只有这样，概念模式才算做到了“物理数据独立性”。

描述概念模式的数据定义语言称为“模式DDL（Schema Data Definition Language）”。

2．外模式

外模式也称用户模式或子模式，是用户与数据库系统的接口，是用户用到的那部分数据的描述。它由若干个外部记录类型组成。用户使用数据操纵语言对数据库进行操作，实际上是对外模式的外部记录进行操作。

描述外模式的数据定义语言称为“外模式DDL”。有了外模式后，程序员不必关心概念模式，只与外模式发生联系，按外模式的结构存储和操纵数据。

3．内模式

内模式也称存储模式，是数据物理结构和存储方式的描述，是数据在数据库内部的表示方式。定义所有的内部记录类型、索引和文件的组织方式，以及数据控制方面的细节。

例如，记录的存储方式是顺序存储，按照B树结构存储，还是Hash方法存储；索引按照什么方式组织；数据是否压缩存储，是否加密；数据的存储记录结构有何规定。

需要说明的是，内部记录并不涉及到物理记录，也不涉及到设备的约束。比内模式更接近于物理存储和访问的那些软件机制是操作系统的一部分（即文件系统）。例如，从磁盘上读、写数据。

描述内模式的数据定义语言称为“内模式DDL”。

总之，数据按外模式的描述提供给用户，按内模式的描述存储在磁盘上，而概念模式提供了连接这两极模式的相对稳定的中间观点，并使得两级的任意一级的改变都不受另一级的牵制。

4．两级映像

数据库系统在三级模式之间提供了两级映像：模式／内模式映像、外模式／模式映像。正因为这两级映像保证了数据库中的数据具有较高的逻辑独立性和物理独立性。

（1）模式／内模式的映像。存在于概念级和内部级之间，实现了概念模式到内模式之间的相互转换。

（2）外模式／模式的映像。存在于外部级和概念级之间，实现了外模式到概念模式之间的相互转换。

数据的独立性是指数据与程序独立，将数据的定义从程序中分离出去，由DBMS负责数据的存储，从而简化应用程序，大大减少应用程序编制的工作量。数据的独立性是由DBMS的二级映像功能来保证的。数据的独立性包括数据的物理独立性和数据的逻辑独立性。

（1）数据的物理独立性。是指当数据库的内模式发生改变时，数据的逻辑结构不变。由于应用程序处理的只是数据的逻辑结构，这样物理独立性可以保证，当数据的物理结构改变了，应用程序不用改变。但是，为了保证应用程序能够正确执行，需要修改概念模式／内模式之间的映像。

（2）数据的逻辑独立性。是指用户的应用程序与数据库的逻辑结构是相互独立的。数据的逻辑结构发生变化后，用户程序也可以不修改。但是，为了保证应用程序能够正确执行，需要修改外模式／概念模式之间的映像。

7.2　数据模型

7.2.1　数据模型的基本概念

模型就是对现实世界特征的模拟和抽象，数据模型是对现实世界数据特征的抽象。对于具体的模型人们并不陌生，如航模飞机、地图和建筑设计沙盘等都是具体的模型。最常用的数据模型分为概念数据模型和基本数据模型。

（1）概念数据模型。也称信息模型，是按用户的观点对数据和信息建模，是现实世界到信息世界的第一层抽象，强调其语义表达功能，易于用户理解，是用户和数据库设计人员交流的语言，主要用于数据库设计。这类模型中最著名的是实体联系模型，简称E-R模型。

（2）基本数据模型。它是按计算机系统的观点对数据建模，是现实世界数据特征的抽象，用于DBMS的实现。基本的数据模型有层次模型、网状模型、关系模型和面向对象模型（Object Oriented Model）。

7.2.2　数据模型的三要素

数据库结构的基础是数据模型，是用来描述数据的一组概念和定义。数据模型的三要素是数据结构、数据操作和数据的约束条件。

（1）数据结构。是所研究的对象类型的集合，是对系统静态特性的描述。

（2）数据操作。对数据库中各种对象（型）的实例（值）允许执行的操作的集合，包括操作及操作规则。如操作有检索、插入、删除和修改，操作规则有优先级别等。数据操作是对系统动态特性的描述。

（3）数据的约束条件。是一组完整性规则的集合。也就是说，对于具体的应用数据必须遵循特定的语义约束条件，以保证数据的正确、有效和相容。例如，某单位人事管理中，要求在职的“男”职工的年龄必须大于18岁小于60，工程师的基本工资不能低于1500元，每个职工可担任一个工种，这些要求可以通过建立数据的约束条件来实现。

7.2.3　E-R模型

概念模型是对信息世界建模，所以概念模型能够方便、准确地表示信息世界中的常用概念。概念模型有很多种表示方法，其中最为常用的是P. P. S. Chen于1976年提出的实体-联系方法（Entity Relationship Approach）。该方法用E-R图来描述现实世界的概念模型，称为实体-联系模型（Entity-Relationship Model，E-R模型）。

E-R模型是软件工程设计中的一个重要方法，因为它接近于人的思维方式，容易理解并且与计算机无关，所以用户容易接受，是用户和数据库设计人员交流的语言。但是，E-R模型只能说明实体间的语义联系，还不能进一步地详细说明数据结构。在解决实际应用问题时，通常应该先设计一个E-R模型，然后再把其转换成计算机能接受的数据模型。

1．实体

在E-R模型中，实体用矩形表示，通常矩形框内写明实体名。实体是现实世界中可以区别于其他对象的“事件”或“物体”。例如，企业中的每个人都是一个实体。每个实体由一组特性（属性）来表示，其中的某一部分属性可以唯一标识实体，如职工号。实体集是具有相同属性的实体集合，例如，学校所有教师具有相同的属性，因此教师的集合可以定义为一个实体集；学生具有相同的属性，因此学生的集合可以定义为另一个实体集。

2．联系

在E-R模型中，联系用菱形表示，通常菱形框内写明联系名，并用无向边分别与有关实体连接起来，同时在无向边旁标注上联系的类型（1∶1、1∶n或m∶n）。实体的联系分为实体内部的联系和实体与实体之间的联系。实体内部的联系反映数据在同一记录内部各字段间的联系。

1）两个不同实体之间的联系

两个不同实体集之间存在如下三种联系类型。

●　一对一（1∶1）。指实体集E_1
中的一个实体最多只与实体集E_2
中的一个实体相联系。

●　一对多（1∶n）。表示实体集E_1
中的一个实体可与实体集E_2
中的多个实体相联系。

●　多对多（m∶n）。表示实体集E_1
中的多个实体可与实体集E_2
中的多个实体相联系。

例如，图7-5表示两个不同实体集之间的联系。其中：

（1）电影院里一个座位只能坐一个观众，因此观众与座位之间是一个1∶1的联系，联系名为V_S，用E-R图表示如图7-5（a）所示。

（2）部门DEPT和职工EMP实体集，若一个职工只能属于一个部门，那么这两个实体集之间应是一个1∶n的联系，联系名为D_E，用E-R图表示如图7-5（b）所示。

（3）工程项目PROJ和职工EMP实体集，若一个职工可以参加多个项目，一个项目可以有多个职工参加，那么这两个实体集之间应是一个m∶n的联系，联系名为PR_E，用E-R图表示如图7-5（c）所示。

图7-5　两个不同实体集之间的联系

2）两个以上不同实体集之间的联系

两个以上不同实体集之间存在1∶1∶1、1∶1∶n、1∶m∶n和r∶m∶n的联系。例如，图7-6表示了三个不同实体集之间的联系。其中：

（1）图7-6（a）表示供应商Supp、项目Proj和零件Part之间多对多对多（r∶n∶m）的联系，联系名为SP_P。表示供应商为多个项目供应多种零件，每个项目可用多个供应商供应的零件，每种零件可由不同的供应商供应的语义。

（2）图7-6（b）表示病房、病人和医生之间一对多对多（1∶n∶m）的联系，联系名为P_D。表示一个特护病房有多个病人和多个医生，一个医生只负责一个病房，一个病人只属于一个病房的语义。

注意，三个实体集之间的多对多联系和三个实体集两两之间的多对多联系的语义是不同的。例如，供应商和项目实体集之间的“合同”联系，表示供应商为哪几个工程签了合同。供应商与零件两个实体集之间的“库存”联系，表示供应商库存零件的数量。项目与零件两个实体集之间的“组成”联系，表示一个项目由哪几种零件组成。

图7-6　三个不同实体集之间的联系

3）同一实体集内的二元联系

同一实体集内的各实体之间也存在1∶1、1∶n和m∶n的联系，如图7-7所示。

图7-7　同一实体集之间的1∶n和1∶1联系

从图中可见，职工实体集中的领导与被领导联系是1∶n的。但是，职工实体集中的婚姻联系是1∶1的。

3．属性

属性是实体某方面的特性。例如，职工实体集具有职工号、姓名、年龄、参加工作时间和通信地址等属性。每个属性都有其取值范围，如职工号为0001～9999的4位整型数，姓名为10位的字符串，年龄的取值范围为18～60等。在同一实体集中，每个实体的属性及其域是相同的，但可能取不同的值。E-R模型中的属性有如下分类。

（1）简单属性和复合属性。简单属性是原子的、不可再分的，复合属性可以细分为更小的部分（即划分为别的属性）。有时用户希望访问整个属性，有时希望访问属性的某个成分，那么在模式设计时可采用复合属性。例如，职工实体集的通信地址可以进一步分为邮编、省、市、街道。若不特别声明，通常指的是简单属性。

（2）单值属性和多值属性。前面所举的例子中，定义的属性对于一个特定的实体都只有单独的一个值。例如，对于一个特定的职工，只对应一个职工号、职工姓名，这样的属性叫做单值属性。但是，在某些特定情况下，一个属性可能对应一组值。例如，职工可能有0个、1个或多个亲属，那么职工的亲属的姓名可能有多个数目，这样的属性称为多值属性。

（3）NULL属性。当实体在某个属性上没有值或属性值未知时，使用NULL值。表示无意义或不知道。

（4）派生属性。派生属性可以从其他属性得来。例如，职工实体集中有“参加工作时间”和“工作年限”属性，那么“工作年限”的值可以由当前时间和参加工作时间得到。这里，“工作年限”就是一个派生属性。

4．E-R方法

概念模型中最常用的方法为实体-联系方法，简称E-R方法。该方法直接从现实世界中抽象出实体和实体间的联系，然后用非常直观的E-R图来表示数据模型。在E-R图中有表7-1所示的几个主要构件。

表7-1　E-R图中的主要构件

说明1：在E-R图中，实体集中作为主码的一部分属性以下划线标明。另外，在实体集与联系的线段上标上联系的类型。

说明2：在本书中，若不引起误解，实体集有时简称实体，联系集有时简称联系。

【例7.1】学校有若干个系，每个系有若干名教师和学生；每个教师可以担任若干门课程，并参加多项项目；每个学生可以同时选修多门课程。请设计该学校教学管理的E-R模型，要求给出每个实体、联系的属性。

解：该学校教学管理的E-R模型应该有5个实体：系、教师、学生、项目和课程。

（1）设计各实体属性如下：

系（系号，系名，主任名）

教师（教师号，教师名，职称）

学生（学号，姓名，年龄，性别）

项目（项目号，名称，负责人）

课程（课程号，课程名，学分）

（2）各实体之间的联系如下：

教师担任课程的1∶n“任课”联系；教师参加项目的n∶m“参加”联系；学生选修课程的n∶m“选修”联系；教师、学生与系之间所属关系的1∶n∶m“领导”联系。其中“参加”联系有一个排名属性，“选修”联系有一个成绩属性。

通过上述分析，该学校教学管理的E-R模型如图7-8所示。

图7-8　学校教学管理的E-R模型

特别需要指出的是，E-R模型强调的是语义，与现实世界的问题密切相关。这句话的意思是，尽管都是学校教学管理，但由于不同的学校教学管理的方法可能会有不同的语义，因此会得到不同的E-R模型。

5．扩充的E-R模型

尽管基本的E-R模型足对大多数数据库特征建模，但数据库某些情况下的特殊语义，仅用基本E-R模型无法表达清楚。在这一节中，将讨论扩充的E-R模型，包括弱实体、特殊化、概括和聚集等概念。

1）弱实体

在现实世界中有一种特殊的联系，这种联系代表实体间的所有（ownership）关系，例如职工与家属的联系，家属总是属于某职工的。这种实体对于另一些实体具有很强的依赖关系，即一个实体的存在必须以另一个实体为前提，将这类实体称为弱实体。

在扩展的E-R图中，弱实体用双线矩形框表示。图7-9为职工与家属的E-R图。

图7-9　弱实体与依赖联系

2）特殊化

前面已经介绍了，实体集是具有相同属性的实体集合。但在现实世界中，某些实体一方面具有一些共性，另一方面还具有各自的特殊性。这样，一个实体集可以按照某些特征区分为几个子实体。例如，学生实体集可以分为研究生、本科生和大专生等子集。将这种普遍到特殊的过程叫做“特殊化”。

将几个具有共同特性的实体集概括成一个更普遍的实体集的过程叫做“普遍化”。例如，可以将大专生、本科生和研究生概括为学生；还可以将学生、教师和职工概括为人。这就是从特殊到一般的过程。

设有实体集E，如果S是E的某些真子集的集合，记为S＝｛Si
｜Si
⊂E，i＝1，2，…，n｝，则称S是E的一个特殊化，E是S_1
、S_2
、…、Sn
的超类，S_1
、S_2
、…、Sn
称为E的子类。

如果
则称S是E的全特殊化，否则是E的部分特殊化。

如果Si
∩Sj
＝Φ，i≠j，则S是不相交特殊化，否则是重叠特殊化。

教职工实体集中的某个职工既是在职生又是教师或工人，那么在职生、教师和工人应该是重叠特殊化；而在职生、教师和工人的集合等于教职工，所以是全部特殊化。

在扩充的E-R模型中，子类继承超类的所有属性和联系，但是，子类还有自己特殊的属性和联系。例如，研究生除了学习外，还要参加科研项目。那么，研究生不仅要继承学生的所有属性，还要增加学位类型、导师的属性，并且需要增加与项目的联系。

在扩充的E-R图中，超类-子类关系模型使用特殊化圆圈和连线的一般方式来表示。超类到圆圈有一条连线，连线为双线表示全特殊化，连线为单线表示部分特殊化；双竖边矩形框表示子类；有符号“∪”的线表示特殊化；圆圈中的d表示不相交特殊化；圆圈中的o表示重叠特殊化；超类与圆圈用单线相连，则表示部分特殊化。图7-10给出了一个特殊化应用实例。

图7-10　特殊化应用实例

7.2.4　层次模型

层次模型（Hierarchical Model）采用树型结构表示数据与数据间的联系。在层次模型中，每一个节点表示一个记录类型（实体），记录之间的联系用节点之间的连线表示，并且根节点以外的其他节点有且仅有一个双亲节点。

【例7.2】某商场的部门、员工和商品三个实体的PEP模型如图7-11所示。在该模型中，每个部门有若干个员工，每个部门负责销售的商品有若干种，即该模型还表示部门到员工之间的一对多（1∶n）联系，部门到商品之间的一对多（1∶n）联系。

图7-11　层次模型

图7-11给出的只是PEP模型的“型”，而不是“值”。在数据库中，所谓“型”就是数据库模式，而“值”就是数据库实例。模式是数据库的逻辑设计，而数据库实例是给定时刻数据库中数据的一个快照。图7-12表示销售部的一个实例。该实例表示在某一时刻销售部是由李军负责，销售部下属有4个员工，负责销售的商品有5种。

图7-12　层次模型实例

层次模型不能直接表示多对多的联系。若要表示多对多的联系，可采用如下两种方法。

（1）冗余节点法。两个实体的多对多联系转换为两个一对多联系。

该方法的优点是节点清晰，允许节点改变存储位置。缺点是需要额外的存储空间，有潜在的数据不一致性。

（2）虚拟节点分解法。将冗余节点转换为虚拟节点。虚拟节点是一个指引元，指向所代替的节点。该方法的优点是减少对存储空间的浪费，避免数据不一致性。缺点是改变存储位置可能引起虚拟节点中指针的修改。

层次模型的特点是记录之间的联系通过指针实现，比较简单，查询效率高。

层次模型的缺点是只能表示1∶n的联系，尽管有许多辅助手段实现m∶n的联系，但较复杂不易掌握；由于层次顺序严格和复杂，插入删除操作的限制比较多，导致应用程序编制比较复杂。1968年，美国IBM公司推出的IMS系统（信息管理系统）是典型的层次模型系统，20世纪70年代在商业上得到了广泛的应用。

【例7.3】员工与商品，员工可以销售多种商品，一种商品可以由多个员工销售，所以员工与商品之间是一个多对多的联系，如图7-13（a）所示。采用冗余节点法将其转换为两个一对多的联系，如图7-13（b）所示。采用虚拟节点分解法，将冗余节点转换为虚拟节点，如图7-13（c）所示。

图7-13　员工与商品的多对多联系

7.2.5　网状模型

采用网络结构表示数据与数据间联系的数据模型称为网状模型（Network Model）。在网状模型中，允许一个以上的节点无双亲，一个节点可以有多于一个的双亲。

网状模型（也称DBTG模型）是一个比层次模型更具有普遍性的数据结构，是层次模型的一个特例。网状模型可以直接地描述现实世界，因为去掉了层次模型的两个限制，允许两个节点之间有多种联系（称之为复合联系）。

网状模型中的每个节点表示一个记录类型（实体），每个记录类型可以包含若干个字段（实体的属性），节点间的连线表示记录类型之间一对多的联系。层次模型和网状模型的主要区别如下。

（1）网状模型中子女节点与双亲节点的联系不唯一，因此需要为每个联系命名。

（2）网状模型允许复合链，即两个节点之间有两种以上的联系，如图7-14所示。

图7-14　网状模型举例

需要说明的是，网状模型不能表示记录之间的多对多联系，需要引入联结记录来表示多对多联系。

【例7.4】学生、课程以及他们之间的多对多联系不能直接用网状模型表示。因为一个学生可以选若干门课，而一门课可以被多个学生选。为此，引入选课联结记录，如图7-15（a）所示。这样，学生与选课之间的S-SC是一对多联系，课程与选课之间的C-SC也是一对多联系。图7-15（a）中，Sno、Sname、SD、Sage、Cno、Cname、Pcno和Grade分别表示学号、姓名、系、年龄、课程号、课程名、先修课程号和成绩。学生选课网状数据库的存储示意图如图7-15（b）所示。

通常，网状数据模型没有层次模型那样严格的完整性约束条件，但DBTG在模式DDL中提供了定义DBTG数据库完整性的若干概念和语句，主要有：

（1）支持记录码的概念。码能唯一标识记录的数据项的集合。

（2）保证一个联系中双亲记录和子女记录之间是一对多联系。

（3）以支持双亲记录和子女记录之间的某些约束条件。例如，当插入一条选课记录“010014，100，98”时，只有学生实体中存在学号为“010014”的学生记录，课程实体存在课程号，系统才认为是合法的操作。

图7-1　学生选课网状数据库的存储示意图

网状模型的主要优点是能更为直接地描述现实世界，具有良好的性能，存取效率高。

网状模型的主要缺点是结构复杂。例如，当应用环境不断扩大时，数据库结构就变得很复杂，不利于最终用户掌握。编制应用程序难度比较大。DBTG模型的DDL、DML语言复杂，记录之间的联系是通过存取路径来实现的，因此程序员必须了解系统结构的细节，增加了编写应用程序的负担。

7.2.6　关系模型

关系模型（Relation Model）是目前最常用的数据模型之一。关系数据库系统采用关系模型作为数据的组织方式，在关系模型中用表格结构表达实体集以及实体集之间的联系，其最大特色是描述的一致性。关系模型是由若干个关系模式组成的集合。一个关系模式相当于一个记录型，对应于程序设计语言中类型定义的概念。关系是一个实例，也是一张表，对应于程序设计语言中变量的概念。给定变量的值随时间可能发生变化，类似地，当关系被更新时，关系实例的内容也随时间发生了变化。

【例7.5】教学数据库的4个关系模式如下：

关系模式中有下划线的属性是主码属性。图7-16是教学模型的一个具体实例

图7-16　关系模型的实例

由于关系模型比网状、层次模型更为简单灵活，因此，数据处理领域中，关系数据库的使用已相当普遍。但是，现实世界存在着许多含有更复杂数据结构的实际应用领域，例如CAD数据、图形数据和人工智能研究等，需要有一种数据模型来表达这类信息，这种数据模型就是面向对象的数据模型。

7.2.7　面向对象模型

面向对象数据模型（Object Oriented Model）的核心概念如下。

（1）对象和对象标识（OID）。对象是现实世界中实体的模型化，与记录、元组的概念相似，但远比它们复杂。每一个对象都有一个唯一的标识，称为对象标识。对象标识不等于关系模式中的记录标识，OID是独立于值的，全系统唯一的。

（2）封装（encapsulate）。每一个对象是状态（state）和行为（behavior）的封装。对象的状态是该对象属性的集合，对象的行为是在该对象状态上操作的方法（程序代码）的集合。被封装的状态和行为在对象外部是看不见的，只能通过显式定义的消息传递来访问。

（3）对象的属性（object attribute）。对象的属性描述对象的状态、组成和特性，对象的某个属性可以是单值或值的集合。对象的一个属性值本身在该属性看来也是一个对象。

（4）类和类层次（class and class hierarchy）。

①　类。所有具有相同属性和方法集的对象构成了一个对象类。任何一个对象都是某个对象类的一个实例（instance）。对象类中属性的定义域可以是任何类，包括基本类，如整型、实型和字串等；一般类，包含自身属性和方法类本身。

②　类层次。所有的类组成了一个有根有向无环图，称为类层次（结构）。一个类可以从直接／间接祖先（超类）中继承（inherit）所有的属性和方法，该类称为子类。

（5）继承（inherit）。子类可以从其超类中继承所有属性和方法。类继承可分为单继承（即一个类只能有一个超类）和多重继承（即一个类可以有多个超类）。

【例7.6】对于学生、教师和课程的E-R图如图7-17所示。可以设计成图7-18所示的面向对象模型。

图7-17　学生、教师和课程的E-R图

图7-18　学生、教师和课程的对象模型

这个面向对象模型中有5个类，分别是SC、CT、S、C和T，其中类SC的属性SSno取值为类S中的对象，属性SCno取值为类C中的对象，类CS中的属性CCno取值也为类C的对象，属性CTno值为类T的对象，这就充分表达了图中E-R图的全部语义。

面向对象数据模型比网络、层次、关系数据模型具有更加丰富的表达能力。但正因为面向对象模型的丰富表达能力，模型相对复杂、实现起来较困难。

7.3　关系代数

7.3.1　关系数据库的基本概念

1．属性和域

在现实世界中，要描述一个事物常常取若干特征来表示，这些特征称为属性（attribute）。例如，用学生学号、姓名、性别、系别、年龄和籍贯等属性来描述。每个属性的取值范围所对应一个值的集合，称为该属性的域（domain）。例如，学号的域是6位整型数；姓名的域是10位字符；性别的域为｛男，女｝等。

一般在关系数据模型中，对域还加了一个限制，所有的域都应是原子数据（atomic data）。例如，整数、字符串是原子数据，而集合、记录、数组是非原子数据。关系数据模型的这种限制称为第一范式（first normal form, 1NF）条件。但也有些关系数据模型突破了1NF的限制，称为非1NF的。

2．笛卡儿积与关系

【定义7.1】　设D_1
，D_2
，D_3
，…，Dn
为任意集合，定义D_1
，D_2
，D_3
，…，Dn
的笛卡儿积为

D_1
×D_2
×D_3
…×Dn
＝｛（d_1
，d_2
，d_3
，…，dn
）｜di
∈Di
，i＝1，2，3，…，n｝

其中，每一个元素（d_1
，d_2
，d_3
，…，dn
）叫做一个n元组（n-tuple属性的个数），元组的每一个值di
叫做元组一个分量，若Di
（i＝1，2，3，…，n）为有限集，其基数（Cardinal number元组的个数）为mi
（i＝1，2，3，…，n），则D_1
×D_2
×D_3
…×Dn
的基数M为
笛卡儿积可以用二维表来表示。

【例7.7】　若D_1
＝｛0，1｝，D_2
＝｛a，b｝，D_3
＝｛c，d｝，求D_1
×D_2
×D_3
。

解：根据定义，笛卡儿积中的每一个元素应该是一个三元组，每个分量来自不同的域，因此结果为

用二维表表示如图7-19所示。

图7-19　D_1
×D_2
×D_3
笛卡儿积的二维表表示

【定义7.2】　D_1
×D_2
×D_3
…×Dn
的子集叫做在域D_1
，D_2
，D_3
，…，Dn
上的关系，记为R（D_1
，D_2
，D_3
，…，Dn
），称关系R为n元关系。

定义7.2可以得出一个关系也可以用二维表来表示。关系中属性的个数称为“元数”，元组的个数称为“基数”。关系模型中的术语与一般术语的对应情况可以通过图7-20中的学生关系说明。图中学生关系模式可表示为：学生（S_no
，Sname，SD，Sex）。该学生关系的主码为S_no，属性分别为S_no、Sname、SD和Sex，对属性Sex的域为男、女，等等。而该学生关系的元数为4，基数为6。

图7-20　学生关系与术语的对应情况

3．关系的相关名词

（1）目或度（Degree）。这里的R表示关系的名字，n是关系的目或度。

（2）候选码（Candidate Key）。若关系中的某一属性或属性组的值能唯一地标识一个元组，则称该属性或属性组为候选码。

（3）主码（Primary Key）。若一个关系有多个候选码，则选定其中一个为主码。

（4）主属性（Non-Key attribute）。包含在任何候选码中的诸属性称为主属性。不包含在任何候选码中的属性称为非码属性。

（5）外码（Foreign key）。如果关系模式R中的属性或属性组非该关系的码，但它是其他关系的码，那么该属性集对关系模式R而言是外码。

例如，客户与贷款之间的借贷联系c-1（c-id
，loan-no
），属性c-id是客户关系中的码，所以c-id是外码；属性loan-no是贷款关系中的码，所以loan-no也是外码。

（6）全码（All-key）。关系模型的所有属性组是这个关系模式的候选码，称为全码。

例如，关系模式R（T，C，S），属性T表示教师，属性C表示课程，属性S表示学生。假设一个教师可以讲授多门课程，某门课程可以由多个教师讲授，学生可以听不同教师讲授的不同课程，那么，要想区分关系中的每一个元组，这个关系模式R的码应为全属性T、C和S，即All-key。

4．关系的三种类型

（1）基本关系（通常又称为基本表或基表）。是实际存在的表，它是实际存储数据的逻辑表示。

（2）查询表。查询结果对应的表

（3）视图表。是由基本表或其他视图表导出的表。由于它本身不独立存储在数据库中，数据库中只存放它的定义，所以常称为虚表。

5．关系数据库模式

在数据库中要区分型和值。关系数据库中的型也称为关系数据库模式，是关系数据库结构的描述。它包括若干域的定义以及在这些域上定义的若干关系模式。实际上，关系的概念对应于程序设计语言中变量的概念，而关系模式对应于程序设计语言中类型定义的概念。关系数据库的值是这些关系模式在某一时刻对应的关系的集合，通常称之为关系数据库。

【定义7.3】关系的描述称为关系模式（Relation Schema）。可以形式化地表示为：

其中，R表示关系名；U是组成该关系的属性名集合；D是属性的域；dom是属性向域的映像集合；F为属性间数据的依赖关系集合。

通常将关系模式简记为：

R（U）或R（A_1
，A_2
，A_3
，…，An
）

其中，R为关系名，A_1
，A_2
，A_3
，…，An
为属性名或域名，属性向域的映像常常直接说明属性的类型、长度。通常在关系模式主属性上加下划线表示该属性为主码属性。

例如：学生关系S有学号Sno、学生姓名Same、系名SD、年龄SA属性；课程关系C有课程号Cno、课程名Cname、先修课程号PCno属性；学生选课关系SC有学号Sno、课程号Cno、成绩Grade属性。定义关系模式及主码如下（本题未考虑F属性间数据的依赖，该问题在后续内容讨论）。

（1）学生关系模式S（Sno
，Sname，SD，SA）。

（2）课程关系模式C（Cno
，Cname，PCno）Dom（PCno）＝Cno。这里，Pcno是先行课程号，来自Cno域，但由于Pcno属性名不等于Cno值域名，所以要用Dom来定义。但是，不能将Pcno直接改为Cno，因为在关系模型中，各列属性必须取相异的名字。

（3）学生选课关系模式SC（Sno
，Cno
，Grade）。SC关系中的Sno、Cno又分别为外码。因为它们分别是S、C关系中的主码。

6．完整性约束

完整性规则提供了一种手段来保证当授权用户对数据库作修改时不会破坏数据的一致性。因此，完整性规则防止的是对数据的意外破坏。关系模型的完整性规则是对关系的某种约束条件。关系的完整性共分为三类：实体完整性、参照完整性（也称引用完整性）和用户定义完整性。

（1）实体完整性（Entity Integrity）。规定基本关系R的主属性A不能取空值。

（2）参照完整性（Referential Integrity）。现实世界中的实体之间往往存在某种联系，在关系模型中实体及实体间的联系是用关系来描述的，这样自然就存在着关系与关系间的引用。

例如，员工和部门关系模式表示如下，其中在关系模式主属性上加下划线表示该属性为主码属性。

员工（员工号
，姓名，性别，参加工作时间，部门号）

部门（部门号
，名称，电话，负责人）

这两个关系存在着属性的引用，即员工关系中的“部门号”值必须是确实存在的部门的部门号，即部门关系中有该部门的记录。也就是说，员工关系中的“部门号”属性取值要参照部门关系的“部门号”属性取值。

参照完整性规定，若F是基本关系R的外码，它与基本关系S的主码Ks
相对应（基本关系R和S不一定是不同的关系），则对于R中每个元组在F上的值或者取空值（F的每个属性值均为空值），或者等于S中某个元组的主码值。

（3）用户定义完整性（User defined Integrity）。就是针对某一具体的关系数据库的约束条件，反映某一具体应用所涉及的数据必须满足的语义要求，由应用的环境决定。例如，银行的用户账户规定必须大于等于100000，小于999999。

7．关系运算

关系操作的特点是操作对象和操作结果都是集合，而非关系数据模型的数据操作方式则为一次一个记录的方式。关系数据语言分为三类：关系代数语言、关系演算语言和具有关系代数和关系演算双重特点的语言（如SQL）。关系演算语言包含元组关系演算语言（如Aplha、Quel）和域关系演算语言（如QBE）。

关系代数语言、元组关系演算和域关系演算是抽象查询语言，它与具体的DBMS中实现的实际语言并不一样，但是可以用它评估实际系统中的查询语言能力的标准。

关系代数运算符有4类：集合运算符，专门的关系运算符，算术比较符和逻辑运算符。根据运算符的不同，关系代数运算可分为传统的集合运算和专门的关系运算。传统的集合运算是从关系的水平方向进行的，包括并、交、差及广义笛卡儿积。专门的关系运算既可以从关系的水平方向进行运算，又可以向关系的垂直方向运算，包括选择、投影、连接以及除法，如表7-2所示。表7-2中，并、差、笛卡儿积、投影和选择是5种基本的运算，因为其他运算可以通过基本的运算导出。

表7-2　关系代数运算符

7.3.2　五种基本的关系代数运算

五种基本的关系代数运算包括并、差、笛卡儿积、投影和选择，其他运算可以通过基本的关系运算导出。

1．并（Union）

关系R与S具有相同的关系模式，即R与S的元数相同（结构相同）。关系R与S并由属于R或属于S的元组构成的集合组成，记作R∪S，其形式定义如下：

R∪S＝｛t｜t∈R∨t∈S｝

式中t为元组变量。

2．差（Difference）

关系R与S具有相同的关系模式，关系R与S的差是由属于R但不属于S的元组构成的集合，记作R－S，其形式定义如下：

R－S＝｛t｜t∈R∧t∉S｝

3．广义笛卡儿积（Extended Cartesian Product）

两个元数分别为n目和m目的关系R和S的广义笛卡儿积是一个（n＋m）列的元组的集合。元组的前n列是关系R的一个元组，后m列是关系S的一个元组，记作R×S，其形式定义如下：

R×S＝｛t｜t＝＜tn
，tm
＞∧tn
∈R∧tm
∈S｝

如果R和S中有相同的属性名，可在属性名前加关系名作为限定，以示区别。若R有K_1
个元组，S有K_2
个元组，则R和S的广义笛卡儿积有K_1
×K_2
个元组。

注意：本教材中的＜tn
，tm
＞意为元组tn
和tm
拼接成的一个元组。

4．投影（Projection）

投影运算是从关系的垂直方向进行运算，在关系R中选择出若干属性列A组成新的关系，记作πA
（R），其形式定义如下：

πA
（R）＝｛t［A］｜t∈R｝

5．选择（Selection）

选择运算是从关系的水平方向进行运算，是从关系R中选择满足给定条件的诸元组，记作σF
（R），其形式定义如下：

σF
（R）＝｛t｜t∈R∧F（t）＝True｝

其中，F中的运算对象是属性名（或列的序号）或常数，运算符、算术比较符（＜、≤、＞、≥、≠）和逻辑运算符（∧、∨、﹁）。例如，σ1≥6
（R）表示选取R关系中第1个属性值大于等于第6个属性值的元组；σ1＞′6′
（R）表示选取R关系中第1个属性值大于等于6的元组。

【例7.8】设有关系R、S如图7-21所示，请求出R∪S，R－S，R×S，πA，C
（R），σA＞B
（R）和σ3＜4
（R×S）。

图7-21　关系R、S

解：R∪S，R－S，R×S，πA，C
（R），σA＞B
（R）和σ3＜4
（R×S）的结果如图7-22所示。其中，R×S生成的关系属性名有重复，按照关系“属性不能重名”的性质，通常采用“关系名．属性名”的格式。对于σ3＜4
（R×S）的含义是R×S后“选取第3个属性值小于第4个属性值”的元组。由于R×S的第3个属性为R.C，第4个属性是S.A，因此σ3＜4
（R×S）的含义也是R×S后“选取R.C值小于S.A值”的元组。

图7-22　运算结果

7.3.3　扩展的关系代数运算

扩展的关系代数运算可以从基本的关系运算中导出，主要包括选择、投影、连接、除法、广义笛卡儿积和外连接。

1．交（Intersection）

关系R与S具有相同的关系模式，关系R与S的交是由属于R同时又属于S的元组构成的集合，记作R∩S，其形式定义如下：

显然，R∩S＝R－（R－S），或者R∩S＝S－（S－R）。

2．连接（Join）

连接分为θ连接、等值连接及自然连接三种。连接运算是从两个关系R和S的笛卡儿积中选取满足条件的元组。因此，可以认为笛卡儿积是无条件连接，其他的连接操作认为是有条件连接。下面分述如下。

（1）θ连接。从R与S的笛卡儿积中选取属性间满足一定条件的元组。记作：

其中，XθY为连接的条件，θ是比较运算符，X和Y分别为R和S上度数相等，且可比的属性组。tn
［X］表示R中tn
元组的相应于属性X的一个分量。tm
［Y］表示S中tm
元组的相应于属性Y的一个分量。有如下说明。

①　θ连接也可以表示为：

其中，i＝1，2，3，…，n，j＝1，2，3，…，m，iθj的含义为从两个关系R和S中选取R的第i列和S的第j列之间满足θ运算的元组进行连接。

②　θ连接可以由基本的关系运算笛卡尔积和选取运算导出。因此，θ连接可表示为：

【例7.9】设有关系R、S如图7-19所示，求

解：本题连接的条件为R.A＜S.B，意为将R关系中属性A的值小于S关系中属性B的值的元组取出来作为结果集的元组。结果集为R×S后选出满足条件的元组，并且结果集的属性为R.A，R.B，R.C，S.A，S.B，S.C。结果如图7-23所示。

图　7-23

（2）等值连接。当θ为“＝”时，称之为等值连接，记为
其形式定义如下：

（3）自然连接。是一种特殊的等值连接，它要求两个关系中进行比较的分量必须是相同的属性组，并且在结果集中将重复属性列去掉。若tn
表示R关系的元组变量，tm
表示S关系的元组变量；R和S具有相同的属性组B，且B＝（B_1
，B_2
，…，BK
）；并假定R关系的属性为A_1
，A_2
，…，An－k
，B_1
，B_2
，…，BK
，S关系的属性为B_1
，B_2
，…，BK
，BK＋1
，BK＋2
，…，Bm
；为S的元组变量tm
去掉重复属性B所组成的新的元组变量为tm
。自然连接可以记为R
S，其形式定义如下：

R
S＝｛t｜t＝＜tn
，tm
＞∧tn
∈R∧tm
∈S∧R.B_1
＝S.B_1
∧R.B_2
＝S.B_2
∧…∧R.Bn
＝S.Bn
｝

自然连接可以由基本的关系运算笛卡尔积和选取运算导出，因此自然连接可表示为：

特别需要说明的是，一般连接是从关系的水平方向运算，而自然连接不仅要从关系的水平方向，而且要从关系的垂直方向运算。因为自然连接要去掉重复属性，如果没有重复属性，那么自然连接就转化为笛卡儿积。

【例7.10】设有关系R、S如图7-24所示，求R
S。

图7-24　关系R、S

解：本题要求R与S关系的自然连接，自然连接是一种特殊的等值连接，它要求两个关系中进行比较的分量必须是相同的属性组，并且在结果中将重复属性列去掉。本题R与S关系中相同的属性组为AC，因此，结果集中的属性列应为ABCD。其结果如图7-25所示。

图7-25　R
S

3．除（Division）

除运算是同时从关系的水平方向和垂直方向进行运算。给定关系R（X，Y）和S（Y，Z），X、Y、Z为属性组。R÷S应当满足元组在X上的分量值x的象集Yx
包含关系S在属性组Y上投影的集合。其形式定义如下：

其中，Yx
为x在R中的象集，x＝tn
［X］。且R÷S的结果集的属性组为X。

【例7.11】设有关系R、S如图7-26所示，求R÷S。

图7-26　R÷S

解：根据除法定义，此题的X为属性AB，Y为属性CD。R÷S应当满足元组在属性AB上的分量值x的象集Yx
包含关系S在CD上投影的集合。

关系S在Y上的投影为πCD
（S）＝｛（c，d），（e，f）｝。对于关系R，属性组X（即AB）可以取三个值｛（a，b），（b，d），（c，k）｝，它们的象集分别为：

象集CD（a，b）
＝｛（c，d），（e，f），（h，k）｝

象集CD（b，d）
＝｛（e，f），（d，l）｝

象集CD（c，k）
＝｛（c，d），（e，f）｝

由于上述象集包含πCD
（S）有（a，b）和（c，k），因此R÷S＝｛（a，b），（c，k）｝，结果如图7-26（c）所示。

【例7.12】设学生课程数据库中有学生S、课程C和学生选课SC三个关系，如图7-27所示。请用关系代数表达式表达如下检索问题。

（1）检索选修课程名为“数学”的学生号和学生姓名。

（2）检索至少选修了课程号为1和3的学生号。

（3）检索选修了“操作系统”或“数据库”课程的学号和成绩。

（4）检索年龄在18～20之间（含18和20）的女生的学号、姓名及年龄。

（5）检索选修了“对平”老师所讲课程的学生的学号、姓名及成绩。

（6）检索选修全部课程的学生姓名。

（7）检索选修课程包括1042学生所学课程的学生学号。

（8）检索不选修2课程的学生姓名和所在系。

图7-27　S、C、SC关系

解：（1）检索选修课程名为“数学”的学生号和学生姓名的关系代数表达式如下：

对于上述表达式
自然连接后重复的属性列为学号Sno和课程号Cno，去掉重复属性列的结果如图7-28所示。从图中可见，满足课程名为“数学”的只有三个元组，对Sno和Sname投影的结果如图7-29所示。由于Sno、Cno和Cname分别对应第1，2和8列属性，所以上述表达式还可以写为

图7-28　S
SC
C

（2）检索至少选修了课程号为1和3的学生号可有如下两种解题思路。

①　关系代数表达式为
若设SC×SC中的第一个SC关系为S1，与第二个SC关系为S2，那么，该关系表达式的含义为先从SC×SC中选取满足条件S1.Sno＝S2.Sno、S1.Cno＝′1′、S2.Sno＝′3′的元组，最后投影第一个属性列Sno即为所求结果集。

②　关系代数表达式为
分析如下。

表达式
就是构造一个临时关系K，其属性为Cno，结果如下：

图7-29　例7.12的投影结果

查询表达式
的结果集的学生号所选的课程号应包括K。

求解的过程就是对
的每一个元组逐一求某一学生的象集。因为所求的Sno，所以X为Sno，Y为Cno，所以象集为
将Sno的值逐一代入求象集：

可以看出，只有3001包含了K在Cno的投影，所以，

（3）检索选修了“操作系统”或“数据库”课程的学号和姓名的关系代数表达式为：

（4）检索年龄在18～20之间（含18和20）的女生的学号、姓名及年龄的关系代数表达式为：

（5）检索选修了“数据库”课程的学生的学号、姓名及成绩的关系代数表达式为：

（6）检索选修全部课程的学生姓名及所在系的关系代数表达式为：

对于本题给出的具体关系，求解过程分析如下。

①　表示全部课程的临时关系K＝πCno
（C）＝｛1，2，3，4，5，6，7｝。

②　查询选修了所有课程的学生号为πsno，Cno
（SC）÷K＝｛
｝，因为学生所选课程分别为Cno_3001
＝｛1，2，3｝，Cno_3002
＝｛2，3｝和Cno_1042
＝｛1，2｝，所以3001，3002和1042都没有包含K，故结果集为空。

③　与S关系进行自然连接在对学生姓名Sname和学号SD投影的结果也为空。

（7）检索选修课程包含1042学生所学课程的学生学号的关系表达式如下：

（8）检索不选修2课程的学生姓名和所在系的关系代数表达式为：

对于上式也可以用属性列号替换属性名，将上式写成如下等价的关系代数表达式：

4．广义投影（generalized projection）

广义投影运算允许在投影列表中使用算术运算，实现了对投影运算的扩充。

若有关系R，条件F_1
，F_2
，…，Fn
中的每一个都是涉及R中常量和属性的算术表达式，那么广义投影运算的形式定义如下：

【例7.13】信贷额度关系模式credit-in（C_name，limit，Credit_balance），属性分别表示用户姓名、信贷额度和到目前为止的花费。图7-30（a）表示了关系credit-in的一个具体。若要查询给出每个用户还能花费多少，可以用关系代数表达式
来表示，查询结果如图7-30（b）所示。

图7-30　信贷额度关系

5．外连接（outer jion）

外连接运算是连接运算的扩展，可以处理由于连接运算而缺失的信息。对于图7-27的S和SC关系，当对其进行自然连接S
SC时，其结果如图7-31所示。

图7-31　S
C

从图7-31可以看出，S与SC的自然连接
的结果丢失了黎明、对明远、赵国庆的相关信息。可是，使用外连接可以避免这样的信息丢失。外连接运算有三种：左外连接、右外连接和全外连接。

（1）左外连接
取出左侧关系中所有与右侧关系中任一元组都不匹配的元组，用空值null充填所有来自右侧关系的属性，构成新的元组，将其加入自然连接的结果中。对于图7-27的S和SC关系，当对其进行左外连接
时，其结果如图7-32所示。

图7-32

（2）右外连接
取出右侧关系中所有与左侧关系中任一元组都不匹配的元组，用空值null填充所有来自左侧关系的属性，构成新的元组，将其加入自然连接的结果中。对于图7-27的S和SC关系，当对其进行右外连接
时，其结果如图7-33所示。

图7-33

（3）全外连接
完成左外连接和右外连接的操作。即填充左侧关系中所有与右侧关系中任一元组都不匹配的元组，又填充右侧关系中所有与左侧关系中任一元组都不匹配的元组，将产生的新元组加入自然连接的结果中。

【例7.14】设有关系R、S如图7-34所示，求

图7-34　关系R、S

对于图7-34的R、S关系，当对其进行左外连接
右外连接
全外联接
时，其结果分别如图7-35（a）、图7-35（b）和图7-35（c）所示。

图7-35

7.4　关系数据库SQL语言简介

SQL（Structured Query Language）早已确立起自己作为关系数据库标准语言的地位，已被众多商用DBMS产品如DB2、ORACLE、INGRES、SYSBASE、SQL Server和VFP等所采用，使得它已成为关系数据库领域中一个主流语言。SQL是1974年由Boyce和Chamberlin提出的，是在关系数据库中最普遍使用的语言，包括数据查询（query）、数据操纵（manipulation）、数据定义（definition）和数据控制（control）功能，是一种通用的、功能强大的关系数据库的标准语言。

7.4.1　SQL数据库体系结构

SQL主要有三个标准：ANSI（美国国家标准机构）SQL；对ANSI SQL进行修改后在1992年采用的标准SQL-92或SQL2；最近的SQL-99标准，也称为SQL3标准。SQL-99从SQL-92扩充而来，并增加了对象关系特征和许多其他新的功能。

1．SQL的特点

（1）综合统一。非关系模型的数据语言分为模式定义语言和数据操纵语言，其缺点是当要修改模式时，必须停止现有数据库的运行，转储数据，修改模式编译后再重装数据库。SQL是集数据定义、数据操纵和数据控制功能于一体，语言风格统一，可独立完成数据库生命周期的所有活动。

（2）高度非过程化。非关系数据模型的数据操纵语言是面向过程的，若要完成某项请求时，必须指定存储路径；而SQL语言是高度非过程化语言，当进行数据操作时，只要指出“做什么”，无须指出“怎么做”，存储路径对用户来说是透明的，提高了数据的独立性。

（3）面向集合的操作方式。非关系数据模型采用的是面向记录的操作方式，操作对象是一条记录。而SQL语言采用面向集合的操作方式，其操作对象、查找结果可以是元组的集合。

（4）两种使用方式。第一种方式，用户可以在终端键盘上输入SQL命令，对数据库进行操作，故称之为自含式语言；第二种方式，将SQL语言嵌入到高级语言程序中，所以又称为嵌入式语言。

（5）语言简洁、易学易用。SQL语言功能极强，完成核心功能只用了9个动词，包括如下4类。

●　数据查询：SELECT。

●　数据定义：CREATE、DROP、ALTER。

●　数据操纵：INSERT、UODATE、DELETE。

●　数据控制：GRANT、REVORK。

2．SQL支持三级模式结构

SQL语言支持关系数据库的三级模式结构，其中，视图对应外模式、基本表对应模式、存储文件对应内模式。具体结构如图7-36所示。

7.4.2　SQL的基本组成

SQL由以下几个部分组成。

（1）数据定义语言。SQL DDL提供定义关系模式和视图、删除关系和视图、修改关系模式的命令。

（2）交互式数据操纵语言。SQL DML提供查询、插入、删除和修改的命令。

（3）事务控制（transaction control）。SQL提供定义事务开始和结束的命令。

图7-36　关系数据库的三级模式结构

（4）嵌入式SQL和动态SQL（Embeded SQL and Dynamic SQL）。用于嵌入到某种通用的高级语言（C、C++、Java、PL/I、COBOL和VB等）中混合编程。其中，SQL负责操纵数据库，高级语言负责控制程序流程。

（5）完整性（integrity）。SQL DDL包括定义数据库中的数据必须满足的完整性约束条件的命令，对于破坏完整性约束条件的更新将被禁止。

（6）权限管理（authorization）。SQL DDL中包括说明对关系和视图的访问权限。

7.4.3　SQL数据定义

基本表和视图都是表，所不同的是基本表是实际存储在数据库中的表，视图是虚表，是从基本表或其他视图中导出的表。数据库中只存放视图的定义，而不存放视图的数据。用户可用SQL语句对基本表和视图进行查询等操作，在用户看来，基本表和视图一样，都是关系（即表）。一个基本表可以存储在一个或多个存储文件中，一个存储文件也可存储一个或多个基本表。一个表可以带若干索引，索引也存储在存储文件中。每个存储文件就是外部存储器上一个物理文件，存储文件的逻辑结构组成了关系数据库的内模式。

SQL的数据定义包括对表、视图、索引的创建和删除。

1．创建表（CREATE TABLE）

语句格式：CREATE TABLE＜表名＞（＜列名＞＜数据类型＞［列级完整性约束条件］

［，＜列名＞＜数据类型＞［列级完整性约束条件］］…

［，＜表级完整性约束条件＞］）；

列级完整性约束条件有NULL（空）和UNIQUE（取值唯一），如NOT NULL UNIQUE表示取值唯一，不能取空值。

【例7.15】建立一个供应商、零件数据库。其中“供应商”表S（Sno，Sname，Status，City）分别表示供应商代码、供应商名、供应商状态和供应商所在城市。“零件”表P（Pno，Pname，Color，Weight，City）表示零件号、零件名、颜色、重量及产地。其中，数据库要满足如下要求。

（1）供应商代码不能为空，且值是唯一的，供应商的名也是唯一的。

（2）零件号不能为空，且值是唯一的；零件名不能为空。

（3）一个供应商可以供应多个零件，而一个零件可以由多个供应商供应。

分析：根据题意，供应商和零件分别要建立一个关系模式。供应商和零件之间是一个多对多的联系，在关系数据库中，多对多联系必须生成一个关系模式，而该模式的码是该联系两端实体的码加上联系的属性构成的，若该联系名为SP，那么关系模式为SP（Sno，Pno，Qty），其中，Qty表示零件的数量。

根据上述分析，用SQL建立一个供应商、零件数据库如下：

从上述定义可以看出，Sno CHAR（5）NOT NULL UNIQUE语句定义了Sno的列级完整性约束条件，取值唯一，不能取空值。

需要说明如下。

（1）PRIMARY KEY （Sno）　已经定义了Sno为主码，所以Sno CHAR（5） NOT NULL UNIQUE语句中的NOT NULL UNIQUE可以省略。

（2）FOREIGN KEY （Sno） REFERENCESS （Sno）定义了在SP关系中Sno为外码，其取值必须来自S关系的Sno域。同理，在SP关系中Pno也定义为外码。

2．修改和删除表

1）修改表（ALTER TABLE）

语句格式：ALTER TABLE＜表名＞［ADD＜新列名＞＜数据类型＞［完整性约束条件］］

［DROP＜完整性约束名＞］

［MODIFY＜列名＞＜数据类型＞］；

例如，向“供应商”表S增加Zap“邮政编码”可用如下语句：

注意，不论基本表中原来是否已有数据，新增加的列一律为空。

又如，将Status字段改为整型可用如下信息：

2）删除表（DROP TABLE）

语句格式：DROP TABLE＜表名＞

例如，执行DROP TABLE Student，此后关系Student不再是数据库模式的一部分，关系中的元组也无法访问。

3．索引建立与删除

数据库中的索引与书籍中的索引类似，在一本书中，利用索引可以快速查找所需信息，无须阅读整本书。在数据库中，索引使数据库程序无须对整个表进行扫描，就可以在其中找到所需数据。书中的索引是一个词语列表，其中注明了包含各个词的页码。而数据库中的索引是某个表中一列或者若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作用如下。

（1）通过创建唯一索引，可以保证数据记录的唯一性。

（2）可以大大加快数据检索速度。

（3）可以加速表与表之间的连接，这一点在实现数据的参照完整性方面有特别的意义。

（4）在使用ORDER BY和GROUP BY子句中进行检索数据时，可以显著减少查询中分组和排序的时间。

（5）使用索引可以在检索数据的过程中使用优化隐藏器，提高系统性能。

索引分为聚集索引和非聚集索引。聚集索引是指索引表中索引项的顺序与表中记录的物理顺序一致的索引。

1）建立索引

语句格式：CREATE[UNIQUE][CLUSTER]INDEX＜索引名＞

ON＜表名＞（＜列名＞［＜次序＞］［，＜列名＞［＜次序＞］］…）；

参数说明如下。

●　次序：可选ASC（升序）或DSC（降序），默认值为ASC。

●　UNIQUE：表明此索引的每一个索引值只对应唯一的数据记录。

●　CLUSTER：表明要建立的索引是聚簇索引，意为索引项的顺序是与表中记录的物理顺序一致的索引组织。

【例7.16】假设供应销售数据库中有供应商S、零件P、工程项目J、供销情况SPJ关系，希望建立4个索引。其中，供应商S中Sno按升序建立索引；零件P中Pno按升序建立索引；工程项目J中Jno按升序建立索引；供销情况SPJ中Sno按升序，Pno按降序，Jno按升序建立索引。

解：根据题意建立的索引如下。

2）删除索引

语句格式：DROP INDEX＜索引名＞

例如，执行DROP INDEX StudentIndex，此后索引StudentIndex不再是数据库模式的一部分。

4．视图创建与删除

视图是从一个或者多个基本表或视图中导出的表，其结构和数据是建立在对表的查询基础上的。和真实的表一样，视图也包括几个被定义的数据列和多个数据行，但从本质上讲，这些数据列和数据行来源于其所引用的表。因此，视图不是真实存在的基本表，而是一个虚拟表，视图所对应的数据并不实际地以视图结构存储在数据库中，而是存储在视图所引用的表中。使用视图的优点和作用如下。

（1）可以使视图集中数据、简化和定制不同用户对数据库的不同数据要求。

（2）使用视图可以屏蔽数据的复杂性，用户不必了解数据库的结构，就可以方便地使用和管理数据，简化数据权限管理和重新组织数据以便输出到其他应用程序中。

（3）视图可以使用户只关心他感兴趣的某些特定数据和他们所负责的特定任务，而那些不需要的或者无用的数据则不在视图中显示。

（4）视图大大地简化了用户对数据的操作。

（5）视图可以让不同的用户以不同的方式看到不同或者相同的数据集。

（6）在某些情况下，由于表中数据量太大，因此在表的设计时常将表进行水平或者垂直分割，但表的结构的变化对应用程序产生不良的影响。

（7）视图提供了一个简单而有效的安全机制。

1）视图的创建

语句格式：CREATE VIEW　视图名（列表名）

AS SELECT　查询子句

[WITH CHECK OPTION]；

注意：视图的创建中，必须遵循如下规定。

（1）子查询可以是任意复杂的SELECT语句，但通常不允许含有order by子句和DISTINCT短语。

（2）WITH CHECK OPTION表示对UPDATE，INSTER，DELETE操作时保证更新、插入或删除的行满足视图定义中的谓词条件（即子查询中的条件表达式）。

（3）组成视图的属性列名或者全部省略或者全部指定。如果省略属性列名，则隐含该视图由SELECT子查询目标列的主属性组成。

【例7.17】若学生关系模式为Student（Sno，Sname，Sage，Sex，SD，Email，Tel），建立“计算机系”（CS表示计算机系）学生的视图，并要求进行修改、插入操作时保证该视图只有计算机系的学生。

由于CS-STUDENT视图使用了WITH CHECK OPTION子句，因此，对该视图进行修改、插入操作时DBMS会自动加上SD＝′CS′的条件，保证该视图只有计算机系的学生。

2）视图的删除

语句格式：DROP VIEW视图名

例如，DROP VIEW CS-STUDENT将删除视图CS-STUDENT。

7.4.4　SQL数据查询

SQL的数据操纵功能包括SELECT（查询）、INSERT（插入）、DELETE（删除）和UPDATE（修改）这4条语句。SQL语言对数据库的操作十分灵活方便，原因在于SELECT语句中成分丰富多样的元组，有许多可选形式，尤其是目标列和条件表达式。

本小节数据查询以图7-27所示学生选课数据库为例。

1．Select基本结构

数据库查询是数据库的核心操作，SQL语言提供了SELECT语句进行数据库的查询。

语句格式：SELECT[ALL｜DISTINCT]＜目标列表达式＞［，＜目标列表达式＞］…

SQL查询中的子句顺序为SELECT、FROM、WHERE、GROUP BY、HAVING和ORDER BY。其中，SELECT、FROM是必须的，HAVING条件子句只能与GROUP BY搭配起来使用。

（1）SELECT子句对应的是关系代数中的投影运算，用来列出查询结果中的属性。其输出可以是列名、表达式、集函数（AVG、COUNT、MAX、MIN、SUM），DISTINCT选项可以保证查询的结果集中不存在重复元组。

（2）FROM子句对应的是关系代数中的笛卡儿积，它列出的是表达式求值过程中需扫描的关系，即在FROM子句中出现多个基本表或视图时，系统首先执行笛卡儿积操作。

（3）WHERE子句对应的是关系代数中的选择谓词。WHERE子句的条件表达式中可以使用的运算符如表7-3所示。

一个典型的SQL查询具有如下形式：

对应关系代数表达式为∏A_1
，A_2
，…，An

（σp
（r_1
×r_2
×…×rm
））。

表7-3　WHERE子句的条件表达式中可以使用的运算符

2．简单查询

SQL最简单的查询是找出关系中满足特定条件的元组，这些查询与关系代数中的选择操作类似。简单查询只需要使用三个保留字SELECT、FROM和WHERE。

【例7.18】查询学生-课程数据库中计算机系CS学生的学号、姓名及年龄。

注意，通常为了便于理解查询语句的结构，在写SQL语句时要将保留字如FORM或WHERE作为每一行的开头。但是，如果一个查询或子查询非常短的时候，可以直接将它们写在一行上，这种风格使得查询语句很紧凑，也具有很好的可读性。如上例也可写成如下形式：

【例7.19】查询学生的出生年份。

3．连接查询

若查询涉及两个以上的表，则称为连接查询。

【例7.20】检索选修了课程号为“C1”的学生号和学生姓名，可用连接查询和嵌套查询实现，实现方法如下：

【例7.21】检索选修课程名为“MS”的学生号和学生姓名，可用连接查询和嵌套查询实现，实现方法如下：

【例7.22】检索至少选修了课程号为“C1”和“C3”的学生号，实现方法如下：

4．子查询与聚集函数

1）子查询

子查询也称嵌套查询。嵌套查询是指一个SELECT-FROM-WHERE查询块可以嵌入另一个查询块之中。在SQL中允许多重嵌套。

【例7.23】例7-21可以采用嵌套查询来实现。

2）聚集函数

聚集函数是一个值的集合为输入，返回单个值的函数。SQL提供了5个预定义集函数：平均值avg、最小值min、最大值max、求和sum以及计数count。如表7-4所示。

使用ANY和ALL谓词必须同时使用比较运算符，其含义及等价的转换关系如表7-5所示。用集函数实现子查询通常比直接用ALL或ANY查询效率要高。

表7-4　集函数的功能

表7-5　ANY、ALL谓词含义及等价的转换关系

【例7.24】查询课程C1的最高分和最低分以及高低分之间的差距。

【例7.25】查询其他系比计算机系CS所有学生年龄都要小的学生姓名及年龄。

方法1：（用ALL谓词）

方法2：（用MIN集函数）从等价的转换关系表7-4中可见，＜ALL可用＜MIN代换。

说明：方法2实际上是找出计算机系年龄最小的学生的年龄，只要其他系的学生年龄比这个年龄小，那么就应在结果集中。

【例7.26】查询其他系比计算机系某一学生年龄小的学生姓名及年龄。

方法1：（用ANY谓词）

方法2：（用MAX集函数）从等价的转换关系表7-5中可见，＜ANY可用＜MAX代换。

说明：方法2实际上是找出计算机系年龄最大的学生的年龄，只要其他系的学生年龄比这个年龄大，那么就应在结果集中。

5．分组查询

1）GROUP BY子句

在WHERE子句后面加上GROUP BY子句可以对元组进行分组，保留字GROUP BY后面跟着一个分组属性列表。最简单的情况是FROM子句后面只有一个关系，根据分组属性对它的元组进行分组。SELECT子句中使用的聚集操作符仅用在每个分组上。

【例7.27】学生数据库中的SC关系，查询每个学生的平均成绩。

该语句是将SC关系的元组重新组织，并进行分组使得不同学号的元组分别被组织在一起，求出各个学生的平均值输出。

2）HAVING子句

假如元组在分组前按照某种方式加上限制，使得不需要的分组为空，可以在GROUP BY子句后面跟一个HAVING子句即可。

注意：当元组含有空值时，应该记住以下两点。

（1）空值在任何聚集操作中被忽视。它对求和、求平均值和计数都没有影响。它也不能是某列的最大值或最小值。例如，COUNT（*）是某个关系中所有元组数目之和，但COUNT（A）却是A属性非空的元组个数之和。

（2）NULL值又可以在分组属性中看作是一个一般的值。例如，SELECT A, AVG（B）FORM R中，当A的属性值为空时，就会统计A＝NULL的所有元组中B的均值。

【例7.28】供应商数据库中的S、P、J、SPJ关系，查询某工程至少用了三家供应商（包含三家）供应的零件的平均数量，并按工程号的降序排列。

根据题意“某工程至少用了三家供应商（包含三家）供应的零件”，应该按照工程号分组，而且应该加上条件供应商的数目。但是需要注意的是，一个工程项目可能用了同一个供应商的多种零件，因此，在统计供应商数的时候需要加上DISTINCT，以避免重复统计导致错误的结果。例如，按工程号Jno＝'J1'分组，其结果如表7-6所示。如果不加DISTINCT，统计的结果数为7；而加了DISTINCT，统计的结果数为5。

表7-6　按工程号Jno＝'J1'分组

6．更名运算

SQL提供可为关系和属性重新命名的机制，这是通过使用具有如下形式的as子句来实现的：

As子句即可出现在select子句，也可出现在from子句中。

【例7.29】查询计算机系学生的Sname和Age，但Sname用“姓名”表示，Age用“年龄”表示。其语句如下：

SQL中的元组变量必须和特定的关系相联系。元组变量是通过from子句中使用As子句来定义的。通过例7.29来说明。

【例7.30】查询计算机系选修了C1课程的学生姓名Sname和成绩Grade。其语句如下：

元组变量在比较同一关系的两个元组时非常有用。

7．字符串操作

对于字符串进行的最通常的操作是使用操作符like的模式匹配。使用两个特殊的字符来描述模式：“％”匹配任意字符串；“_”匹配任意一个字符。模式是大小写敏感的。例如：

Marry%匹配任何以Marry开头的字符串；%idge%匹配任何包含idge的字符串，例如Marryidge、Rock Ridge、Mianus Bridge和Ridgeway。

“＿”匹配只含两个字符的字符串；“＿%”匹配至少包含两个字符的字符串。

【例7.31】学生关系模式为（Sno，Sname，Sex，SD，Age，Add），其中，Sno为学号，Sname为姓名，Sex为性别，SD为所在系，Age为年龄，Add为家庭住址。请查询：

（1）家庭住址包含“科技路”的学生姓名。

（2）名字为“晓军”的学生姓名、年龄和所在系。

解：（1）家庭住址包含“科技路”的学生姓名的SQL语句如下：

（2）名字为“晓军”的学生姓名、年龄和所在系的SQL语句如下：

为了使模式中包含特殊模式字符（即%和_），在SQL中允许使用escape关键词来定义转义符。转义字符紧靠着特殊字符，并放在它的前面，表示该特殊字符被当成普通字符。例如，在like比较中使用escape关键词来定义转义符，例如使用反斜杠“\”作为转义符。

Like'ab\%cd%'escape'\'，匹配所有以ab%cd开头的字符串。

Like'ab\\cd%'escape'\'，匹配所有以ab\cd开头的字符串。

8．视图的查询

【例7.32】建立“计算机系”（CS表示计算机系）学生的视图如下，并要求进行修改、插入操作时保证该视图只有计算机系的学生。

此时要查询计算机系年龄小于20岁的学生的学号及年龄的SQL语句如下：

SELECT Sno, Age FORM CS-STUDENT WHERE SD='CS' AND Age<20；

系统执行该语句时，通常先将其转换成等价的对基本表的查询，然后执行查询语句。即当查询视图表时，系统先从数据字典中取出该视图的定义，然后将定义中的查询语句和对该视图的查询语句结合起来，形成一个修正的查询语句。对上例修正之后的查询语句为：

7.4.5　SQL数据更新

1．插入

要在关系数据库中插入数据，可以指定被插入的元组，或者用查询语句选出一批待插入的元组。插入语句的基本格式如下：

【例7.33】将学号为3002、课程号为C4、成绩为98的元组插入SC关系中。其语句如下：

【例7.34】若创建了一个新的视图v_employees，该视图基于表employees创建。

通过执行以下语句使用v_employees视图向表employees中添加一条新的数据记录。

2．删除

【例7.35】删除表employees中姓名为张然的记录。

3．修改

【例7.36】将教师的工资增加5％。

【例7.37】将教师的工资小于1000的增加5％工资。

使用视图可以更新数据记录，但应该注意的是，更新的只是数据库中的基表。

【例7.38】创建了一个基于表employees的视图v_employees，然后通过该视图修改表employees中的记录。其语句如下：

7.4.6　SQL访问控制

数据控制的是控制用户对数据的存储权力，是由DBA来决定的。但是，某个用户对某类数据具有何种权利，是个政策问题而不是技术问题。DBMS的功能就是保证这些决定的执行。因此，DBMS数据控制应具有如下功能。

（1）通过GRANT和REVOKE将授权通知系统，并存入数据字典。

（2）当用户提出请求时，根据授权情况检查是否执行操作请求。

SQL标准包括delete、insert、select和update权限。select权限对应于read权限，SQL还包括了references权限，用来限制用户在创建关系时定义外码的能力。

1．授权的语句格式

注意：不同类型的操作对象有不同的操作权限，常见的操作权限如表7-7所示。

表7-7　常见的操作权限

说明如下。

●　PUBLIC：接受权限的用户可以是单个或多个具体的用户，PUBLIC参数可将权限赋给全体用户。

●　WITH GRANT OPTION：若指定了此子句，那么获得了权限的用户还可以将权限赋给其他用户。

【例7.39】如果用户要求把数据库SPJ中供应商S、零件P、项目J表赋予各种权限。

（1）将对供应商S、零件P、项目J的所有操作权限赋给用户User1及User2，其授权语句如下：

（2）将对供应商S的插入权限赋给用户User1，并允许将此权限赋给其他用户，其授权语句如下：

（3）DBA把数据库SPJ中建立表的权限赋给用户User1，其授权语句如下：

2．收回权限语句格式

【例7.40】要求回收用户对数据库SPJ中供应商S、零件P、项目J表的操作权限。

（1）将用户User1及User2对供应商S、零件P、项目J的所有操作权限收回。

（2）将所有用户对供应商S的所有查询权限收回。

（3）将User1用户对供应商S的供应商编号Sno的修改权限收回。

7.4.7　嵌入式SQL

SQL提供了将SQL语句嵌入到某种高级语言中的使用方式，通常采用预编译的方法识别嵌入在高级语言中的SQL语句。该方法的关键问题是必须区分主语言中嵌入的SQL语句，以及主语言和SQL间的通信问题。为了区分主语言与SQL语言，需要在所有的SQL语句前加前缀EXEC SQL，而SQL的结束标志随主语言的不同而不同。

例如，PL/I和C语言的引用格式为：

又如，COBOL语言的引用格式为：

嵌入式SQL与主语言之间的通信通常有如下三种方式。

（1）SQL通信区（SQL Communication Area，SQLCA）向主语言传递SQL语句执行的状态信息，使主语言能够根据此信息控制程序流程。

（2）主变量也称共享变量。主语言向SQL语句提供参数主要通过主变量，主变量由主语言的程序定义，并用SQL的DECLARE语句说明。引用时，为了与SQL属性名相区别，需在主变量前加“:”。

【例7.41】根据共享变量givensno的值，查询学生关系students中学生的姓名、年龄和性别。

（3）游标SQL语言是面向集合的，一条SQL语句可产生或处理多条记录。而主语言是面向记录的，一组主变量一次只能放一条记录，所以，引入游标，通过移动游标指针来决定获取哪一条记录。

7.5　关系数据库规范化

在关系模型中，一个数据库模式是关系模式的集合。关系数据理论是指导数据库设计的基础，关系数据库设计是数据库语义学的问题。要保证构造的关系既能准确地反应现实世界，又有利于应用和具体的操作。关系数据库设计理论的核心是数据间的函数依赖，衡量的标准是关系规范化的程度及分解的无损连接和保持函数依赖性。关系数据库设计的目标是生成一组合适的、性能良好的关系模式，以减少系统中信息存储的冗余度，但又可方便地获取信息。

7.5.1　函数依赖

数据依赖是通过一个关系中属性间值的相等与否体现出来的数据间的相互关系，是现实世界属性间联系和约束的抽象，是数据内在的性质，是语义的体现。函数依赖则是一种最重要、最基本的数据依赖。

（1）函数依赖。设R（U）是属性集U上的关系模式，X、Y是U的子集。若对R（U）的任何一个可能的关系r，r中不可能存在两个元组在X上的属性值相等，而在Y上的属性值不等，则称X函数决定Y或Y函数依赖于X，记作X→Y。

（2）非平凡的函数依赖。如果X→Y，但Y⊈X，则称X→Y是非平凡的函数依赖。一般情况下，总是讨论非平凡的函数依赖。

（3）平凡的函数依赖。如果X→Y，但Y⊆X，则称X→Y是平凡的函数依赖。

（4）完全函数依赖。在R（U）中，如果X→Y，并且对于X的任何一个真子集X′，都有X′不能决定Y，则称Y对X完全函数依赖，记作

例如，给定一个学生选课关系SC（Sno，Cno，G），可以得到F＝｛（Sno，Cno）→G｝，对（Sno，Cno）中的任何一个真子集Sno或Cno都不能决定G，所以，G完全依赖于Sno，Cno。

（5）部分函数依赖。如果X→Y，但Y不完全函数依赖于X，则称Y对X部分函数依赖，记作
部分函数依赖也称为局部函数依赖。

（6）传递依赖。在R（U，F）中，如果X→Y，Y⊈X，Y⊈X，Y→Z，则称Z对X传递依赖。

（7）码。设K为R（U，F）中属性的组合，若K→U，且对于K的任何一个真子集K′，都有K′不能决定U，则K为R的候选码。若有多个候选码，则选一个作为主码。候选码通常也称为候选关键字。

（8）主属性和非主属性。包含在任何一个候选码中的属性叫做主属性，否则叫做非主属性。

（9）外码。若R（U）中的属性或属性组X非R的码，但X是另一个关系的码，则称X为外码。

（10）函数依赖的公理系统（Armstrong公理系统）。设关系模式R（U，F），其中U为属性集，F是U上的一组函数依赖，那么有如下推理规则。

A1自反律：若Y⊆X⊆U，则X→Y为F所蕴涵。

A2增广律：若X→Y为F所蕴涵，且Z⊆U，则XZ→YZ为F所蕴涵。

A3传递律：若X→Y，Y→Z为F所蕴涵，则X→Z为F所蕴涵。

根据上述三条推理规则又可推出下述三条推理规则。

合并规则：若X→Y，X→Z，则X→YZ为F所蕴涵。

伪传递率：若X→Y，WY→Z，则XW→Z为F所蕴涵。

分解规则：若X→Y，Z⊆Y，则X→Z为F所蕴涵。

7.5.2　规范化

关系数据库设计的方法之一就是设计满足适当范式的模式，通常可以通过判断分解后的模式达到几范式来评价模式规范化的程度。范式有1NF、2NF、3NF、BCNF、4NF和5NF，其中1NF级别最低。这几种范式之间5NF⊂4NF⊂BCNF⊂3NF⊂2NF⊂1NF成立。通过分解，可以将一个低一级范式的关系模式转换成若干个高一级范式的关系模式，这种过程叫做规范化。下面给出1NF、2NF和3NF的定义。

1．1NF（第一范式）

定义：若关系模式R的每一个分量是不可再分的数据项，则关系模式R属于第一范式。

例如，供应者和它所提供的零件信息，关系模式FIRST和函数依赖集F如下：

若具体的关系如表7-8所示。从表7-8可以看出，每一个分量都是不可再分的数据项，所以是1NF的。但是，1NF存在如下4个问题。

（1）冗余度大。例如，每个供应者的Sno，Sname，Status，City要与其供应的零件的种类一样多。

（2）引起修改操作的不一致性。例如，供应者S1从“天津”搬到“上海”，若稍不注意，就会使一些数据被修改，另一些数据没有被修改，导致数据修改的不一致性。

表7-8　FIRST

（3）插入异常。关系模式FRIST的主码为Sno、Pno，按照关系模式实体完整性规定，主码不能取空值或部分取空值。这样，当某个供应者的某些信息未提供时（如Pno），则不能进行插入操作，这就是所谓的插入异常。

（4）删除异常。若供应商S4的P2零件销售完了，并且以后不再销售P2零件，那么应删除该元组。这样，在基本关系FIRST找不到S4，可S4又是客观存在的。

正因为上述4个原因，所以要对模式进行分解，并引入了2NF。

2．2NF（第二范式）

定义：若关系模式R∈1NF，且每一个非主属性完全依赖于码，则关系模式R∈2NF。

换句话说，当1NF消除了非主属性对码的部分函数依赖，则称为2NF。

例如，FIRST关系中的码是Sno、Pno，而Sno→Status，因此非主属性Status部分函数依赖于码，故非2NF的。

若此时将FIRST关系分解为FIRST1（Sno，Sname，Status，City）和FIRST2（Sno，Pno，Qty）。其中，FIRST1∈2NF，FIRST2∈2NF。因为分解后的关系模式FIRST1的码为Sno，非主属性Sname、Status、City完全依赖于码Sno，所以属于2NF；关系模式FIRST2的码为Sno、Pno，非主属性Qty完全依赖于码，所以也属于2NF。

3．3NF（第三范式）

定义：若关系模式R（U，F）中若不存在这样的码X，属性组Y及非主属性Z（Z⊈Y）使得X→Y（Y
X），Y→Z成立，则关系模式R∈3NF。

即当2NF消除了非主属性对码的传递函数依赖，则称为3NF。

例如，FIRST1∉3NF，因为在分解后的关系模式FIRST1中有Sno→Status，Status→City，存在着非主属性City传递依赖于码Sno。若此时将FIRST1继续分解为：

通过上述分解，数据库模式FIRST转换为FIRST11（Sno，Sname，Status），FIRST12（Status，City）和FIRST2（Sno，Pno，Qty）三个子模式。由于这三个子模式都达到了3NF，因此称分解后的数据库模式达到了3NF。

可以证明，3NF的模式必是2NF的模式。产生冗余和异常的两个重要原因是部分依赖和传递依赖。因为3NF模式中不存在非主属性对码的部分函数依赖和传递函数依赖，所以具有较好的性能。对于非3NF的1NF、2NF，其性能弱，一般不宜作为数据库模式，通常要将它们变换成为3NF或更高级别的范式，这种变换过程称为“关系模式的规范化处理”。

7.5.3　模式分解及分解应具有的特性

1．分解

定义：（分解）关系模式R（U，F）的一个分解是指ρ＝｛R_1
（U_1
，F_1
），R_2
（U_2
，F_2
），…，Rn
（Un
，Fn
）｝，其中
并且没有Ui
⊆Uj
，1≤i，j≤n；Fi
是F在Ui
上的投影，Fi
＝｛X→Y｜X→Y∈F+
∧XY⊆Ui
｝。

对一个给定的模式进行分解，使得分解后的模式是否与原来的模式等价有三种情况。

（1）分解具有无损连接性。

（2）分解要保持函数依赖。

（3）分解既要无损连接性，又要保持函数依赖。

2．无损连接

定义：（无损连接）ρ＝｛R_1
（U_1
，F_1
），R_2
（U_2
，F_2
），…，Rk
（Uk
，Fk
）｝是关系模式R（U，F）的一个分解，若对R（U，F）的任何一个关系r均有r＝mρ
（r）成立，则分解ρ具有无损连接性（简称无损分解），其中，

定理：关系模式R（U，F）的一个分解ρ＝｛R_1
（U_1
，F_1
），R_2
（U_2
，F_2
）｝具有无损连接的充分必要的条件是U_1
⋂U_2
→U_1
－U_2
∈F+
或U_1
⋂U_2
→U_2
－U_1
∈F+
证明略。

3．保持函数依赖

定义：设关系模式R（U，F）的一个分解ρ＝｛R_1
（U_1
，F_1
），R_2
（U_2
，F_2
）…，Rk
（Uk
，Fk
）｝，如果
则称分解ρ保持函数依赖。

7.6　数据库的控制功能

7.6.1　事务管理

事务是一个操作序列，这些操作“要么都做，要么都不做”，是数据库环境中不可分割的逻辑工作单位。事务和程序是两个不同的概念，一般一个程序可包含多个事务。在SQL语言中，事务定义的语句有如下三条。

（1）BEGIN TRANSACTION：事务开始。

（2）COMMIT：事务提交。该操作表示事务成功地结束，它将通知事务管理器该事务的所有更新操作现在可以被提交或永久地保留。

（3）ROLLBACK：事务回滚。该操作表示事务非成功地结束，它将通知事务管理器出故障了，数据库可能处于不一致状态，该事务的所有更新操作必须回滚或撤销。

事务具有原子性（atomicity）、一致性（consistency）、隔离性（isolation）和持久性（durability）。这4个特性也称事务的ACID性质。

（1）原子性。事务是原子的，要么都做，要么都不做。

（2）一致性。事务执行的结果必须保证数据库从一个一致性状态变到另一个一致性状态。因此，当数据库只包含成功事务提交的结果时，称数据库处于一致性状态。

（3）隔离性。事务相互隔离。当多个事务并发执行时，任一事务的更新操作直到其成功提交的整个过程，对其他事务都是不可见的。

（4）持久性。一旦事务成功提交，即使数据库崩溃，其对数据库的更新操作也将永久有效。

7.6.2　数据库的备份与恢复

在数据库的运行过程中，难免会出现计算机系统的软、硬件故障，这些故障会影响数据库中数据的正确性，甚至破坏数据库，使数据库中全部或部分数据丢失。因此，数据库的关键技术在于建立冗余数据，即备份数据。如何在系统出现故障后能够及时使数据库恢复到故障前的正确状态，就是数据库恢复技术。

1．故障类型

在数据库中的4类故障是事务内部故障、系统故障、介质故障及计算机病毒。

（1）事务内部故障。事务内部的故障有的可以通过事务程序本身发现。例如，银行转账事务，将账户A的金额转X到账户B，此时应该将账户A的余额-X，将账户B的余额+X。如果账户A的余额不足，那么，两个事务都不做，否则都做。但有些是非预期的，不能由事务程序处理，如运算溢出、并发事务发生死锁等。

（2）系统故障。通常称为软故障，是指造成系统停止运行的任何事件，使得系统要重新启动，如CPU故障、操作系统故障和突然停电等。

（3）介质故障。通常称为硬故障，如磁盘损坏、磁头碰撞和瞬时强磁干扰。此类故障发生几率小，但破坏性最大。

（4）计算机病毒。是一种人为的故障和破坏，是在计算机程序中插入的破坏，计算机功能或者数据，可以繁殖和传播的一组计算机指令或程序代码。

2．备份方法

恢复的基本原理是“建立数据冗余”（重复存储）。建立冗余数据的方法是进行数据转储和登记日志文件。数据的转储分为静态转储和动态转储、海量转储和增量转储。

（1）静态转储和动态转储。静态转储是指在转储期间不允许对数据库进行任何存取、修改操作；动态转储是在转储期间允许对数据库进行存取、修改操作，因此，转储和用户事务可并发执行。

（2）海量转储和增量转储。海量转储是指每次转储全部数据；增量转储是指每次只转储上次转储后更新过的数据。

（3）日志文件。在事务处理的过程中，DBMS把事务开始、事务结束以及对数据库的插入、删除和修改的每一次操作写入日志文件。一旦发生故障，DBMS的恢复子系统利用日志文件撤销事务对数据库的改变，回退到事务的初始状态。因此，DBMS利用日志文件来进行事务故障恢复和系统故障恢复，并可协助后备副本进行介质故障恢复。

3．恢复

事务恢复有如下三个步骤。

（1）反向扫描文件日志（即从最后向前扫描日志文件），查找该事务的更新操作。

（2）对事务的更新操作执行逆操作。

（3）继续反向扫描日志文件，查找该事务的其他更新操作，并做同样的处理，直到事务的开始标志。

4．数据库镜像

为了避免磁盘介质出现故障影响数据库的可用性，许多DBMS提供数据库镜像功能用于数据库恢复。需要说明的是，数据库镜像是通过复制数据实现的，但频繁地复制数据会降低系统的运行效果。因此，实际应用中往往对关键的数据和日志文件镜像。

7.6.3　并发控制

所谓并发操作，是指在多用户共享的系统中，许多用户可能同时对同一数据进行操作。并发操作带来的问题是数据的不一致性，主要有三类：丢失更新、不可重复读和读脏数据。其主要原因是事务的并发操作破坏了事务的隔离性。DBMS的并发控制子系统负责协调并发事务的执行，保证数据库的完整性不受破坏，避免用户得到不正确的数据。

1．并发操作带来的问题

并发操作带来的数据不一致性有三类：丢失修改、不可重复读和读脏数据，如图7-37所示。

图7-37　三种数据不一致性

（1）丢失修改。如图7-37（a）所示，事务T_1
、T_2
都是对数据A做减1操作。事务T_1
在时刻t_6
把A修改后的值15写入数据库，但事务T_2
在时刻t_7
再把它对A减1后的值15写入。两个事务都是对A的值进行减1操作并且都执行成功，但A中的值却只减了1。例如火车售票系统，T_1
与T_2
各售出了一张票，但数据库里的存票却只减了一张，造成数据的不一致。原因在于T_1
事务对数据库的修改被T_2
事务覆盖而丢失了，破坏了事务的隔离性。

（2）不可重复读。如图7-37（b）所示，事务T_1
读取A、B的值后进行运算，事务T_2
在t_6
时刻对B的值做了修改以后，事务T_1
又重新读取A、B的值再运算，同一事务内对同一组数据的相同运算结果不同，显然与事实不相符。同样是事务T_2
干扰了事务T_1
的独立性。

（3）读脏数据。如图7-37（c）所示，事务T_1
对数据C修改之后，在t_4
时刻事务T_2
读取修改后的C值做处理，之后事务T_1
回滚，数据C恢复了原来的值，事务T_2
对C所做的处理是无效的，它读的是被丢掉的垃圾值。

通过以上三个例子，在事务并行处理的过程中对相同数据进行访问，导致了数据的不一致性。解决问题的方法可以从保证事务的隔离性入手。

2．并发控制技术

并发控制的主要技术是封锁。基本封锁的类型有排它锁（简称X锁或写锁）和共享锁（简称S锁或读锁）。

1）封锁

（1）排它锁。若事务T对数据对象A加上X锁，则只允许T读取和修改A，其他事务都不能再对A加任何类型的锁，直到T释放A上的锁。

（2）共享锁。若事务T对数据对象A加上S锁，则只允许T读取A，但不能修改A，其他事务只能再对A加S锁，直到T释放A上的S锁。这就保证了其他事务可以读A，但在T释放A上的S锁之前不能对A进行任何修改。

2）三级封锁协议

（1）一级封锁协议。事务在修改数据R之前必须先对其加X锁，直到事务结束才释放。事务结束包括正常结束（COMMIT）和非正常结束（ROLLBACK）。一级封锁协议可以解决丢失更新问题。

（2）二级封锁协议。在一级封锁协议的基础上，加上事务T在读数据R之前必须先对其加S锁，读完后即可释放S锁。二级封锁协议可以解决读脏数据的问题。但是，由于二级封锁协议读完了数据后即可释放S锁，所以不能保证可重复读。

（3）三级封锁协议。在一级封锁协议的基础上，加上事务T在读数据R之前必须先对其加S锁，直到事务结束时释放S锁。三级封锁协议除了防止丢失修改和不读“脏”数据外，还进一步防上了不可重复读。

3．活锁与死锁

所谓活锁，是指当事务T_1
封锁了数据R，事务T_2
请求封锁数据R，于是T_2
等待，当T_1
释放了R上的封锁后，系统首先批准了T_3
请求，于是T_2
仍等待，当T_3
释放了R上的封锁后，又批准了T_4
请求，依此类推，使得T_2
可能永远等待的现象。

所谓死锁，是指两个以上的事务分别请求封锁对方已经封锁的数据，导致长期等待而无法继续运行下去的现象。

4．并发调度的可串行性

多个事务的并发执行是正确的，当且仅当其结果与某一次序串行地执行它们时的结果相同，称这种调度策略是可串行化的调度。

可串行性是并发事务正确性的准则，按这个准则规定，一个给定的并发调度，当且仅当它是可串行化的才认为是正确调度。

5．两段封锁协议

所谓两段封锁协议，是指所有事务必须分两个阶段对数据项加锁和解锁。即事务分两个阶段，第一阶段是获得封锁，事务可以获得任何数据项上的任何类型的锁，但不能释放；第二阶段是释放封锁，事务可以释放任何数据项上的任何类型的锁，但不能申请。

6．封锁的粒度

封锁对象的大小称为封锁的粒度。封锁的对象可以是逻辑单元（如属性、元组、关系、索引项、整个索引直至整个数据库），也可以是物理单元（如数据页或索引页）。

第8章　数据结构

数据结构是程序设计的重要理论和技术基础，它所讨论的内容和技术，对从事软件项目的开发有重要作用。学习数据结构要达到的目标是：学会从问题出发，分析和研究计算机加工的数据结构的特性，以便为应用所涉及的数据选择适当的逻辑结构、存储结构及其相应的操作方法，为提高应用计算机解决问题的效率服务。

数据结构是指数据元素的集合（或数据对象）及元素间的相互关系和构造方法。元素之间的相互关系是数据的逻辑结构，数据元素及元素之间关系的存储形式称为存储结构（或物理结构）。

数据结构按照逻辑关系的不同分为线性结构和非线性结构两大类，其中非线性结构又可分为树结构和图结构。

算法与数据结构密切相关，数据结构是算法设计的基础，合理设计的数据结构可使算法简单而高效。

8.1　线性结构

线性结构是一种基本的数据结构，主要用于对客观世界中具有单一的前驱和后继的数据关系进行描述。线性结构的特点是数据元素之间呈现一种线性关系，即元素“一个接一个地排列”。

8.1.1　线性表

线性表是最简单、最基本、也是最常用的一种线性结构。它有两种存储方法：顺序存储和链式存储，主要的基本操作是插入、删除和查找等。

1．线性表的定义

一个线性表是n（n≥0）个元素的有限序列，通常表示为（a_1
，a_2
，…，an
）。非空线性表的特点如下。

（1）存在唯一的一个称作“第一个”的元素。

（2）存在唯一的一个称作“最后一个”的元素。

（3）除第一个元素外，序列中的每个元素均只有一个直接前驱。

（4）除最后一个元素外，序列中的每个元素均只有一个直接后继。

2．线性表的存储结构

线性表的存储结构分为顺序存储和链式存储。

1）线性表的顺序存储

线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的数据元素，从而使得逻辑上相邻的两个元素在物理位置上也相邻，如图8-1所示。在这种存储方式下，元素间的逻辑关系无须占用额外的空间来存储。

图8-1　线性表元素的顺序存储

一般地，以LOC（a_1
）表示线性表中第一个元素的存储位置，在顺序存储结构中，第i个元素ai
的存储位置为

其中，L是表中每个数据元素所占空间的大小。根据该计算关系，可随机存取表中的任一个元素。

线性表采用顺序存储结构的优点是可以随机存取表中的元素，缺点是插入和删除操作需要移动元素。插入元素前要移动元素以挪出空的存储单元，然后再插入元素；删除元素时同样需要移动元素，以填充被删除的元素空出来的存储单元。

在表长为n的线性表中插入新元素时，共有n＋1个插入位置，在位置1（元素a_1
所在位置）插入新元素，表中原有的n个元素都需要移动，在位置n＋1（元素an
所在位置之后）插入新元素时不需要移动任何元素，因此，等概率下（即新元素在n＋1个位置插入的概率相同时）插入一个新元素需要移动元素的个数Einsert
为

其中，Pi
表示在表中的位置i插入新元素的概率。

在表长为n的线性表中删除元素时，共有n个可删除的元素，删除元素a_1
时需要移动n－1个元素，删除元素an
时不需要移动元素，因此，等概率下删除元素时需要移动元素的个数Edelete
为

其中，qi
表示删除元素ai
的概率。

2）线性表的链式存储

线性表的链式存储是用节点来存储数据元素，基本的节点结构如下所示：

其中，数据域用于存储数据元素的值，指针域则存储当前元素的直接前驱或直接后继信息，指针域中的信息称为指针（或链）。

存储各数据元素的节点的地址并不要求是连续的，因此存储数据元素的同时必须存储元素之间的逻辑关系。另外，节点空间只有在需要的时候才申请，无须事先分配。

节点之间通过指针域构成一个链表，若节点中只有一个指针域，则称为线性链表（或单链表），如图8-2所示。

图8-2　线性表元素的单链表存储

设线性表中的元素是整型，则单链表节点类型的定义为：

在链式存储结构中，只需要一个指针（称为头指针，如图8-2中的head）指向第一个节点，就可以顺序访问到表中的任意一个元素。

在链式存储结构下进行插入和删除，其实质都是对相关指针的修改。在单链表中，若在p所指节点后插入新元素节点（s所指节点，已经生成），如图8-3（a）所示，其基本步骤如下。

即先将p所指节点的后继节点指针赋给s所指节点的指针域，然后将p所指节点的指针域修改为指向s所指节点。

图8-3　在单链表中插入、删除节点时的指针变化示意图

同理，在单链表中删除p所指节点的后继节点时（如图8-3（b）所示），步骤如下。

即先令临时指针q指向待删除的节点，然后修改p所指节点的指针域为指向p所指节点的后继的后继节点，从而将元素b所在的节点从链表中摘除，最后释放q所指节点的空间。

实际应用中，为了简化对链表状态的判定和处理，特别引入一个不存储数据元素的节点，称为头节点，将其作为链表的第一个节点并令头指针指向该节点。

下面给出单链表上查找、插入和删除运算的实现过程。

【函数】单链表的查找运算。

LinkList Find_List（LinkList L, intk）/*L为带头节点单链表的头指针*/

/*在表中查找第k个元素，若找到，返回该元素节点的指针；否则，返回空指针NULL*/

【函数】单链表的插入运算。

【函数】单链表的删除运算。

线性表采用链表作为存储结构时，不能对数据元素进行随机访问，但是插入和删除操作不需要移动元素。

根据节点中指针域的设置方式，还有其他几种链表结构。

●　双向链表。每个节点包含两个指针，分别指出当前节点元素的直接前驱和直接后继。其特点是可从表中任意的元素节点出发，从两个方向上遍历链表。

●　循环链表。在单向链表（或双向链表的基础上），令表尾节点的指针指向表中的第一个节点，构成循环链表。其特点是可从表中任意节点开始遍历整个链表。

●　静态链表。借助数组来描述线性表的链式存储结构。

若双向链表中节点的front和next指针域分别指示当前节点的直接前驱和直接后继，则在双向链表中插入节点*s时指针的变化情况如图8-4（a）所示，其操作过程可表示为：

在双向链表中删除节点时指针的变化情况如图8-4（b）所示，其操作过程可表示为：

图8-4　双向链表插入和删除节点时的指针变化示意图

8.1.2　栈和队列

栈和队列是软件设计中常用的两种数据结构，它们的逻辑结构和线性表相同。其特点在于运算有所限制：栈按“后进先出”的规则进行操作，队列按“先进先出”的规则进行操作，故称运算受限的线性表。

1．栈

1）栈的定义及基本运算

（1）栈的定义。

栈是只能通过访问它的一端来实现数据存储和检索的一种线性数据结构。换句话说，栈的修改是按先进后出的原则进行的。因此，栈又称为后进先出（Last In First Out，LIFO）的线性表。在栈中进行插入和删除操作的一端称为栈顶（top），相应地，另一端称为栈底（bottom）。不含数据元素的栈称为空栈。

（2）栈的基本运算。

①　初始化栈InitStack（S）：创建一个空栈S。

②　判栈空StackEmpty（S）：当栈S为空时返回“真”值，否则返回“假”值。

③　入栈Push（S,x）：将元素x加入栈顶，并更新栈顶指针。

④　出栈Pop（S）：将栈顶元素从栈中删除，并更新栈顶指针。若需要得到栈顶元素的值，可将Pop（S）定义为一个返回栈顶元素值的函数。

⑤　读栈顶元素Top（S）：返回栈顶元素的值，但不修改栈顶指针。

应用中常使用上述5种基本运算实现基于栈结构的问题求解。

2）栈的存储结构

（1）顺序存储。

栈的顺序存储是指用一组地址连续的存储单元依次存储自栈顶到栈底的数据元素，同时附设指针top指示栈顶元素的位置。采用顺序存储结构的栈也称为顺序栈。在该存储方式下，需要预先定义（或申请）栈的存储空间，也就是说，栈空间的容量是有限的。因此，在顺序栈中，当一个元素入栈时，需要判断是否栈满（栈空间中没有空闲单元），若栈满，则元素入栈会发生上溢现象。

（2）栈的链式存储。

为了克服顺序存储的栈可能存在上溢的不足，可以用链表存储栈中的元素。用链表作为存储结构的栈也称为链栈。由于栈中元素的插入和删除仅在栈顶一端进行，因此不必设置头节点，链表的头指针就是栈顶指针。链栈的表示如图8-5所示。

（3）栈的应用。

栈的典型应用包括表达式求值、括号匹配等，在计算机语言的实现以及将递归过程转变为非递归过程的处理中，栈有重要的作用。

图8-5　链栈示意图

2．队列

1）队列的定义及基本运算。

（1）队列的定义。

队列是一种先进先出（First In First Out，FIFO）的线性表，它只允许在表的一端插入元素，而在表的另一端删除元素。在队列中，允许插入元素的一端称为队尾（rear），允许删除元素的一端称为队头（front）。

（2）队列的基本运算。

①　初始化队InitQueue（Q）：创建一个空的队列Q。

②　判队空Empty（Q）：当队列为空时返回“真”值，否则返回“假”值。

③　入队EnQueue（Q,x）：将元素x加入到队列Q的队尾，并更新队尾指针。

④　出队DeQueue（Q）：将队头元素从队列Q中删除，并更新队头指针。

⑤　读队头元素FrontQue（Q）：返回队头元素的值，但不更新队头指针。

2）队列的存储结构

（1）队列的顺序存储。

队列的顺序存储结构又称为顺序队列，它也是利用一组地址连续的存储单元存放队列中的元素。由于队列中元素的插入和删除限定在表的两端进行，因此设置队头指针和队尾指针，分别指示出当前的队首元素和队尾元素。

下面设顺序队列Q的容量为6，其队头指针为front，队尾指针为rear，头、尾指针和队列中元素之间的关系如图8-6所示。

在顺序队列中，为了降低运算的复杂度，元素入队时只修改队尾指针，元素出队时只修改队头指针。由于顺序队列的存储空间是提前设定的，所以队尾指针会有一个上限值，当队尾指针达到该上限时，就不能只通过修改队尾指针来实现新元素的入队操作了。此时，可通过整除取余运算将顺序队列假想成一个环状结构，如图8-7所示，称之为循环队列。

图8-6　队列的头、尾指针与队列中元素之间的关系

图8-7　循环队列的头、尾指针示意图

设循环队列Q的容量为MAXSIZE，初始时队列为空，且Q.rear和Q.front都等于0，如图8-8（a）所示。

元素入队时，修改队尾指针Q.rear＝（Q.rear+1）%MAXSIZE，如图8-8（b）所示。

元素出队时，修改队头指针Q.front＝（Q.front+1）%MAXSIZE，如图5-8（c）所示。

根据出队列操作的定义，当出队操作导致队列变为空时，则有Q.rear＝＝Q.front，如图8-8（d）所示；若队列满，则Q.rear＝＝Q.front，如图8-8（e）所示。

在队列空和队列满的情况下，循环队列的队头、队尾指针指向的位置是相同的，此时仅仅根据Q.rear和O.front之间的关系无法断定队列的状态。为了区别队空和队满的情况，可采用以下两种处理方式：其一是设置一个标志位，以区别头、尾指针的值相同时队列是空还是满；其二是牺牲一个存储单元，约定以“队列的尾指针所指位置的下一个位置是队头指针”表示队列满，如图8-8（f）所示，而头、尾指针的值相同时表示队列为空。

设队列中的元素类型为整型，则循环队列的类型定义为。

图8-8　循环队列的头、尾指针示意图

【函数】创建一个空的循环队列。

【函数】元素入循环队列。

【函数】元素出循环队列。

（2）队列的链式存储。

队列的链式存储也称为链队列。这里为了便于操作，给链队列添加一个头节点，并令头指针指向头节点。因此，队列为空的判定条件是：头指针和尾指针的值相同，且均指向头节点。队列的链式存储结构如图8-9所示。

3）队列的应用

队列结构常用于处理需要排队的场合，如操作系统中处理打印任务的打印队列、离散事件的计算机模拟等。

图8-9　链队列示意图

8.1.3　串

串（字符串）是一种特殊的线性表，其数据元素为字符。计算机中非数值问题处理的对象经常是字符串数据，例如，在汇编和高级语言的编译程序中，源程序和目标程序都是字符串数据；在事务处理程序中，姓名、地址等一般也是作为字符串处理的。串具有自身的特性，运算时常常把一个串作为一个整体来处理。这里介绍串的定义、基本运算、存储结构及串的模式匹配算法。

1．串的定义及基本运算

1）串的定义

串是仅由字符构成的有限序列，是取值范围受限的线性表。一般记为
其中S是串名，单引号括起来的字符序列是串值。

2）串的几个基本概念

●　空串：长度为零的串，空串不包含任何字符。

●　空格串：由一个或多个空格组成的串。虽然空格是一个空白字符，但它也是一个字符，计算串长度时要将其计算在内。

●　子串：由串中任意长度的连续字符构成的序列称为子串。含有子串的串称为主串。子串在主串中的位置是指子串首次出现时，该子串的第一个字符在主串中的位置。空串是任意串的子串。

●　串相等：指两个串长度相等且对应位置上的字符也相同。

●　串比较：两个串比较大小时以字符的ASCⅡ码值（或其他字符编码集合）作为依据。实质上，比较操作从两个串的第一个字符开始进行，字符的码值大者所在的串为大；若其中一个串先结束，则以串长较大者为大。

3）串的基本操作

（1）赋值操作StrAssign（s,t）：将串s的值赋给串t。

（2）联接操作Concat（s,t）：将串t接续在串s的尾部，形成一个新串。

（3）求串长StrLength（s）：返回串s的长度。

（4）串比较StrCompare（s,t）：比较两个串的大小。返回值-1、0和1分别表示s＜t、s＝t和s＞t三种情况。

（5）求子串SubString（s,start,len）：返回串s中从start开始的、长度为len的字符序列。

以上5种最基本的串操作构成了串的最小操作子集，利用它们可以实现串的其他运算。

2．串的存储结构

（1）串的定长存储结构。串的静态存储结构就是串的顺序存储结构，用一组地址连续的存储单元来存储串值的字符序列。由于串中的元素为字符，所以可通过程序语言提供的字符数组定义串的存储空间，也可以根据串长的需要动态申请字符串的空间。

（2）串的链式存储。字符串也可以采用链表作为存储结构，当用链表存储串中的字符时，每个节点中可以存储一个字符，也可以存储多个字符，此时要考虑存储密度问题。

在链式存储结构中，节点大小的选择和顺序存储方法中数组空间大小的选择一样重要，它直接影响对串的处理效率。

3．串的模式匹配

子串的定位操作通常称为串的模式匹配，它是各种串处理系统中最重要的运算之一。子串也称为模式串。

1）朴素的模式匹配算法

该算法也称为布鲁特—福斯算法，其基本思想是从主串的第一个字符起与模式串的第一个字符比较，若相等，则继续逐对字符进行后续的比较，否则从主串第二个字符起与模式串的第一个字符重新比较，直至模式串中每个字符依次和主串中一个连续的字符序列相等时为止，此时称为匹配成功。如果不能在主串中找到与模式串相同的子串，则匹配失败。

【函数】以字符数组存储字符串，实现朴素的模式匹配算法。

假设主串和模式串的长度分别为n和m，位置序号从0开始计算，下面分析朴素模式匹配算法的时间复杂度。设从主串的第i个位置开始与模式串匹配成功，在前i趟匹配中（位置0～i－1），每趟不成功的匹配都是模式串的第一个字符与主串中相应的字符不相同，则在前i趟匹配中，字符的比较共进行了i次，而第i＋1趟（从位置i开始）成功匹配的字符比较次数为m，所以总的字符比较次数为i＋m（0≤i≤n－m）。若在主串的n－m个起始位置上匹配成功的概率相同，则在最好情况下，匹配成功时字符问的平均比较次数为

因此，在最好情况下匹配算法的时间复杂度为Ο（n＋m）。

而在最坏情况下，每一趟不成功的匹配都是模式串的最后一个字符与主串中相应的字符不相等，则主串中新一趟的起始位置为i－m＋2。若设从主串的第i个字符开始匹配时成功，则前i趟不成功的匹配中，每趟都比较了m次，总共比较了i×m次。因此，最坏情况下的平均比较次数为

由于n>>m，所以该算法在最坏情况下的时间复杂度为Ο（n×m）。

2）改进的模式匹配算法

改进的模式匹配算法又称为KMP算法，其改进之处在于：每当匹配过程中出现相比较的字符不相等时，不需要回溯主串的字符位置指针，而是利用已经得到的“部分匹配”结果，将模式串向右“滑动”尽可能远的距离，再继续进行比较。

设模式串为″p_0
…pm－1
″，KMP匹配算法的思想是：当模式串中的字符pj
与主串中相应的字符Si
不相等时，因其前j个字符（″p_0
…pj－1
″）已经获得了匹配，所以若模式串中的″p_0
…pk－1
″与″pj－k
…pj－1
″相同，这时可令pk
与Si
进行比较，从而使i无须回退。

在KMP算法中，依据模式串的next函数值实现子串的滑动。若令next[j]＝k，则next[j]表示当模式串中的pj
与主串中相应字符不相等时，令模式串的pk
与主串的相应字符进行比较。

next函数的定义如下：

【函数】求模式串的next函数。

例如，设主串为“abacbcabababbcbc”，模式串为“abababb”，且模式串的next函数值如下表所示，则KMP算法的匹配过程如图8-10所示。

【函数】KMP模式匹配算法，设模式串第一个字符的下标为0。

（a）i是主串字符的下标，j是模式串字符的下标

（b）若S[i]==P[j]，则i++、j++；否则，j=next[j]，即S[i]与P[next[j]]比较，重复该步，若j等于-1，则令i++、j=0

（c）重复（b）直到完全匹配或达到主串结尾匹配失败

图8-10　KMP模式匹配过程示意图

8.2　数组、矩阵和广义表

数组与广义表可看作是线性表的推广，其特点是数据元素仍然是一个表。这里讨论多维数组的逻辑结构和存储结构，特殊矩阵和矩阵的压缩存储，广义表的逻辑结构、存储结构和基本运算。

8.2.1　数组

1．数组的定义及基本运算

1）数组的定义

数组是定长线性表在维数上的扩张，即线性表中的元素又是一个线性表。n维数组是一种“同构”的数据结构，其每个数据元素类型相同，结构一致。

设有n维数组A[b_1
，b_2
，…，bn
]，其每一维的下界都为1，bi
是第i维的上界。从数据结构的逻辑关系角度来看，A中的每个元素A[j_1
，j_2
，…，jn
]（1≤ji
≤bi
）都被n个关系所约束。在每个关系中，除第一个和最后一个元素外，其余元素都只有一个直接后继和一个直接前驱。因此就单个关系而言，仍是线性的。

以二维数组A[m，n]为例，可以把它看成是一个定长的线性表，它的每个元素也是一个定长线性表。

A可看成一个行向量形式的线性表：

或列向量形式的线性表：

数组结构的特点如下。

（1）数据元素数目固定。一旦定义了一个数组结构，就不再有元素个数的增减变化。

（2）数据元素具有相同的类型。

（3）数据元素的下标关系具有上下界的约束且下标有序。

2）数组的两个基本运算

（1）给定一组下标，存取相应的数据元素。

（2）给定一组下标，修改相应的数据元素中某个数据项的值。

几乎所有的高级程序设计语言都提供了数组类型。实际上，在程序语言中把数组看成是具有共同名字的同一类型多个变量的集合。

2．数组的顺序存储

数组一般不作插入和删除运算，一旦定义了数组，则结构中的数据元素个数和元素之间的关系就不再发生变动，因此数组适合于采用顺序存储结构。

由于计算机的内存结构是一维线性的，因此存储多维数组时必须按某种方式进行降维处理，即将数组元素排成一个线性序列，这就产生了次序约定问题。因为多维数组是由较低一维的数组来定义的，依此类推，通过这种递推关系将多维数组的数据元素排成一个线性序列。

对于数组，一旦确定了其维数和各维的长度，便可为它分配存储空间。反之，只要给出一组下标便可求得相应数组元素的存储位置，即在数据的顺序存储结构中，数据元素的位置是其下标的线性函数。

二维数组的存储结构可分为以行为主序和以列为主序的两种方法，如图8-11所示。

图8-11　二维数组的两种存储方式

设每个数据元素占用L个单元，m、n为数组的行数和列数，Loc（a_11
）表示元素a_11
的地址，那么以行为主序优先存储的地址计算公式为

同理，以列为主序优先存储的地址计算公式为

推广至多维数组，按下标顺序存储时，先排最右的下标，从右向左直到最左下标，而逆下标顺序则正好相反。

8.2.2　矩阵

矩阵是很多科学与工程计算问题中研究的数学对象。在数据结构中，主要讨论如何在节省存储空间的情况下，使矩阵的各种运算能高效地进行。

在一些矩阵中，存在很多值相同的元素或者是0元素。为了节省存储空间，可以对这类矩阵进行压缩存储，即为多个值相同的元素只分配一个存储单元，对0元不分配存储单元。假如值相同的元素或0元在矩阵中的分布有一定的规律，则称此类矩阵为特殊矩阵，否则称为稀疏矩阵。

1．特殊矩阵

若矩阵中元素（或非0元素）的分布有一定的规律，则称之为特殊矩阵。常见的特殊矩阵有对称矩阵、三角矩阵和对角矩阵等。对于特殊矩阵，由于其非0元的分布有一定的规律，所以可将其压缩存储在一维数组中，并建立起每个非0元在矩阵中的位置与其在一维数组中的位置之间的对应关系。

若矩阵An×n
中的元素特点为aij
＝aji
（1≤i，j≤n），则称之为n阶对称矩阵。

若对称矩阵中的每一对元素仅占用一个存储单元，那么就可将n^2
个元素压缩存储到能存放n（n＋1）/2个元素的存储空间中。不失一般性，以行为主序存储下三角（包括对角线）中的元素。假设以一维数组B[n（n＋1）/2]作为n阶对称矩阵A的压缩存储空间，则B[k]（1≤k≤n（n＋1）/2）与矩阵元素aij
之间存在着一一对应的关系。

对角矩阵是指矩阵中的非0元素都集中在以主对角线为中心的带状区域中，即除了主对角线上和直接在对角线上、下方若干条对角线上的元素外，其余的矩阵元素都为0。一个n阶的三对角矩阵如图8-12所示。

若以行为主序将n阶三对角矩阵An×n
的非0元素存储在一维数组B[k]（1≤k≤3×n－2）中，则元素位置之间的对应关系为

其他特殊矩阵可作类似的推算，这里不再一一说明。

2．稀疏矩阵

在一个矩阵中，若非0元素的个数远远少于0元素的个数，且非0元素的分布没有规律，则称之为稀疏矩阵。对于稀疏矩阵，存储非0元素时必须同时存储其位置（即行号和列号），所以三元组（i，j，aij
）可唯一确定矩阵A中的一个元素。由此，一个稀疏矩阵可由表示非0元素的三元组及其行、列数唯一确定。图8-13所示的是一个6行7列的稀疏矩阵，其三元组表为（（1，2，12），（1，3，9），（3，1，-3），（3，6，14），（4，3，24），（5，2，18），（6，1，15），（6，4，-7））。

图8-12　三对角矩阵示意图

图8-13　稀疏矩阵示意图

稀疏矩阵的三元组表的顺序存储结构称为三元组顺序表，常用的三元组表的链式存储结构是十字链表。

8.2.3　广义表

1．广义表的定义

广义表是线性表的推广，是由0个或多个单元素或子表组成的有限序列。

广义表与线性表的区别在于：线性表的元素都是结构上不可分的单元素，而广义表的元素既可以是单元素，也可以是有结构的表。

广义表一般记为

其中，αi
（1≤i≤n）既可以是单个元素，又可以是广义表，分别称为原子和子表。

广义表的长度是指广义表中元素的个数。广义表的深度是指广义表展开后所含的括号的最大层数。

2．广义表的基本操作

与线性表类似，广义表也有查找、插入和删除等操作。由于广义表的结构较复杂，其各种运算的实现也不如线性表简单，这里只讨论两个重要的运算。

（1）取表头head（LS）。非空广义表LS的第一个元素称为表头，它可以是一个单元素，也可以是一个子表。

（2）取表尾tail（LS）。在非空广义表中，除表头元素之外，由其余元素所构成的表称为表尾。非空广义表的表尾必定是一个表。

3．广义表的特点

（1）广义表可以是多层次的结构，因为广义表的元素可以是子表，而子表的元素还可以是子表。

（2）广义表中的元素可以是已经定义的广义表的名字，所以一个广义表可被其他广义表所共享。

（3）广义表可以是一个递归的表，即广义表中的元素也可以是本广义表的名字。

4．广义表的存储结构

由于广义表中的元素本身又可以具有结构，它是一种带有层次的非线性结构，因此难以用顺序存储结构表示，通常采用链式存储结构。由上面讨论可知，若广义表不空，则可分解为表头和表尾两部分。反之，一对确定的表头和表尾可唯一确定一个广义表。针对原子和子表可分别设计不同的节点结构，如图8-14所示。对于广义表LS＝（a，（b，c，d）），其链式存储结构如图8-15所示。

图8-14　广义表的链表节点结构

图8-15　广义表（a，（b，c，d））的存储结构示意图

8.3　树

树结构是一种非常重要的非线性结构，该结构中一个数据元素可以有两个或两个以上的直接后继元素，树可以用来描述客观世界中广泛存在的层次结构关系。

8.3.1　树与二叉树的定义

1．树的定义

树是n（n≥0）个节点的有限集合，当n＝0时称为空树。在任一非空树（n＞0）中，有且仅有一个称为根的节点；其余节点可分为m（m≥0）个互不相交的有限集T_1
，T_2
，…，Tm
，其中每个Ti
又都是一棵树，并且称为根节点的子树。

树的定义是递归的，它表明了树本身的固有特性，也就是一棵树由若干棵子树构成，而子树又由更小的子树构成。

该定义只给出了树的组成特点，若从数据结构的逻辑关系角度来看，树中元素之间有明显的层次关系。对树中的某个节点，它最多只和上一层的一个节点（即其双亲节点）有直接的关系，而与其下一层的多个节点（即其孩子节点）有直接关系，如图8-16所示。通常，凡是分等级的分类方案都可以用具有严格层次关系的树结构来描述。

图8-16　树结构示意图

2．树的基本概念

（1）双亲、孩子和兄弟：节点的子树的根称为该节点的孩子；相应地，该节点称为其子节点的双亲。具有相同双亲的节点互为兄弟。

（2）节点的度：一个节点的子树的个数记为该节点的度。

（3）叶子节点：也称为终端节点，指度为0的节点。

（4）内部节点：度不为0的节点称为分支节点或非终端节点。除根节点之外，分支节点也称为内部节点。

（5）节点的层次：根为第一层，根的孩子为第二层，依此类推，若某节点在第i层，则其孩子节点在第i＋1层。

（6）树的高度：一棵树的最大层次数记为树的高度（或深度）。

（7）有序（无序）树：若将树中节点的各子树看成是从左到右具有次序的，即不能交换，则称该树为有序树，否则称为无序树。

3．二叉树的定义

二叉树是n（n≥0）个节点的有限集合，它或者是空树（n＝0），或者是由一个根节点及两棵不相交的且分别称为左、右子树的二叉树所组成。可见，二叉树同样具有递归性质。

特别需要注意的是，尽管树和二叉树的概念之间有许多联系，但它们是两个不同的概念。树和二叉树之间最主要的区别是：二叉树节点的子树要区分左子树和右子树，即使在节点只有一棵子树的情况下，也要明确指出该子树是左子树还是右子树。另外，二叉树节点最大度为2，而树中不限制节点的度数，如图8-17所示。

图8-17　二叉树与普通树

8.3.2　二叉树的性质与存储结构

1．二叉树的性质

（1）二叉树第i层（i≥1）上至多有2i－1
个节点。

此性质只要对层数i进行数学归纳证明即可。

（2）高度为k的二叉树至多有2k
－1个节点（k≥1）。

由性质1，每一层的节点数都取最大值
即可。

（3）对任何一棵二叉树，若其终端节点数为n_0
，度为2的节点数为n_2
，则n_0
＝n_2
＋1。

（4）具有n个节点的完全二叉树的深度为
log_2
n
＋1。

（5）对一棵有n个节点的完全二叉树的节点按层次自左至右进行编号，则对任一节点i（1≤i≤n）有：

①　若i＝1，则节点i是二叉树的根，无双亲；若i＞1，则其双亲为

②　若2i＞n，则节点i没有左孩子，否则其左孩子为2i。

③　若2i＋1＞n，则节点i没有右孩子，否则其右孩子为2i＋1。

若深度为k的二叉树有2k
－1个节点，则称其为满二叉树。可以对满二叉树中的节点进行连续编号：约定编号从根节点起，自上而下、自左至右依次进行。深度为k、有n个节点的二叉树，当且仅当其每一个节点都与深度为k的满二叉树中编号从1至n的节点一一对应时，称之为完全二叉树。满二叉树和完全二叉树的示意图如图8-18所示。

图8-18　满二叉树和完全二叉树示意图

2．二叉树的存储结构

1）二叉树的顺序存储结构

用一组地址连续的存储单元存储二叉树中的节点，必须把节点排成一个适当的线性序列，并且节点在这个序列中的相互位置能反映出节点之间的逻辑关系。

对于深度为k的完全二叉树，除第k层外，其余各层中含有最大的节点数，即每一层的节点数恰为其上一层节点数的两倍，由此从一个节点的编号可推知其双亲、左孩子和右孩子的编号。

假设有编号为i的节点，则有：

（1）若i＝1，该节点为根节点，无双亲。

（2）若i＞1，该节点的双亲节点为（i＋1）/2（取整数）。

（3）若2i≤n，则该节点的左孩子编号为2i，否则无左孩子。

（4）若2i＋1≤n，则该节点的右孩子编号为2i＋1，否则无右孩子。

（5）若i为奇数且不为1，则该节点左兄弟的编号为i－1，否则无左兄弟。

（6）若i为偶数且小于n，则该节点右兄弟的编号为i＋1，否则无右兄弟。

完全二叉树的顺序存储结构如图8-19（a）所示。

显然，完全二叉树采用顺序存储结构既简单又节省空间，对于一般的二叉树，则不宜采用顺序存储结构。因为一般的二叉树也必须按照完全二叉树的形式存储，也就是要添上一些实际并不存在的“虚节点”，这将造成空间的浪费，如图8-19（b）所示。

图8-19　二叉树的顺序存储结构

在最坏情况下，一个深度为k且只有k个节点的二叉树（单支树）却需要2k
－1个存储单元。

2）二叉树的链式存储结构

由于二叉树的节点中包含有数据元素、左子树的根、右子树的根及双亲等信息，因此可以用三叉链表或二叉链表（即一个节点含有三个指针或两个指针）来存储二叉树，链表的头指针指向二叉树的根节点，如图8-20所示。

图8-20　二叉树的链表存储结构

设节点中的数据元素为整型，则二叉链表的节点类型定义如下：

在不同的存储结构中，实现二叉树的运算方法也不同，具体应采用什么存储结构，除考虑二叉树的形态外还应考虑需要进行的运算特点。

8.3.3　二叉树的遍历

遍历是按某种策略访问树中的每个节点，且仅访问一次的过程。由于二叉树所具有的递归性质，一棵非空的二叉树可以看作是由根节点、左子树和右子树三部分构成的，因此若能依次遍历这三部分，也就遍历了整棵二叉树。按照先遍历左子树后遍历右子树的约定，根据访问根节点位置的不同，可得到二叉树的先序、中序和后序三种遍历方法。此外，对二叉树还可进行层序遍历。

【函数】二叉树的先序遍历。

【函数】二叉树的中序遍历。

【函数】二叉树的后序遍历。

从树根节点出发，三种方法的遍历路线如图8-21所示。该路线从根节点出发，逆时针沿着二叉树的外缘移动，对每个节点均途经三次。若第一次经过节点时进行访问，则是先序遍历；若第二次（或第三次）经过节点时访问节点，则是中序遍历（或后序遍历）。因此，只要将遍历路线上所有在第一次、第二次和第三次经过的节点信息分别输出，即可分别得到该二叉树的先序、中序和后序遍历序列。粗略地讲，若去掉三种遍历算法中的打印输出语句，则三种遍历方法基本相同。这说明三种遍历过程的路线相同。

图8-21　三种遍历过程执行示意图

遍历二叉树的基本操作就是访问节点，不论按照哪种次序遍历，对含有n个节点的二叉树，遍历算法的时间复杂度都为Ο（n）。因为在遍历的过程中，每进行一次递归调用，都是将函数的“活动记录”压入栈中，因此，栈的最大长度恰为树的高度。所以，在最坏情况下，二叉树是有n个节点且高度为n的单枝树，遍历算法的空间复杂度也为Ο（n）。

借助于一个栈，可将二叉树的递归遍历算法转换为非递归算法。下面以中序遍历为例给出中序遍历的非递归算法。

【函数】二叉树的中序非递归遍历算法。

遍历二叉树的过程实质上是按一定规则将树中的节点排成一个线性序列的过程，因此遍历操作得到的是树中节点的一个线性序列。在每一种序列中，有且仅有一个起始点和一个终节点，其余节点有且仅有唯一的直接前驱和直接后继。显然，关于节点的前驱和后继的讨论是针对某一个遍历序列而言的。

对二叉树还可以进行层序遍历。设二叉树的根节点所在层数为1，层序遍历就是从树的根节点出发，首先访问第1层的树根节点，然后从左到右依次访问第二层上的节点，其次是第三层上的节点，依此类推，自上而下、自左至右逐层访问树中各节点的过程就是层序遍历。

【算法】二叉树的层序遍历算法。

8.3.4　线索二叉树

1．线索二叉树的定义

二叉树的遍历实质上是对一个非线性结构进行线性化的过程，它使得每个节点（除第一个和最后一个外）在这些线性序列中有且仅有一个直接前驱和直接后继。但在二叉链表存储结构中，只能找到一个节点的左、右孩子，而不能直接得到节点在任一遍历序列中的前驱和后继，这些信息只有在遍历的动态过程中才能得到，因此，引入线索二叉树来保存这些动态过程得到的信息。

2．建立线索二叉树

为了保存节点在任一序列中的前驱和后继信息，可以考虑在每个节点中增加两个指针域来存放遍历时得到的前驱和后继信息，这样就可以为以后的访问带来方便。但增加指针信息会降低存储空间的利用率，因此可考虑采用其他方法。

若n个节点的二叉树采用二叉链表作存储结构，则链表中必然有n＋1个空指针域，可以利用这些空指针域来存放节点的前驱和后继信息。线索链表的节点结构如下所示。

其中：

若二叉树的二叉链表采用以上所示的节点结构，则相应的链表称为线索链表，其中指向节点前驱、后继的指针称为线索。加上线索的二叉树称为线索二叉树。对二叉树以某种次序遍历使其成为线索二叉树的过程称为线索化。中序线索二叉树及其存储结构如图8-22所示。

那么如何进行线索化呢？按某种次序将二叉树线索化，实质上是在遍历过程中用线索取代空指针。因此，若设指针p指向正在访问的节点，则遍历时设立一个指针pre，使其始终指向刚刚访问过的节点，这样就记下了遍历过程中节点被访问的先后关系。

在遍历的过程中，设指针p指向正在访问的节点。

图8-22　线索二叉树及其存储结构示意图

（1）若p所指向的节点有空指针域，则将相应的标志域置为1。

（2）若pre!=NULL且pre所指节点的rtag等于1，则令pre->rchild=p。

（3）若p所指向节点的ltag等于1，则令p->lchild=pre。

（4）使pre指向刚刚访问过的节点，即令pre=p。

需要说明的是，用这种方法得到的线索二叉树，其线索并不完整，也就是说，部分节点的前驱或后继信息还不能从其存储结构中直接得到。

3．访问线索二叉树

如何在线索二叉树中查找节点的前驱和后继呢？

以中序线索二叉树为例，令p指向树中的某个节点，查找p所指节点的后继节点的方法如下。

（1）若p->rtag==1，则p->rchild即指向其后继节点。

（2）若p->rtag==0，则p所指节点的中序后继必然是其右子树中进行中序遍历得到的第一个节点。也就是说，从p所指节点的右子树的根节点出发，沿左孩子指针链向下查找，直到找到一个没有左孩子的节点时为止，这个节点就是p所指节点的直接后继节点，也称其为p的右子树中“最左下”的节点。

令p指向中序线索树中的某个节点，则查找p所指节点的直接前驱的方法如下。

（1）若p->ltag==1，则p->lchild即指向其前驱节点。

（2）若p->ltag==0，则p所指节点的中序前驱必然是其左子树中进行中序遍历得到的最后一个节点。也就是说，从p所指节点的左子树的根节点出发，沿右孩子指针链向下查找，直到找到一个没有右孩子的节点时为止，这个节点就是p所指节点的直接前驱节点，也称其为p的左子树中“最右下”的节点。

8.3.5　最优二叉树

1．最优二叉树

最优二叉树又称为哈夫曼树，是一类带权路径长度最短的树。路径是从树中一个节点到另一个节点之间的通路，路径上的分支数目称为路径长度。

树的路径长度是从树根到每一个叶子之间的路径长度之和。节点的带权路径长度为从该节点到树根之间的路径长度与该节点权的乘积。

树的带权路径长度为树中所有叶子节点的带权路径长度之和，记为

其中，n为带权叶子节点数目，wk
为叶子节点的权值，lk
为叶子节点到根的路径长度。

哈夫曼树是指权值为w_1
，w_2
，…，wn
的n个叶子节点的二叉树中带权路径长度最小的二叉树。

例如，图8-23所示的具有4个叶子节点的二叉树，其中以图8-23（b）所示二叉树带权路径长度最小。

图8-23　不同带权路径长度的二叉树

那么如何构造最优二叉树呢？构造最优二叉树的哈夫曼算法如下。

（1）根据给定的n个权值｛w_1
，w_2
，…，wn
｝，构成n棵二叉树的集合F＝｛T_1
，T_2
，…，Tn
｝，其中每棵树Ti
中只有一个带权为wi
的根节点，其左右子树均空。

（2）在F中选取两棵权值最小的树作为左、右子树构造一棵新的二叉树，置新构造二叉树的根节点的权值为其左、右子树根节点的权值之和。

（3）从F中删除这两棵树，同时将新得到的二叉树加入到F中。

重复（2）、（3）步，直到F中只含一棵树时为止，这棵树便是最优二叉树（哈夫曼树）。

由此算法可知，以选中的两棵子树构成新的二叉树，谁作为左子树，谁作为右子树，并没有明确。所以具有n个叶子节点的权值为w_1
，w_2
，…，wn
的最优二叉树不唯一，但其WPL值是唯一确定的。

当给定了n个权值后，构造出的最优二叉树中的节点数目m就确定了，即m＝2×n－1，所以可用一维的结构数组来存储最优二叉树，下面举例说明。

【函数】创建最优二叉树。

2．哈夫曼编码

若对每个字符编制相同长度的二进制码，则称为等长编码。例如，英文字符集中的26个字符可采用5位二进制串表示，按等长编码格式构造一个字符编码表。发送方按照编码表对信息原文进行编码后送出电文，接收方对接收到的二进制代码按每5位一组进行分割，通过查字符的编码表即可得到对应字符，实现译码。

等长编码方案的实现方法比较简单，但对通信中的原文进行编码后，所得电文的码串过长，不利于提高通信效率，因此希望缩短码串的总长度。如果对每个字符设计长度不等的编码，且让电文中出现次数较多的字符采用尽可能短的编码，那么传送的电文码串总长度则可减少。

要设计长度不等的编码，必须满足下面的条件：任一字符的编码都不是另一个字符的编码的前缀，这种编码也称为前缀码。对给定的字符集D＝｛d_1
，d_2
，…，dn
｝及字符的使用频率W＝｛w_1
，w_2
，…，wn
｝，构造其最优前缀码的方法为：以d_1
，d_2
，…，dn
作为叶子节点，w_1
，w_2
，…，wn
作为叶子节点的权值，构造出一棵最优二叉树，然后将树中每个节点的左分支标上0，右分支标上1，则每个叶子节点代表的字符的编码就是从根到叶子的路径上的0、1组成的串。最优前缀编码方法如图8-24所示。

图8-24　前缀编码示例

【函数】从每个叶子节点出发追溯到树根，逆向找出最优二叉树中叶子节点的编码。

利用哈夫曼树译码的过程为：从根节点出发，按二进制位串中的0和1确定是进入左分支还是右分支，当到达叶子节点时译出一个字符。若位串未结束，则回到根节点继续上述译码过程。

【函数】用最优二叉树进行译码。

8.3.6　树和森林

1．树的存储结构

（1）树的双亲表示法：用一组地址连续的单元存储树的节点，并在每个节点中附设一个指示器，指出其双亲节点在该存储结构中的位置（节点所在数组元素的下标）。显然，这种表示对于求指定节点的双亲或祖先都十分方便，但对于求指定节点的孩子及后代则需要遍历整个数组，如图8-25所示。

（2）树的孩子表示法：在存储结构中用指针指示出节点的每个孩子，由于树中每个节点的子树数目不尽相同，因此在采用链式存储结构时可以考虑多重链表。因为每个节点的指针数目不好确定，对于定长的节点可依据树的度来设置节点中的指针，显然会造成极大的浪费；若设置节点中的指针数目不相等，则运算时又不方便，为此可以考虑为树中每个节点的孩子建立一个链表，即令每个节点的所有孩子节点构成一个用单链表表示的线性表，则n个节点的树具有n个单链表。将这n个单链表的头指针又排成一个线性表，如图8-26（a）所示。

也可以将双亲表示法和孩子表示法结合起来，形成树的双亲孩子表示结构，如图8-26（b）所示。

图8-25　树的双亲表示法示意图

图8-26　图8-25（a）中树的孩子表示法

（3）孩子兄弟表示法：又称为二叉链表表示法。在链表的节点中设置两个指针域分别指向该节点的第一个孩子和下一个兄弟，如图8-27所示。

树的孩子兄弟表示法为实现树、森林与二叉树之间的转换提供了基础，充分利用二叉树的有关算法来实现树及森林的操作，对难于把握规律的树和森林有着重要的现实意义。

图8-27　图8-25（a）中树的孩子兄弟表示法

2．树和森林的遍历

1）树的遍历

由于树中每个节点可以有两棵以上的子树，因此遍历树的方法有两种：先根遍历和后根遍历。

（1）树的先根遍历。树的先根遍历是先访问树的根节点，然后依次先根遍历根的各棵子树。对树的先序遍历等同于对转换所得的二叉树进行先序遍历。

（2）树的后根遍历。树的后根遍历是先依次后根遍历树根的各棵子树，然后访问树根节点。树的后根遍历等同于对转换所得的二叉树进行中序遍历。

2）森林的遍历

按照森林和树的相互递归定义，可以得出森林的两种遍历方法。

（1）先序遍历森林。若森林非空，访问森林中第一棵树的根节点，先序遍历第一棵子树根节点的子树森林，再先序遍历除第一棵树之外剩余的树所构成的森林。

（2）中序遍历森林。若森林非空，先中序遍历森林中第一棵树的子树森林，然后访问第一棵树的根节点，最后中序遍历除第一棵树之外剩余的树所构成的森林。

3．树、森林和二叉树之间的相互转换

树、森林和二叉树之间可以互相进行转换，即任何一个森林或一棵树可以对应一棵二叉树，而任一棵二叉树也能对应到一个森林或一棵树上。

（1）树、森林转换为二叉树。利用树的孩子兄弟表示法可导出树与二叉树的对应关系，在树的孩子兄弟表示法中，从物理结构上看与二叉树的二叉链表表示法相同，因此就可以用这种同一存储结构的不同解释将一棵树转换为一棵二叉树，如图8-28所示。一棵树可转换成唯一的一棵二叉树。

图8-28　树转换为二叉树

由于树根没有兄弟，所以树转换为二叉树后，二叉树的根一定没有右子树。这样，将一个森林转换为一棵二叉树的方法是：先将森林中的每一棵树转换为二叉树，再将第一棵树的根作为转换后的二叉树的根，第一棵树的左子树作为转换后二叉树根的左子树，第二棵树作为转换后二叉树的右子树，第三棵树作为转换后二叉树根的右子树的右子树，依此类推，森林就可以转换为一棵二叉树，如图8-29所示。

2）二叉树转换为树和森林。二叉树可转换为唯一的树或森林，如图8-30所示。

图8-29　森林转换为二叉树

图8-30　二叉树转换为树（或森林）

8.4　图

图是比树结构更复杂的一种数据结构。在线性结构中，除首节点没有前驱、末尾节点没有后继之外，一个节点只有唯一的一个直接前驱和唯一的一个直接后继。在树结构中，可认为除根节点没有前驱节点外，其余的每个节点只有唯一的一个前驱（双亲）节点和多个后继（子树）节点。而在图结构中，任意两个节点之间都可能有直接的关系，所以图中一个节点的前驱节点和后继节点的数目是没有限制的。

8.4.1　图的定义与存储

1．图的定义

图G是由集合V和E构成的二元组，记作G＝（V，E），其中V是图中顶点的非空有限集合，E是图中边的有限集合。从数据结构的逻辑关系角度来看，图中任一顶点都有可能与其他顶点有关系，而图中所有顶点都有可能与某一顶点有关系。在图中，数据结构中的数据元素用顶点表示，数据元素之间的关系用边表示。

（1）有向图。若图中每条边都是有方向的，那么顶点之间的关系用＜vi
，vj
＞表示，它说明从vi
到vj
有一条有向边（也称为弧）。vi
是有向边的起点，称为弧尾；vj
是有向边的终点，称为弧头。所有边都有方向的图称为有向图。

（2）无向图。若图中的每条边都是无方向的，顶点vi
和vj
之间的边用（vi
，vj
）表示。因此，在有向图中＜vi
，vj
＞与＜vj
，vi
＞分别表示两条边，而在无向图中（vi
，vj
）与（vj
，vi
）表示的是同一条边。

（3）完全图。若一个无向图具有n个顶点，而每一个顶点与其他n－1个顶点之间都有边，则称之为无向完全图。显然，含有n个顶点的无向完全图共有
条边。类似地，有n个顶点的有向完全图中弧的数目为n（n－1），即任意两个不同顶点之间都有方向相反的两条弧存在。

（4）度、出度和入度。顶点v的度是指关联于该顶点的边的数目，记作D（v）。若G为有向图，顶点的度表示该顶点的入度和出度之和。顶点的入度是以该顶点为终点的有向边的数目，而顶点的出度指以该顶点为起点的有向边的数目，分别记为ID（v）和OD（v）。无论是有向图还是无向图，顶点数n、边数e与各顶点的度之间有以下关系

（5）路径。在无向图G中，从顶点vp
到顶点vq
的路径是指存在一个顶点序列vp
，vi1
，vi2
，…，vin
，vq
，使得（vp
，vi1
），（vi1
，vi2
），…，（vin
，vq
）均属于E（G）。若G是有向图，其路径也是有方向的，它由E（G）中的有向边＜vp
，vi1
＞，＜vi1
，vi2
＞，…，＜vin
，vq
＞组成。路径长度是路径上边或弧的数目。第一个顶点和最后一个顶点相同的路径称为回路或环。若一条路径上除了vp
和vq
可以相同外，其余顶点均不相同，则称其为简单路径。

（6）子图。若有两个图G＝（V，E）和G′＝（V′，E′），如果V′⊆V且E′⊆E，则称G′为G的子图。

（7）连通图与连通分量。在无向图G中，若从顶点vi
到顶点vj
有路径，则称顶点vi
和顶点vj
是连通的。如果无向图G中任意两个顶点都是连通的，则称其为连通图。无向图G的极大连通子图称为G的连通分量。

（8）强连通图与强连通分量。在有向图G中，如果对于每一对顶点vi
，vj
∈V且vi
≠vj
，从顶点vi
到顶点vj
和从顶点vj
到顶点vi
都存在路径，则称图G为强连通图。有向图中的极大连通子图称为有向图的强连通分量。

（9）网。边（或弧）带权值的图称为网。

（10）有向树。如果一个有向图恰有一个顶点的入度为0，其余顶点的入度均为1，则是一棵有向树。

从图的逻辑结构的定义来看，图中的顶点之间不存在全序关系（即无法将图中的顶点排列成一个线性序列），任何一个顶点都可被看成第一个顶点；另一方面，任一顶点的邻接点之间也不存在次序关系。为了便于运算，给图中每个顶点赋予一个序号值。

2．图的存储结构

图的基本存储结构有邻接矩阵表示法和邻接链表表示法两种。

1）邻接矩阵表示法

图的邻接矩阵表示利用一个矩阵来表示图中顶点之间的关系。对于具有n个顶点的图G＝（V，E），其邻接矩阵是一个n阶方阵，且满足：

图8-31　所示的有向图和无向图的邻接矩阵分别为A和B。

图8-31　有向图和无向图及其邻接矩阵

由邻接矩阵的定义可知，无向图的邻接矩阵是对称的，有向图的邻接矩阵则不一定对称。借助于邻接矩阵容易判定任意两个顶点之间是否有边（或弧）相连，并且容易求得各个顶点的度。对于无向图，顶点vi
的度是邻接矩阵第i行（或列）中值不为0的元素个数；对于有向图，第i行（或列）中值不为0的元素个数是顶点vi
的出度OD（vi
），第j列的非0元素个数是顶点vj
的入度ID（vj
）。

网（赋权图）的邻接矩阵可定义为：

其中，Wij
是边上的权值。

图8-32所示的是网及其邻接矩阵C。

图8-32　一个网及其邻接矩阵表示

若用邻接矩阵表示图，则对应的数据类型可定义为：

或

2）邻接链表表示法

邻接链表指的是为图的每个顶点建立一个单链表，第i个单链表中的节点表示依附于顶点vi
的边（对于有向图是以vi
为尾的弧）。邻接链表中的节点有表节点和表头节点两种类型，如下所示。

表节点

表头节点

其含义如下。

●　adjvex：指示与顶点vi
邻接的顶点的序号。

●　nextarc：指示下一条边或弧的节点。

●　info：存储与边或弧有关的信息，如权值等。

●　data：存储顶点vi
的名或其他有关信息。

●　firstarc：指示链表中的第一个节点（邻接顶点）。

这些表头节点通常以顺序的形式存储，以便随机访问任一顶点的邻接链表。若图用邻接链表来表示，则对应的数据类型可定义如下：

显然，对于有n个顶点、e条边的无向图来说，其邻接链表需用n个头节点和2e个表节点。

对于无向图的邻接链表，顶点vi
的度恰为第i个邻接链表中表节点的数目，而在有向图中，为求顶点的入度，必须扫描逐个邻接表，这是因为第i个邻接链表中表节点的数目只是顶点vi
的出度。为此，可以建立一个有向图的逆邻接链表。有向图的邻接表和逆邻接表如图8-33所示。

图8-33　一个有向图及其邻接表和逆邻接表表示

8.4.2　图的遍历

与树的遍历相似，图的遍历也是从某个顶点出发，沿着某条搜索路径对图中所有顶点进行访问且只访问一次。图的遍历算法是求解图的连通性问题、拓扑排序及求关键路径等算法的基础。

图的遍历要比树的遍历复杂得多。因为图的任一个节点都可能与其余顶点相邻接，所以在访问了某个顶点之后，可能沿着某路径又回到该节点上，为了避免顶点的重复访问，在图的遍历过程中，必须记下每个已访问过的顶点。深度优先遍历和广度优先遍历是两种遍历图的基本方法。

1．深度优先搜索（Depth First Search，DFS）

此种方法类似于树的先根遍历，在第一次经过一个顶点时就进行访问操作。从图G中任一节点v出发按深度优先搜索法进行遍历的步骤如下。

（1）设置搜索指针p，使p指向顶点v；

（2）访问p所指顶点，并使p指向与其相邻接的且尚未被访问过的顶点。

（3）若p所指顶点存在，则重复步骤（2），否则执行步骤（4）。

（4）沿着刚才访问的次序和方向回溯到一个尚有邻接顶点且未被访问过的顶点，并使p指向这个未被访问的顶点，然后重复步骤（2），直至所有的顶点均被访问为止。

该算法的特点是尽可能先对纵深方向搜索，因此可以得到其递归遍历算法。

【函数】以邻接链表表示图的深度优先搜索算法。

从函数Dfs（）之外调用Dfs可以访问到所有与一个指定顶点有路径相通的其他顶点。若图是不连通的，则下一次应从另一个未被访问过的顶点出发，再次调用Dfs进行遍历，直到将图中所有的顶点都访问到为止。深度优先的搜索过程如图8-34所示。

深度优先遍历图的过程实质上是对某个顶点查找其邻接点的过程，其耗费的时间取决于所采用的存储结构。当图用邻接矩阵表示时，查找所有顶点的邻接点所需时间为Ο（n^2
）。若以邻接表作为图的存储结构，则需要Ο（e）的时间复杂度查找所有顶点的邻接点。因此，当以邻接表作为存储结构时，深度优先搜索遍历图的时间复杂度为Ο（n＋e）。

2．广度优先搜索（Breadth First Search，BFS）

图的广度优先搜索方法为：从图中某个顶点v出发，在访问了v之后依次访问v的各个未被访问过的邻接点，然后分别从这些邻接点出发依次访问它们的邻接点，并使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问，直至图中所有已被访问的顶点的邻接点都被访问到。若此时还有未被访问的顶点，则另选图中的一个未被访问的顶点作为起点，重复上述过程，直至图中所有的顶点都被访问到为止。对图8-35所示的图进行广度优先搜索，得到的序列为1，2，3，4，5，6。

图8-34　深度优先搜索遍历过程

图8-35　一个不连通的无向图

广度优先遍历图的特点是尽可能先进行横向搜索，即最先访问的顶点的邻接点也先被访问。为此，引入队列来保存已访问过的顶点序列，即每当一个顶点被访问后，就将其放入队中，当队头顶点出队时，就访问其未被访问的邻接点并令这些邻接顶点入队。

【算法】以邻接链表表示图的广度优先搜索算法。

在广度优先遍历算法中，每个顶点至多进一次队列。

遍历图的过程实质上是通过边或弧找邻接点的过程，因此广度优先搜索遍历图和深度优先搜索遍历图的时间复杂度相同，其不同之处仅仅在于对顶点访问的次序不同。

8.4.3　生成树及最小生成树

1．生成树的概念

一个连通图的生成树是一个极小连通子图，它包含图中的全部顶点，但只有构成一棵树的n－1条边。图8-36所示的是图及其生成树和非生成树。

图8-36　一个无向图的生成树和非生成树

按深度和广度优先搜索进行遍历将得到不同的生成树，分别称为深度优先生成树和广度优先生成树。例如，图8-37所示的是图的一棵深度优先生成树和一棵广度优先生成树。

对于有n个顶点的连通图，至少有n－1条边，而生成树中恰好有n－1条边，所以连通图的生成树是该图的极小连通子图。若在图的生成树中任意加一条边，则必然形成回路。

图8-37　图的搜索生成树

图的生成树不是唯一的。从不同的顶点出发，选择不同的存储方式，可以得到不同的生成树。对于非连通图而言，每个连通分量中的顶点集和遍历时走过的边集一起构成若干棵生成树，把它们称为非连通图的生成树森林。

2．最小生成树

一个图的生成树不唯一，从不同的顶点出发可得到不同的生成树。对于连通网来说，边是带权值的，生成树的各边也带权值，因此把生成树各边的权值总和称为生成树的权，把权值最小的生成树称为最小生成树。求解最小生成树有许多实际的应用。

常用的最小生成树算法有普里姆（Prim）算法和克鲁斯卡尔（Kruskal）算法。

（1）普里姆（Prim）算法。

假设N＝（V，E）是连通网，TE是N上最小生成树中边的集合。算法从顶点集合U＝｛u_0
｝（u_0
∈V）、边的集合TE＝｛｝开始，重复执行下述操作：在所有u∈U，v∈V－U的边（u，v）∈E中找一条代价最小的边（u_0
，v_0
），把这条边并入集合TE，同时将v_0
并入集合U，直至U＝V时为止。此时TE中必有n－1条边，T＝（V，｛TE｝）为N的最小生成树。

由此可知，普里姆算法构造最小生成树的过程是以一个顶点集合U＝｛u_0
｝作初态，不断寻找与U中顶点相邻且代价最小的边的另一个顶点，扩充U集合直至U＝V时为止。

用普里姆算法构造最小生成树的过程如图8-38所示。

图8-38　普里姆算法构造最小生成树的过程

普里姆算法的时间复杂度为Ο（n^2
），与图中的边数无关，因此该算法适合于求边稠密的网的最小生成树。

（2）克鲁斯卡尔（Kruskal）算法。

克鲁斯卡尔求最小生成树的算法思想为：假设连通网N＝（V，E），令最小生成树的初始状态为只有n个顶点而无边的非连通图T＝（V，｛｝），图中每个顶点自成一个连通分量。在E中选择代价最小的边，若该边依附的顶点落在T中不同的连通分量上，则将此边加入到T中，否则舍去此边而选择下一条代价最小的边。依此类推，直至T中所有顶点都在同一连通分量上为止。

用克鲁斯卡尔算法构造最小生成树的过程如图8-39所示。

图8-39克　斯卡尔算法构造最小生成树的过程

克鲁斯卡尔算法的时间复杂度为Ο（eloge），与图中的顶点数无关，因此该算法适合于求边稀疏的网的最小生成树。

8.4.4　拓扑排序和关键路径

1．AOV网

在工程领域，一个大的工程项目通常被划分为许多较小的子工程（称为活动）。显然，当这些子工程都完成时，整个工程也就完成了。在有向图中，若以顶点表示活动，用有向边表示活动之间的优先关系，则称这样的有向图为以顶点表示活动的网（Activity On Vertex network，AOV网）。在网中，若从顶点vi
到顶点vj
有一条有向路径，则顶点vi
是vj
的前驱，顶点vj
是vi
的后继。若＜vi
，vj
＞是网中的一条弧，则顶点vi
是vj
的直接前驱，顶点vj
是vi
的直接后继。AOV网中的弧表示了活动之间的优先关系，也可以说是一种活动进行时的制约关系。

在AOV网中不应出现有向环，若存在的话，则意味着某项活动必须以自身任务的完成为先决条件，显然这是荒谬的。因此，若要检测一个工程是否可行，首先就应检查对应的AOV网是否存在回路。不存在回路的AOV网称为有向无环图，或DAG（Directed Acycline Graph）图。检测的方法是对有向图构造其顶点的拓扑有序序列。若图中所有顶点都在它的拓扑有序序列中，则该AOV网中必定不存在环。

2．拓扑排序及其算法

拓扑排序是将AOV网中所有顶点排成一个线性序列的过程，并且该序列满足：若在AOV网中从顶点vi
到vj
有一条路径，则在该线性序列中，顶点vi
必然在顶点vj
之前。

拓扑排序即指对AOV网构造拓扑序列的操作。一般情况下，假设AOV图代表一个工程计划，则AOV网的一个拓扑排序就是一个工程顺利完成的可行方案。

对AOV网进行拓扑排序的方法如下。

（1）在AOV网中选择一个入度为0（没有前驱）的顶点且输出它。

（2）从网中删除该顶点及其与该顶点有关的所有边。

（3）重复上述两步，直至网中不存在入度为0的顶点为止。

执行的结果会有两种情况：一种是所有顶点已输出，此时整个拓扑排序完成，说明网中不存在回路；另一种是尚有未输出的顶点，剩余的顶点均有前驱顶点，表明网中存在回路，拓扑排序无法进行下去。图8-40所示的是拓扑排序过程，得到的拓扑序列为6，1，4，3，2，5。

图8-40　拓扑排序过程

当有向图中无环时，也可以利用深度优先遍历进行逆拓扑排序。由于图中无环，从图中某点出发进行深度优先搜索遍历时，最先退出DFS函数的顶点即是出度为0的顶点，它是拓扑有序序列中最后的一个顶点。由此，按退出DFS函数的先后记录下来的顶点序列即为逆向的拓扑有序序列。拓扑排序算法的时间复杂度为Ο（n＋e）。

3．AOE网

若在带权有向图G中以顶点表示事件，以有向边表示活动，边上的权值表示该活动持续的时间，则这种带权有向图称为用边表示活动的网（Activity On Edge network，AOE网）。通常在AOE网中列出了完成预定工程计划所需进行的活动、每项活动的计划完成时间、要发生哪些事件以及这些事件和活动间的关系，从而可以分析该项工程是否实际可行并估计工程完成的最短时间，以及影响工程进度的关键活动；进一步可以进行人力、物力的调度和分配，以达到缩短工期的目的。

用AOE网表示一项工程计划时，顶点所表示的事件实际上就是某些活动已经完成、某些子活动可以动工的标志。具体地说，顶点所表示的事件是指该顶点所有进入边所表示的活动均已完成、从它出发的边所表示的活动均可以开始的一种状态。

一般情况下，每项工程都有一个开始状态和一个结束状态，所以在AOE网中至少有一个入度为0的开始顶点，称为源点。另外，应有一个出度为0的结束顶点，称为汇点。AOE网中不应存在有向回路，否则整个工程无法完成。

与AOV网不同，AOE网所关心的问题如下。

（1）完成该工程至少需要多少时间？

（2）哪些活动是影响整个工程进度的关键？

由于AOE网中的某些活动能够并行地进行，因此完成整个工程所需的时间是从开始顶点到结束顶点的最长路径的长度。这里的路径长度是指该路径上的权值之和。

4．关键路径和关键活动

从源点到汇点的路径中，长度最长的路径称为关键路径。关键路径上的所有活动均是关键活动。如果任何一项关键活动没有按期完成，则会影响整个工程的进度，而提高关键活动的速度通常可以缩短整个工程的工期。假设在n个顶点的AOE网中，顶点v_0
表示源点、顶点vn－1
表示汇点，则引入顶点事件的最早、最晚发生时间，活动的最早、最晚开始时间等术语。

（1）顶点事件的最早发生时间ve（j）。ve（j）是指从源点v_0
到vj
的最长路径长度（时间）。这个时间决定了所有从vj
发出的弧所表示的活动能够开工的最早时间。

ve（j）计算方法为

其中，T是所有到达顶点j的弧的集合，dut（＜i，j＞）是弧＜i，j＞上的权值，n是网中的顶点数，如图8-41（a）所示。

显然，上式是一个从源点开始的递推公式。必须在vj
的所有前驱顶点事件的最早发生时间全部得出后才能计算ve（j）。这样必须对AOE网进行拓扑排序，然后按拓扑有序序列逐个求出各顶点事件的最早发生时间。

（2）顶点事件的最晚发生时间vl（i）。vl（i）是指在不推迟整个工期的前提下，事件vi
的最晚发生时间。对一个工程来说，计划用几天时间完成是可以从AOE网求得的，其数值就是汇点vn－1
的最早发生时间ve（n－1），而这个时间也就是vl（n－1）。其他顶点事件的vl应从汇点开始，逐步向源点方向递推才能求得，所以vl（i）的计算公式为

其中，S是所有从顶点i发出的弧的集合，如图8-41（b）所示。

图8-41　推导事件的最早、最晚发生时间示意图

显然，必须在顶点vi
的所有后继顶点事件的最晚发生时间全部得出后才能计算vl（i）。这样必须对AOE网逆拓扑排序，由逆拓扑序列递推计算各顶点的vl值。

（3）活动ak
的最早开始时间e（k）。e（k）是指弧＜i，j＞所表示的活动ak
最早可开工时间。

这说明活动ak
的最早开始时间等于事件vi
的最早发生时间。

（4）活动ak
的最晚开始时间l（k）。l（k）是指在不推迟整个工期的前提下，该活动的最晚开始时间。若活动ak
由弧＜i，j＞表示，则

对于活动ak
来说，若e（k）＝l（k），则表示活动ak
是关键活动，它说明该活动最早可开工时间与整个工程计划允许该活动最晚的开工时间一致，施工期一点也不能拖延。若活动ak
不能按期完成，则工程将延期；若活动ak
提前完成，则可能使整个工程提前完工。

由关键活动组成的路径是关键路径。依照上述计算关键活动的方法，就可形成求AOE网的关键路径。

8.4.5　最短路径

1．单源点最短路径

所谓单源点最短路径，是指给定带权有向图G和源点v_0
，求从v_0
到G中其余各顶点的最短路径。迪杰斯特拉（Dijkstra）提出了按路径长度递增的次序产生最短路径的算法，其思想是：把网中所有的顶点分成两个集合S和T，S集合的初态只包含顶点v_0
，T集合的初态为网中除v_0
之外的所有顶点。凡以v_0
为源点，已经确定了最短路径的终点并入S集合中，顶点集合T则是尚未确定最短路径的顶点的集合。按各顶点与v_0
间最短路径长度递增的次序，逐个把T集合中的顶点加入到S集合中去，使得从v_0
到S集合中各顶点的路径长度始终不大于从v_0
到T集合中各顶点的路径长度。

为了能方便地求出从v_0
到T集合中各顶点最短路径的递增次序，引入一个辅助向量dist。它的某一个分量dist[i]表示当前求出的从v_0
到终点vi
的最短路径长度。这个路径长度并不一定是真正的最短路径长度。它的初始状态为：若从v_0
到vi
有弧，则dist[i]为弧上的权值；否则，置dist[i]为∞。显然，长度为dist[u]＝min｛dist[i]｜vi
∈V（G）｝的路径就是从v_0
出发的长度最短的一条最短路径。此路径为（v_0
，u），这时顶点u应从集合T中删除，将其并入集合S。

设网用邻接矩阵arcs存储，那么每次选出一个顶点u并使之并入集合S后，就根据情况修改T集合中各顶点的路径长度dist。对于T集合中的某一个顶点i来说，其更短路径可能为（v_0
，…，vu
，vi
）。也就是说，若dist[u]＋ares[u][i]＜dist[i]，则修改dist[i]，使dist[i]＝dist[u]＋ares[u][i]。

对T集合中各顶点的dist进行修改后，再从中挑选出一个路径长度最小的顶点，从T集合中删除并将其并入S集合。依此类推，就能求出源点到其余各顶点的最短路径长度。

对于图8-42所示的有向网，用迪杰斯特拉算法求解顶点V_0
到达其余顶点的最短路径的过程如表8-1所示。

图8-42　有向网G及其邻接矩阵

表8-1　迪杰斯特拉算法求解图8-46中顶点V_0
到V_1
、V_2
、V_3
、V_4
、V_5
最短路径的过程

2．每对顶点间的最短路径

若每次以一个顶点为源点，重复执行迪杰斯特拉算法n次，便可求得网中每一对顶点之间的最短路径。下面介绍弗洛伊德（Floyd）提出的求最短路径的算法，该算法在形式上要简单一些。

弗洛伊德算法思想是：假设图采用邻接矩阵的方式存储，需要求从顶点vi
到vj
的最短路径。arcs[i][j]表示弧（vi
，vj
）的权值，若此弧不存在，则权值为区别于有效权值的一个数。如果存在vi
到vj
的弧，则从vi
到vj
存在一条长度为arcs[i][j]的路径，该路径不一定是最短路径，尚需进行n次试探。首先考虑路径（vi
，v_0
，vj
）是否存在（即判别路径（vi
，v_0
）和（v_0
，vj
）是否存在），若存在，则比较（vi
，vj
）与（vi
，v_0
，vj
）的路径长度，取较短者为从vi
到vj
的中间顶点的序号不大于0的最短路径。假如在路径上再增加一个顶点v_1
，也就是说，如果（vi
，…，v_1
）和（v_1
，…，vj
）分别是当前找到的中间顶点的序号不大于0的vi
到v_1
以及v_1
到vj
的最短路径，那么（vi
，…v_1
，…，vj
）就有可能是从vi
到vj
的中间顶点的序号不大于1的最短路径。将它与已经得到的从vi
到vj
的中间顶点的序号不大于0的最短路径相比较，从中选出中间顶点的序号不大于1的最短路径之后，再增加一个顶点v_2
继续进行试探，依此类推。在一般情况下，若（vi
，…，vk
）和（vk
，…，vj
）分别是从vi
到vk
和vk
到vj
的中间顶点的序号不大于k－1的最短路径，则将（vi
，…，vk
，…，vj
）与已经得到的从vi
到vj
的中间顶点的序号不大于k－1的最短路径相比较，长度较短者便是从vi
到vj
的中间顶点的序号不大于k的最短路径。这样，在经过n次试探后，最后求得的必是从vi
到vj
的最短路径。按此方法，可以同时求得各对顶点间的最短路径。

8.5　查找

8.5.1　查找的基本概念

1．基本概念

查找是一种常用的基本运算。查找表是指由同一类型的数据元素（或记录）构成的集合。由于“集合”这种数据结构中的数据元素之间存在着完全松散的关系，因此，查找表是一种非常灵活的数据结构。

对查找表经常要进行的两种操作如下。

（1）查询某个“特定”的数据元素是否在查找表中。

（2）检索某个“特定”的数据元素的各种属性。

通常将只进行这两种操作的查找表称为静态查找表。

对查找表经常要进行的另外两种操作如下。

（1）在查找表中插入一个数据元素。

（2）从查找表中删除一个数据元素。

若在查找过程中同时插入查找表中不存在的数据元素，或者从查找表中删除已存在的某个数据元素，则称此类查找为动态查找表。

关键字是数据元素（或记录）的某个数据项的值，用它来识别（标识）这个数据元素。主关键字是指能唯一标识一个数据元素的关键字。次关键字是指能标识多个数据元素的关键字。

根据给定的某个值，在查找表中确定是否存在一个其关键字等于给定值的记录或数据元素。若表中存在这样的一个记录，则称查找成功，此时给出整个记录的信息，或者指出记录在查找表中的位置；若表中不存在关键字等于给定值的记录，则称查找不成功，此时的查找结果用一个“空”记录或“空”指针表示。

2．平均查找长度

对于查找算法来说，其基本操作是“将记录的关键字与给定值进行比较”。因此，通常以“其关键字和给定值进行过比较的记录个数的平均值”作为衡量查找算法好坏的依据。

为确定记录在查找表中的位置，需和给定关键字值进行比较的次数的期望值称为查找算法在查找成功时的平均查找长度。

对于含有n个记录的表，查找成功时的平均查找长度定义为

其中，Pi
为对表中第i个记录进行查找的概率，且
一般情况下，均认为查找每个记录的概率是相等的，即Pi
＝1/n；Ci
为找到表中其关键字与给定值相等的记录时（为第i个记录），和给定值已进行过比较的关键字个数，显然，Ci
随查找方法的不同而不同。

8.5.2　静态查找表的查找方法

1．顺序查找

顺序查找的基本思想是：从表中的一端开始，逐个进行记录的关键字和给定值的比较，若找到一个记录的关键字与给定值相等，则查找成功；若整个表中的记录均比较过，仍未找到关键字等于给定值的记录，则查找失败。

顺序查找的方法对于顺序存储方式和链式存储方式的查找表都适用。

从顺序查找的过程可见，Ci
取决于所查记录在表中的位置。若需查找的记录正好是表中的第一个记录时，仅需比较一次；若查找成功时找到的是表中的最后一个记录，则需比较n次。从表尾开始查找时则正好相反，一般情况下，Ci
＝n－i＋1，因此在等概率情况下，顺序查找成功的平均查找长度为

也就是说，成功查找的平均比较次数约为表长的一半。若所查记录不在表中，则必须进行n次（不设监视哨，设置监视哨时为n＋1次）比较才能确定失败。监视哨是指查找表用一维数组存储时，将待查找的记录放置在查找表的第一个记录之前或最后一个记录之后，从而在查找过程中避免对数组元素的下标合法性进行检查。

与其他查找方法相比，顺序查找方法在n值较大时，其平均查找长度较大，查找效率较低。但这种方法也有优点，那就是算法简单且适应面广，对查找表的结构没有要求，无论记录是否按关键字有序排列均可应用。

2．折半查找

设查找表的元素存储在一维数组r[1..n]中，在表中的元素已经按关键字递增（或递减）方式排序的情况下，进行折半查找的方法是：首先将待查元素的关键字（key）值与表r中间位置上（下标为mid）记录的关键字进行比较，若相等，则查找成功；若key>r[mid].key，则说明待查记录只可能在后半个子表r[mid+1..n]中，下一步应在后半个子表中进行查找，若key>r[mid].key，说明待查记录只可能在前半个子表r[1..mid-1]中，下一步应在r的前半个子表中进行查找，这样通过逐步缩小范围，直到查找成功或子表为空时失败为止。

【函数】设有一个整型数组中的元素是按非递减的方式排列的，在其中进行折半查找的算法为：

【函数】设有一个整型数组中的元素是按非递减的方式排列的，在其中进行折半查找的递归算法如下：

折半查找的性能分析如下。

折半查找的过程可以用一棵二叉树描述，方法是以当前查找区间的中间位置序号作为根，左半个子表和右半个子表中的记录序号分别作为根的左子树和右子树上的节点，这样构造的二叉树称为折半查找判定树。例如，具有11个节点的折半查找判定树如图8-43所示。

从折半查找判定树可以看出，查找成功时，折半查找的过程恰好走了一条从根节点到被查找节点的路径，与关键字进行比较的次数即为被查找节点在树中的层数。因此，折半查找在查找成功时进行比较的关键字数最多不超过树的深度，而具有n个节点的判定树的深度为
，所以折半查找在查找成功时和给定值进行比较的关键字个数至多为
。

给判定树中所有节点的空指针域加一个指向方型节点的指针，称这些方型节点为判定树的外部节点（与之相对，称那些圆形节点为内部节点），如图8-44所示。那么折半查找不成功的过程就是走了一条从根节点到外部节点的路径。与给定值进行比较的关键字个数等于该路径上内部节点个数，因此折半查找在查找不成功时和给定值进行比较的关键字个数最多也不会超过
。

图8-43　具有11个节点的折半查找判定树

图8-44　加上外部节点的判定树

那么折半查找的平均查找长度是多少呢？为了方便起见，不妨设节点总数为n＝2h
－1，则判定树是深度为h＝log_2
（n＋1）的满二叉树。在等概率情况下，折半查找的平均查找长度为

当n值较大时，ASLbs
≈log_2
（n＋1）－1。

折半查找比顺序查找的效率要高，但它要求查找表进行顺序存储并且按关键字有序排列。因此，当需要对表进行元素的插入或删除操作时，需要移动大量的元素。所以折半查找适用于表不易变动，且又经常进行查找的情况。

3．分块查找

分块查找又称索引顺序查找，是对顺序查找方法的一种改进，其效率介于顺序查找与折半查找之间。

在分块查找过程中，首先将表分成若干块，每一块的关键字不一定有序，但块之间是有序的，即后一块中所有记录的关键字均大于前一个块中最大的关键字。此外，还建立了一个“索引表”，索引表按关键字有序，如图8-45所示。

图8-45　表及其索引表

因此，分块查找过程分为两步：第一步在索引表中确定待查记录所在的块；第二步在块内顺序查找。

由于分块查找实际上是两次查找的过程，因此其平均查找长度应该是两次查找的平均查找长度（块内查找与索引查找）之和，即

其中，Lb
为查找索引表的平均查找长度，Lw
为块内查找时的平均查找长度。

分块查找时，可将长度为n的表均匀地分成b块，每块含有s个记录，既有
在等概率查找的情况下，块内查找的概率为
每块的查找概率为
若用顺序查找确定元素所在的块，则分块查找的平均查找长度为

可见，其平均查找长度不仅与表长n有关，而且与每一块中的记录数s有关。可以证明，当s取
时，ASLbs
取最小值
此时的查找效率较顺序查找要好得多，但远不及折半查找。考虑到索引表是一个有序表，因此可以用折半查找确定元素所在的块。

8.5.3　动态查找表

动态查找表的特点是表结构本身是在查找过程中动态生成的，即对于给定值key，若表中存在关键字等于key的记录，则查找成功返回；否则插入关键字为key的记录。

1．二叉排序树

1）二叉排序树的定义

二叉排序树又称二叉查找树，它或者是一棵空树，或者是具有如下性质的二叉树。

（1）若它的左子树非空，则左子树上所有节点的值均小于根节点的值。

（2）若它的右子树非空，则右子树上所有节点的值均大于根节点的值。

（3）左、右子树本身是二叉排序树。

图8-46　为二叉排序树的示意图。

2）二叉排序树的查找过程

因为二叉排序树中左子树上所有节点的关键字均小于根节点的关键字，右子树上所有节点的关键字均大于根节点的关键字，所以在二叉排序树上进行查找的过程为：二叉排序树非空时，将给定值与根节点的关键字值相比较，若相等，则查找成功；若不等，当根节点的关键字值大于给定值时，下一步到根的左子树中进行查找，否则到根的右子树中进行查找。若查找成功，则查找过程是走了一条从树根到所找到节点的路径；否则，查找过程终止于一棵空的子树。

图8-46　二叉排序树示意图

设二叉排序树采用二叉链表存储，节点的类型定义如下：

【函数】二叉排序树的查找算法。

3）二叉排序树中插入节点的操作

二叉排序树是通过依次输入数据元素并把它们插入到二叉树的适当位置上构造起来的，具体的过程是：每读入一个元素，建立一个新节点。若二叉排序树非空，则将新节点的值与根节点的值相比较，如果小于根节点的值，则插入到左子树中，否则插入到右子树中；若二叉排序树为空，则新节点作为二叉排序树的根节点。设关键字序列为｛46，25，54，13，29，91｝，则整个二叉排序树的构造过程如图8-47所示。

图8-47　二叉排序树的构造过程

【函数】二叉排序树的插入算法。

从上面的插入过程还可以看到，每次插入的新节点都是二叉排序树上新的叶子节点，因此插入节点时不必移动其他节点，仅需改动某个节点的孩子指针。这就相当于在一个有序序列上插入一个记录而不需要移动其他记录。它表明，二叉排序树具有类似于折半查找的特性，可采用链表存储结构，因此是动态查找表的一种适宜表示。

另外，由于一棵二叉排序树的形态完全由输入序列决定，所以在输入序列已经有序的情况下，所构造的二叉排序树是一棵单枝树。例如，对于关键字序列（12，18，23，45，60），建立的二叉排序树如图8-48所示，这种情况下的查找效率与顺序查找的效率相同。

图8-48　由关键字序列（12，18，23，45，60）创建的二叉排序树

4）二叉排序树中删除节点的操作

在二叉排序树中删除一个节点，不能把以该节点为根的子树都删除，只能删除这个节点并仍旧保持二叉排序树的特性。也就是说，删除二叉排序树上一个节点相当于删除有序序列中的一个元素。

假设在二叉排序树删除节点*p（p指向被删除节点），*f为其双亲节点，则该操作可分为三种情况：*p节点为叶子节点；*p节点只有左子树或者只有右子树；*p节点的左子树、右子树均存在。

（1）若*p节点为叶子节点，即p->lchild及p->rchild均为空，则由于删去叶子节点后不破坏整棵树的结构，因此只需修改*p节点的双亲节点*f的相应指针即可。

（2）若*p节点只有左子树或者只有右子树，此时只要将*p的左子树或右子树接成其双亲节点*f的左子树（或右子树），即令

或

（3）若*p节点的左、右子树均不空，则删除*p节点时应将其左子树、右子树连接到适当的位置，并保持二叉排序树的特性。可采用如下两种方法进行处理：一是令*p的左子树为*f的左子树（若*p是*f的左子树，否则为右子树），而将*p的右子树下接到*p的中序遍历的直接前驱节点*s（*s节点是*p的左子树中最右下方的节点）的右孩子指针上；二是用*p的中序直接前驱（或后继）节点*s代替*p节点，然后删除*s节点。如图8-49所示。

图8-49　删除二叉排序树中具有两个子树的节点示意图

从二叉排序树的定义可知，中序遍历二叉排序树可得到一个关键字有序的序列。这也说明，一个无序序列可以通过构造一棵二叉排序树而变成一个有序序列，构造树的过程就是对无序序列进行排序的过程。

2．平衡二叉树

平衡二叉树又称为AVL树，它或者是一棵空树，或者是具有下列性质的二叉树。它的左子树和右子树都是平衡二叉树，且左子树和右子树的高度之差的绝对值不超过1。若将二叉树节点的平衡因子（Balance Factor，BF）定义为该节点左子树的高度减去其右子树的高度，则平衡二叉树上所有节点平衡因子只可能是-1、0和1。只要树上有一个节点的平衡因子的绝对值大于1，则该二叉树就是不平衡的。

分析二叉排序树的查找过程可知，只有在树的形态比较均匀的情况下，查找效率才能达到最佳。因此，希望在构造二叉排序树的过程中，保持其为一棵平衡二叉树。

使二叉排序树保持平衡的基本思想是：每当在二叉排序树中插入一个节点时，首先检查是否因插入而破坏了平衡。若是，则找出其中的最小不平衡二叉树，在保持二叉排序树特性的情况下，调整最小不平衡子树中节点之间的关系，以达到新的平衡。所谓最小不平衡子树，是指离插入节点最近且以平衡因子的绝对值大于1的节点作为根的子树。

1）平衡二叉树上的插入操作

一般情况下，假设由于在二叉排序树上插入节点而失去平衡的最小子树根节点的指针为a，也就是说，a所指节点是离插入节点最近且平衡因子的绝对值超过1的祖先节点，那么，失去平衡后进行调整的规律可归纳为以下4种情况。

（1）单向右旋平衡处理。由于在*a的左子树的左子树上插入新节点，使*a的平衡因子由1增至2，导致以*a为根的子树失去平衡，因此需进行一次向右的顺时针旋转操作，如图8-50所示。

图8-50　单向右旋平衡处理示意图

（2）单向左旋平衡处理。由于在*a的右子树的右子树上插入新节点，使*a的平衡因子由-1变为-2，导致以*a为根的子树失去平衡，因此需进行一次向左的逆时针旋转操作，如图8-51所示。

图8-51　单向左旋平衡处理示意图

（3）先左后右双向旋转平衡处理。由于在*a的左子树的右子树上插入新节点，使*a的平衡因子由1增至2，导致以*a为根节点的子树失去平衡，因此需进行两次旋转（先左旋后右旋）操作，如图8-52所示。

（4）先右后左双向旋转平衡处理。由于在*a的右子树的左子树上插入新节点，使*a的平衡因子由-1变为-2，导致以*a为根节点的子树失去平衡，因此需进行两次旋转（先右旋后左旋）操作，如图8-53所示。

2）平衡二叉树上的删除操作

在平衡二叉树上进行删除操作比插入操作更复杂。若待删节点的两个子树都不为空，就用该节点左子树上的中序遍历的最后一个节点（或其右子树上的第一个节点）替换该节点，将情况转化为待删除的节点只有一个子树后再进行处理。当一个节点被删除后，从被删节点到树根的路径上所有节点的平衡因子都需要更新。对于每一个位于该路径上的平衡因子为±2的节点来说，都要进行平衡处理。

图8-52　先左后右双向平衡处理示意图

图8-53　先右后左双向平衡处理示意图

3．B_树

一棵m阶的B_树，或为空树，或为满足下列特性的m叉树。

（1）树中每个节点至多有m棵子树。

（2）若根节点不是叶子节点，则至少有两棵子树。

（3）除根之外的所有非终端节点至少有
棵子树。

（4）所有的非终端节点中包含下列数据信息

其中，Ki
（i＝1，2，…，n）为关键字，且Ki
＜Ki＋1
（i＝1，2，…，n－1）；Ai
（i＝0，1，…，n）为指向子树根节点的指针，且指针Ai－1
所指子树中所有节点的关键字均小于Ki
（i＝1，2，…，n），An
所指子树中所有节点的关键字均大于Kn
，
为节点中关键字的个数。

（5）所有的叶子节点都出现在同一层次上，并且不带信息（可以看作是外部节点或查找失败的节点，实际上这些节点不存在，指向这些节点的指针为空）。

一棵4阶的B_树如图8-54所示。

图8-54　4阶B_树示意图

由B_树的定义可知，在B_树上进行查找的过程是：首先在根节点所包含的关键字中查找给定的关键字，若找到则成功返回；否则确定待查找的关键字所在的子树并继续进行查找，直到查找成功或查找失败（指针为空）时为止。

B_树上的插入和删除运算较为复杂，因为要保证运算后节点中关键字的个数大于等于
因此涉及到节点的“分裂”及“合并”问题。

在B_树中插入一个关键字时，不是在树中增加一个叶子节点，而是首先在低层的某个非终端节点中添加一个关键字，若该节点中关键字的个数不超过m－1，则完成插入；否则，要进行节点的“分裂”处理。所谓“分裂”，就是把节点中处于中间位置上的关键字取出来插入到其父节点中，并以该关键字为分界线，把原节点分成两个节点。“分裂”过程可能会一直持续到树根。

同样，在B_树中删除一个节点时，首先找到关键字所在的节点，若该节点在含有信息的最后一层，且其中关键字的数目不少于
则完成删除；否则需进行节点的“合并”运算。若待删除的关键字所在节点不在含有信息的最后一层上，则将该关键字用其在B_树中的后继替代，然后再删除其后继元素，即将需要处理的情况统一转化为在含有信息的最后一层再进行删除运算。

8.5.4　哈希表

1．哈希表的定义

前面讨论的几种查找方法，由于记录在存储结构中的相对位置是随机的，所以查找时都要通过一系列的关键字的比较才能确定被查记录在表中的位置。也就是说，这类查找都是以关键字的比较为基础的，而哈希表则通过一个以记录的关键字为自变量的函数（称为哈希函数）得到该记录的存储地址查找，所以在哈希表中进行查找操作时，须用同一哈希函数计算得到待查记录的存储地址，然后到相应的存储单元去获得有关信息再判定查找是否成功。

根据设定的哈希函数H（key）和处理冲突的方法，将一组关键字映射到一个有限的连续的地址集（区间）上，并以关键字在地址集中的“像”作为记录在表中的存储位置，这种表称为哈希表，这一映射过程称为哈希造表或散列，所得的存储位置称为哈希地址或散列地址。

对于某个哈希函数H和两个关键字K_1
和K_2
，如果K_1
≠K_2
，而H（K_1
）＝H（K_2
），则称为冲突。具有相同哈希函数值的关键字对该哈希函数来说称为同义词。

一般情况下，冲突只能尽可能减少而不能完全避免，因为哈希函数是从关键字集合到地址集合的映像。通常关键字集合比较大，它的元素包含所有可能的关键字，而地址集合的元素仅为哈希表中的地址值。假设关键字集合为某种高级语言的所有标识符，如果一个标识符对应一个存储地址，那就不会发生冲突了，但这是不可能也没有必要的，因为存储空间难以满足，而且任何一个源程序都不会使用这么多标识符。因此在一般情况下，哈希函数是一个压缩映像，冲突是不可避免的。

对哈希表，主要考虑两个问题：一是如何构造哈希函数，二是如何解决冲突。

2．哈希函数的构造方法

常用的哈希函数构造方法有直接定址法、数字分析法、平方取中法、折叠法、随机数法和除留余数法等。

对于哈希函数的构造，应解决好两个主要问题。

（1）哈希函数应是一个压缩映像函数，它应具有较大的压缩性，以节省存储空间。

（2）哈希函数应具有较好的散列性，虽然冲突是不可避免的，但应尽量减少。

要减少冲突，就要设法使哈希函数尽可能均匀地把关键字映射到符号表存储区的各个存储地址上，这样就可以提高查找效率。构造哈希函数时，一般都要对关键字进行计算，且使关键字的所有组成部分都能起作用。

3．处理冲突的方法

解决冲突就是为出现冲突的关键字找到另一个“空”的哈希地址。在处理冲突的过程中，可能得到一个地址序列Hi
（i＝1，2，…，k）。常见的处理冲突的方法有如下几种。

（1）开放定址法。

其中，H（key）为哈希函数；m为哈希表表长；di
为增量序列。

常见的增量序列有如下三种。

①　di
＝1，2，3，…，m－1，称为线性探测再散列。

②　di
＝1^2
，-1^2
，2^2
，-2^2
，3^2
，…，
称为二次探测再散列。

③　di
＝伪随机序列，称为随机探测再散列。

最简单的产生探测序列的方法是进行线性探测。也就是发生冲突时，顺序地到存储区的下一个单元进行探测。

例如，某记录的关键字为key，哈希函数值H（key）＝j。若在哈希地址j发生了冲突（即此位置已存放了其他记录），则对哈希地址j＋1进行探测，若仍然有冲突，再对地址j＋2进行探测，依此类推，直到将元素存入哈希表。

例如，设关键码序列为47，34，13，12，52，38，33，27，3，哈希表表长为11，哈希函数为Hash（key）=key mod 11，则

使用线性探测法解决冲突构造的哈希表如下：

由哈希函数得到的关键字47，34，13，52，38，33的哈希地址没有冲突，元素直接存入。

对于元素12，其哈希地址为1，但是该地址中已经存入元素34，因此由H_1
＝（Hash（12）＋1）mod 11＝2，再试探哈希地址2，但该地址已被元素13占用，发生冲突；再计算H_2
＝（Hash（12）＋2）mod 11＝3，发生冲突（地址3被元素47占用）；再计算H_3
＝（Hash（12）＋3）mod 11＝4，空闲，因此将元素12存入哈希地址为4的单元。元素27和3也是通过解决冲突后存入的。

线性探测法可能使第i个哈希地址的同义词存入第i＋1个哈希地址，这样本应存入第i＋1个哈希地址的元素变成了第i＋2个哈希地址的同义词，……，因此，可能出现很多元素在相邻的哈希地址上“聚集”起来的现象，大大降低了查找效率。为此，可采用二次探测法或随机探测再散列法，以改善“聚集”问题。

那么在查找时就有三种可能：第一种情况是在某一位置上查到了关键字等于key的记录，查找成功；第二种情况是按探测序列查不到关键字为key的记录而又遇到了空单元，这时表明元素不在表中，表示查找失败；第三种情况是查遍全表，未查到指定关键字且符号表存储区已满，需进行溢出处理。

线性探测法思路清楚，算法简单，但也存在以下缺点。

①　溢出处理需另编程序。一般可另外设立一个溢出表，专门用来存放上述哈希表中放不下的记录。实现溢出表最简单的结构是顺序表，查找方法可用顺序查找。

②　线性探测法很容易产生聚集现象。所谓聚集现象，就是存入哈希表的记录在表中连成一片。当哈希函数不能把关键字很均匀地散列到哈希表中时，尤其容易产生聚集现象。产生聚集现象后，会增加探测的次数，从而降低了查找效率。

可以采取多种方法减少聚集现象的产生，二次探测再散列和随机探测再散列是两种有效的方法。

（2）链地址法。

链地址法（或拉链法）是一种经常使用且很有效的方法。它在查找表的每一个记录中增加一个链域，链域中存放下一个具有相同哈希函数值的记录的存储地址。利用链域，就把若干个发生冲突的记录链接在一个链表内。当链域的值为NULL时，表示已没有后继记录了。因此，对于发生冲突时的查找和插入操作就跟线性表一样了。

例如，哈希表表长为11、哈希函数为Hash（key）＝key mod 11，对于关键码序列47，34，13，12，52，38，33，27，3，使用链地址法构造的哈希表如图8-55所示。

在图8-55所示的哈希表中进行成功查找的平均查找长度ASL为

（3）再哈希法。

图8-55　用链地址法解决冲突构造哈希表

RHi
均是不同的哈希函数，即在同义词发生地址冲突时计算另一个哈希函数地址，直到冲突不再发生。这种方法不易产生聚集现象，但增加了计算时间。

（4）建立一个公共溢出区。无论由哈希函数得到的哈希地址是什么，一旦发生冲突，都填入到公共溢出区中。

4．哈希表的查找

在哈希表中进行查找操作时，用与存入元素时相同的哈希函数和冲突处理方法计算得到待查记录的存储地址，然后到相应的存储单元里去获得有关信息再判定查找是否成功。因此，哈希查找的特点如下。

（1）虽然哈希表在关键字与记录的存储位置之间建立了直接映像，但由于“冲突”的产生，使得哈希表的查找过程仍然是一个给定值和关键字进行比较的过程。因此，仍需以平均查找长度衡量哈希表的查找效率。

（2）查找过程中需和给定值进行比较的关键字的个数取决于下列三个因素：哈希函数、处理冲突的方法和哈希表的装填因子。

在一般情况下，冲突处理方法相同的哈希表，其平均查找长度依赖于哈希表的装填因子。哈希表的装填因子定义为

α标志着哈希表的装满程度。直观地看，α越小，发生冲突的可能性就越小；反之，α越大，表中已填入的记录越多，再填记录时，发生冲突的可能性就越大，则查找时，给定值需与之进行比较的关键字的个数也就越多。

8.6　排序

8.6.1　排序的基本概念

（1）排序。假设含n个记录的文件内容为｛R_1
，R_2
，…，Rn
｝，相应的关键字为｛k_1
，k_2
，…，kn
｝。经过排序确定一种排列｛Rj1
，Rj2
，…，Rjn
｝，使得它们的关键字满足如下递增（或递减）关系：kj1
≤kj2
≤…≤kjn
（或kj1
≥kj2
≥…≥kjn
）。

若在待排序的一组序列中，Ri
和Rj
的关键字相同，即ki
＝kj
，且在排序前Ri
领先于Rj
，那么当排序后，如果Ri
和Rj
的相对次序保持不变，Ri
仍领先于Rj
，则称此类排序方法为稳定的。若在排序后的序列中有可能出现Rj
领先于Ri
的情形，则称此类排序为不稳定的。

（2）内部排序。指待排序记录全部存放在内存中进行排序的过程。

（3）外部排序。指待排序记录的数最很大，以至于内存不能容纳全部记录，在排序过程中尚需对外存进行访问的排序过程。

在排序过程中需进行下列两种基本操作：比较两个关键字的大小；将记录从一个位置移动到另一个位置。前一种操作对大多数排序方法来说都是必要的，而后一种操作可以通过改变记录的存储方式来避免。

8.6.2　简单排序

1．直接插入排序

直接插入排序是一种简单的排序方法，具体做法是：在插入第i个记录时，R_1
，R_2
，…，Ri－1
已经排好序，这时将Ri
的关键字ki
依次与关键字ki－1
，ki－2
等进行比较，从而找到应该插入的位置并将Ri
插入，插入位置及其后的记录依次向后移动。

直接插入排序法在最好情况下（待排序列已按关键码有序）每趟排序只需作1次比较且不需要移动元素，因此n个元素排序时的总比较次数为n－1次，总移动次数为0次。在最坏情况下（元素已经逆序排列），进行第j趟排序时，待插入的记录需要同前面的每个记录进行比较，比较次数为j（设置监视哨时，包括与监视哨的一次比较）或j－1（不设监视哨），移动记录的次数为j＋1。因此，总比较次数为
总移动次数为

直接插入排序是一种稳定的排序方法，其时间复杂度为Ο（n^2
）。排序过程中仅需要一个元素的辅助空间，空间复杂度为Ο（1）。

2．冒泡排序

n个记录进行冒泡排序的方法是：首先将第一个记录的关键字和第二个记录的关键字进行比较，若为逆序，则交换这两个记录的值，然后比较第二个记录和第三个记录的关键字，依此类推，直至第n－1个记录和第n个记录的关键字比较过为止。上述过程称作第一趟冒泡排序，其结果是关键字最大的记录被放置到第n个记录的位置上。然后进行第二趟冒泡排序，对前n－1个记录进行同样的操作，其结果是关键字次大的记录被放置到第n－1个记录的位置上。最多进行n－1趟，所有记录有序排列。若在某趟冒泡排序过程没有进行相邻位置的元素交换处理，则可结束排序过程。

冒泡排序法在最好情况下（待排序列已按关键码有序），只须作1趟排序，元素的比较次数为n－1且不需要交换元素，因此总比较次数为n－1次，总交换次数为0次。在最坏情况下（元素已经逆序排列），进行第j趟排序时，最大的j－1个元素已经排好序，其余的n－（j－1）个元素需要进行n－j次比较和n－j次交换，因此总比较次数为
总交换次数为

冒泡排序是一种稳定的排序方法，其时间复杂度为Ο（n^2
）。排序过程中仅需要一个元素的辅助空间用于元素的交换，空间复杂度为Ο（1）。

3．简单选择排序

n个记录进行简单选择排序的基本方法是：通过n－i（1≤i≤n）次关键字之间的比较，从n－i＋1个记录中选出关键字最小的记录，并和第i个记录进行交换，当i等于n时所有记录有序排列。

【算法】简单排序算法。

简单选择排序法在最好情况下（待排序列已按关键码有序），不需要移动元素，因此n个元素排序时的总移动次数为0次。在最坏情况下（元素已经逆序排列），每趟排序移动记录的次数都为3次（两个数组元素交换值），共进行n－1趟排序，总移动次数为3（n－1）。无论在哪种情况下，元素的总比较次数为

简单选择排序是一种不稳定的排序方法，其时间复杂度为Ο（n^2
）。排序过程中仅需要一个元素的辅助空间用于数组元素值的交换，空间复杂度为Ο（1）。

8.6.3　希尔排序

希尔排序又称“缩小增量排序”，是对直接插入排序方法的改进。

希尔排序的基本思想是：先将整个待排记录序列分割成若干序列，然后分别进行直接插入排序，待整个序列中的记录基本有序时，再对全体记录进行一次直接插入排序。具体做法是：先取一个小于n的整数d_1
作为第一个增量，把文件的全部记录分成d_1
个组，即将所有距离为d_1
倍数的记录放在同一个组中，在各组内进行直接插入排序；然后取第二个增量d_2
＜d_1
，重复上述分组和排序工作，依此类推，直至所取的增量di
＝1（di
＜di－1
＜…＜d_2
＜d_1
），即所有记录放在同一组进行直接插入排序为止。

增量序列为5，3，1时，希尔插入排序过程如下所示。

［初始关键字］：

第一趟排序结果：

第二趟排序结果：

第三趟排序结果：

【函数】用希尔排序方法对整型数组进行非递减排序。

希尔排序是一种不稳定的排序方法，据统计分析其时间复杂度为Ο（n1.3
），排序过程中仅需要一个元素的辅助空间用于数组元素值的交换，空间复杂度为Ο（1）。

8.6.4　快速排序

快速排序的基本思想是：通过一趟排序将待排的记录分割为独立的两部分，其中一部分记录的关键字均不大于另一部分记录的关键字，然后再分别对这两部分记录继续进行排序，以达到整个序列有序。

用一维数组存储记录，一趟快速排序的具体做法是：附设两个指针low和high，它们的初值分别指向第一个记录和最后一个记录。设枢轴记录（通常是第一个记录）的关键字为pivotkey，则首先从high所指位置起向前搜索，找到第一个关键字小于pivotkey的记录与枢轴记录互相交换，然后从low所指位置起向后搜索，找到第一个关键字大于piotkey的记录与枢轴记录互相交换，重复这两步直至low等于high时为止。

【函数】用快速排序方法对整型数组进行非递减排序。

快速排序是不稳定的排序方法，其平均时间复杂度为Ο（nlogn），空间复杂度为Ο（logn）。若初始记录序列按关键字有序或基本有序时，快速排序则退化为冒泡排序，此时，算法的时间复杂度为Ο（n^2
）。

8.6.5　堆排序

对于n个元素的关键字序列｛K_1
，K_2
，…，Kn
｝，当且仅当满足下列关系时称其为堆。

若将此序列对应的一维数组（即以一维数组作为序列的存储结构）看成是一个完全二叉树，则堆的含义表明，完全二叉树中所有非终端节点的值均不小于（或不大于）其左、右孩子节点的值。因此，在一个堆中，堆顶元素（即完全二叉树的根节点）必为序列中的最大元素（或最小元素），并且堆中任一棵子树也都是堆。若堆顶为最小元素，则称为小根堆；若堆顶为最大元素，则称为大根堆。

堆排序的基本思想是：对一组待排序记录的关键字，首先按堆的定义排成一个序列（即建立初始堆），从而输出堆顶的最大关键字（对于大根堆而言），然后将剩余的关键字再调整成新堆，便得到次大的关键字，如此反复，直到全部关键字排成有序序列为止。

初始堆的建立方法是：将待排序的关键字分放到一棵完全二叉树的各个节点中（此时完全二叉树并不一定具备堆的特性），显然，所有
的节点Ki
都没有子节点，以这样的Ki
为根的子树已经是堆，因此初始建堆可从完全二叉树的第
个节点Ki
开始，通过调整，逐步使以
为根的子树满足堆的定义。

在对Ki
为根的子树建堆的过程中，可能需要交换Ki
和k2i
（或K2i＋1
）的值，如此一来，以K2i
（或K2i＋1
）为根的子树可能不再满足堆的定义，则应继续以K2i
（或K2i＋1
）为根进行调整，如此层层地递推下去，可能会一直延伸到树叶时为止。这种方法就像过筛子一样，把最大的关键字一层一层地筛选出来，最后输出堆顶的最大元素。

【函数】将一个整型数组中的元素调整成大根堆。

调整成新堆：假设输出堆顶元素之后，以堆中最后一个元素替代，那么根节点的左、右子树均为堆，此时只需自上至下进行调整即可。

【函数】用堆排序方法对整型数组进行非递减排序。

序列（55，60，40，10，80，65，15，5，75）建立初始堆的过程如图8-56所示，调整为新堆的过程如图8-57所示。

图8-56　初始堆建立过程示意图

图8-57　调整为新堆的过程示意图

对于记录数较少的文件来说，堆排序的优越性并不明显，但对大量的记录来说堆排序是很有效的。堆排序的整个算法时间是由建立堆和不断调整堆这两部分时间代价构成的。可以证明，堆排序算法的时间复杂度为Ο（n logn）。此外，堆排序只需要一个记录大小的辅助空间。堆排序是一种不稳定的排序方法。

8.6.6　归并排序

所谓“归并”，是将两个或两个以上的有序文件合并成为一个新的有序文件。归并排序是把一个有n个记录的无序文件看成是由n个长度为1的有序子文件组成的文件，然后进行两两归并，得到
个长度为2或1的有序文件，再两两归并，如此重复，直至最后形成包含n个记录的有序文件为止。这种反复将两个有序文件归并成一个有序文件的排序方法称为两路归并排序。

两路归并排序的核心操作是将一维数组中前后相邻的两个有序序列归并为一个有序序列。

【算法】将分别有序的data1[s..m]和data1[m＋1..n]归并为有序的data2[s..n]。

一趟归并排序的操作是：调用
n/2h
次Merge算法，将数组data1[0..n-1]中前后相邻且长度为h的有序段进行两两归并，得到前后相邻、长度为2h的有序段，并存放在data2[0..n-1]中，整个归并排序需进行
log_2
n
趟。可见，归并排序需要辅助空间n个（与待排记录数量相等），时间复杂度为Ο（n logn）。

【算法】递归形式的两路归并。

【算法】对一维数组data1[0..n-1]中的元素进行两路归并排序。

8.6.7　基数排序

基数排序的思想是按组成关键字的各个数位的值进行排序，它是分配排序的一种。该排序方法中把一个关键字Ki
看成一个d元组，即

其中，0≤
＜r，i＝1～n，j＝1～d。这里的r称为基数。若关键字是十进制，则r＝10；若关键字是八进制的，则r＝8。d是关键字的位数，d值取所有待排序的关键字位数的最大值，其他不足d位的关键字则在前面补零。

在
，
，…，
中，
称为最高有效位，
称为次高有效位，
称为最低有效位。基数排序可以从最高有效位开始，也可以从最低有效位开始。

基数排序的基本思想是：设立r个队列，队列的编号分别为0，1，2，…，r－1。首先按最低有效位的值，把n个关键字分配到这r个队列中；然后从小到大将各队列中关键字再依次收集起来；接着再按次低有效位的值把刚收集起来的关键字再分配到r个队列中。重复上述收集过程，直至最高有效位。这样得到了一个从小到大有序的关键字序列。为了减少记录移动的次数，队列可以采用链式存储分配，称为链队列。每个链队列设有两个指针，分别指向队头和队尾。

基数排序是一种稳定的排序方法。对于n个记录，执行一次分配和收集的时间为Ο（n＋r）。如果关键字有d位，则要执行d遍。所以总的运算时间为Ο（d（n＋r））。可见，对于不同的基数r所用的时间是不同的。当r或d较小时，这种排序方法较为节省时间。另外，基数排序适用于链式分配的记录的排序，其要求的附加存储量是r个队列的头、尾指针，所以附加存储量为2r个存储单元。由于待排序记录是以链表方式存储的，相对于顺序分配而言，还增加了n个指针域的空间。

8.6.8　内部排序方法小结

综合比较所讨论的各种排序方法，大致结果如表8-2所示。

表8-2　各种排序方法的性能比较

迄今为止，已有的排序方法远远不止上述几种，人们之所以热衷于研究各种排序方法，不仅是由于排序在计算机运算中所处的重要位置，而且还因为不同的方法各有优缺点，可根据需要运用到不同的场合。选取排序方法时需要考虑的因素有待排序的记录个数n；记录本身的大小；关键字的分布情况；对排序稳定性的要求；语言工具的条件和辅助空间的大小。

依据这些因素，可以得到以下几点结论。

（1）若待排序的记录数目n较小时，可采用插入排序和选择排序。由于直接插入排序所需的记录移动操作较简单选择排序多，因而当记录本身信息量较大时，用简单选择排序方法较好。

（2）若待排序记录按关键字基本有序，则宜采用直接插入排序或冒泡排序。

（3）当n很大且关键字的位数较少时，采用链式基数排序较好。

（4）若n较大，则应采用时间复杂度为Ο（nlogn）的排序方法，例如快速排序、堆排序或归并排序。快速排序目前被认为是内部排序方法中最好的方法，当待排序的关键字为随机分布时，快速排序的平均运行时间最短；但堆排序只需1个辅助存储空间，并且不会出现在快速排序中可能出现的最坏情况。这两种排序方法都是不稳定的排序方法。若要求排序稳定，可选择归并排序。通常可将归并排序和直接插入排序结合起来使用。先利用直接插入排序求得较长的有序子序列，然后再两两归并。因为直接插入排序是稳定的，所以改进的归并排序是稳定的。

前面讨论的内部排序算法（除基数排序外）都是在一维数组上实现的。当记录本身信息量较大时，为避免耗费大量的时间移动记录，可以采用链表作为存储结构，在这种情况下，希尔排序、快速排序和堆排序就不适用了。

8.6.9　外部排序

外部排序就是对大型文件的排序，待排序的记录存放在外存。在排序的过程中，内存只存储文件的一部分记录，整个排序过程需要进行多次内外存间的数据交换。

常用的外部排序方法是归并排序，一般分为两个阶段：在第一阶段，把文件中的记录分段读入内存，利用某种内部排序方法对记录段进行排序并输出到外存的另一个文件中，在新文件中形成许多有序的记录段，称为归并段；在第二阶段，对第一阶段形成的归并段用某种归并方法进行一趟趟地归并，使文件的有序段逐渐加长，直到将整个文件归并为一个有序段时为止。下面简单介绍常用的多路平衡归并方法。

k路平衡归并是指文件经外部排序的第一个阶段后，已经形成了由若干个初始归并段构成的文件。在这个基础上，反复将每次确定的k个归并段归并为一个有序段，将一个文件上的记录归并到另一个文件上。重复这个过程，直到文件中的所有记录都归并为一个有序段。

设已经得到8个初始归并段，如图8-58所示，其中bi
表示第i个归并段。

图8-58　初始归并段

在树形选择排序中，首先对n个记录的关键字进行两两比较，然后在
个较小者之间再进行两两比较，如此重复，直到选出最小关键字的记录为止，该过程可用一棵有n个叶子节点的完全二叉树表示，如图8-59（a）所示。在树中，每个非终端节点中的关键字等于其左、右孩子节点中较小的关键字，则树根节点中的关键字即为所有叶子中的最小关键字。在输出最小关键字之后，更新最小关键字所在的叶子节点数据，然后从该叶子节点出发，与其左（兄弟）节点的关键字进行比较，修改从叶子节点到根的路径上各节点的关键字，则根节点的关键字即为次小关键字，如图8-59（b）所示。重复该过程，就可完成对所有记录的排序。

图8-59　树形选择排序

在图8-59所示的树中，每个非终端节点记录了其左、右孩子中的“优胜者”，所以称其为“胜者树”。反之，若在双亲节点中记录比较后的失败者，而让胜者去参加更上一层的比较，便可得到一棵“败者树”。这样一来，当优胜者到达父节点时，立刻就知道原先在此比较的失败者并与失败者进行比较，再次记录新的失败者并让优胜者去进行更上一层的比较。在败者树中，每个节点只需和其父节点进行比较，而在胜者树中，向上调整时节点需与兄弟节点比较，那么就需得到兄弟节点的位置信息，因此败者树更易于编程。

为了简便起见，设每个记录为一个整数，败者树用数组ls[]表示，ls[i]的值为败者所在归并段的段号，令ls[1]是树根节点，ls[0]是ls[1]（根）的父节点，ls[0]中存储每次选出的优胜者所在归并段的段号，输出时则取ls[0]指示的归并段的当前记录。例如，图8-60所示的是一棵实现8路归并的败者树，叶子节点的数据来自各个归并段；败者树的根节点ls[1]的父节点ls[0]中存储了优胜者（最小记录）所在的归并段。图8-61所示的是输出一个记录后，重新调整后的败者树。

图8-60　利用败者树找出8路归并段的最小关键字

图8-61　利用败者树找出8路归并段的次小关键字

【算法】利用败者树实现k路平衡归并。

【函数】败者树的调整：从叶子节点到根节点进行调整。

第9章　算法设计与分析

算法被公认为是计算机科学的基石，算法理论研究的是算法的设计技术和分析技术。前者回答的是“对特定的问题，如何提出一个算法来求解？”这样的问题，即面对一个问题，如何设计一个有效的算法；后者回答的是“该算法是否足够好？”，即对已设计的算法，如何评价或判断其优劣。二者是互相依存的，设计出的算法需要检验和评价，对算法的分析反过来又将改进算法的设计。

9.1　算法设计与分析的基本概念

9.1.1　算法

算法（Algorithm）是对特定问题求解步骤的一种描述，它是指令的有限序列，其中每一条指令表示一个或多个操作。此外，一个算法还具有下列5个重要特性。

（1）有穷性。一个算法必须总是（对任何合法的输入值）在执行有穷步之后结束，且每一步都可在有穷时间内完成。

（2）确定性。算法中每一条指令必须有确切的含义，读者理解时不会产生二义性。并且在任何条件下，算法只有唯一的一条执行路径，即对于相同的输入只能得出相同的输出。

（3）可行性。一个算法是可行的，即算法中描述的操作都是可以通过已经实现的基本运算执行有限次来实现。

（4）输入。一个算法有零个或多个输入，这些输入取自于某个特定的对象的集合。

（5）输出。一个算法有一个或多个输出。这些输出是同输入有着某些特定关系的量。

9.1.2　算法设计

算法设计是一件非常困难的工作，通常设计一个“好”的算法应考虑多个目标，包括正确性、可读性、健壮性和高效性等。由于实际问题各种各样，问题求解的方法千变万化，所以算法设计又是一个灵活的充满智慧的过程，需要设计人员根据实际情况具体问题具体分析。

存在多种算法设计技术（也称为算法设计策略），它们是设计算法的一般性方法。已经证明这些技术对于设计好的算法非常有用，掌握了这些技术之后，设计新的和有用的算法会变得容易。经常采用的算法设计技术主要有分治法、动态规划法、贪心法、回溯法、分支限界法、概率算法和近似算法等。另外，在解决计算机领域以外的问题时，这些技术也能起到很好的指导作用。

9.1.3　算法分析

通常求解一个问题可能会有多种算法可供选择，选择的主要标准首先是算法的正确性、可靠性、简单性和易理解性。其次是算法的时间复杂度和空间复杂度要低，这正是算法分析技术的主要内容。

算法分析是指对一个算法所需要的资源进行估算，这些资源包括内存、通信带宽、计算机硬件和时间等，所需要的资源越多，该算法的复杂性就越高。不言而喻，对于任何给定的问题，设计出复杂性尽可能低的算法是设计算法时追求的重要目标。另一方面，当给定问题有很多种算法时，选择其中复杂性最低者，是选用算法时遵循的重要准则。因此，算法的复杂性分析对算法的设计和选用有重要的指导意义和实用价值。

而在计算机资源中，最重要的是时间和空间（存储器）资源，因此复杂性分析主要包括时间复杂性和空间复杂性。

9.1.4　算法的表示

常用的表示算法的方法有自然语言、流程图、程序设计语言和伪代码等。

（1）自然语言。最大的优点是容易理解，缺点是容易出现二义性，并且算法通常都很冗长。

（2）流程图。优点是直观易懂，缺点是严密性不如程序设计语言，灵活性不如自然语言。

（3）程序设计语言。优点是能用计算机直接执行，缺点是抽象性差，使算法设计者拘泥于描述算法的具体细节，忽略了“好”算法和正确逻辑的重要性。此外，还要求算法设计者掌握程序设计语言及编程技巧。

（4）伪代码。伪代码是介于自然语言和程序设计语言之间的方法，它采用某一程序设计语言的基本语法，操作指令可以结合自然语言来设计。计算机科学家从来没有对伪代码的书写形式达成过共识。在伪代码中，可以采用最具表达力的、最简明扼要的方法来表达一个给定的算法。本章采用伪代码来表示算法。

本章伪代码的一些约定。

（1）用缩进表示程序中的分程序或程序块结构。

（2）while、for等循环结构和if、then、else等条件结构与Pascal中的相似。其实只要熟悉Pascal、C、C++或者Java中的某一种语言，便可很容易理解伪代码表达的含义。

（3）用▷表示注释。

（4）数组的元素通过“数组名［下标］”的方式访问，下标从1开始。

（5）参数采用按值传递。

（6）布尔运算符and和or具有短路能力。

9.2　算法分析基础

9.2.1　时间复杂性

由于时间复杂性与空间复杂性概念类似，计算方法相似，且空间复杂性分析相对简单些，因此下面将主要讨论时间复杂性。算法的时间复杂度分析主要是分析算法的运行时间，即算法所执行的基本操作数。

不同规模的输入所需要的基本操作数是不相同的，如用同一个排序算法排序100个数和排序10000个数所需要的操作数是不相同的，因此考虑特定输入规模的算法的具体操作数既是不现实也是不必要的。在算法分析中，可以建立以输入规模n为自变量的函数T（n）来表示算法的时间复杂度。

即使对相同的输入规模，数据分布不相同也决定了算法执行不同的路径，因此所需要的执行时间也不相同。根据不同的输入，将算法的时间复杂度分析分为三种情况。

（1）最佳情况。使算法执行时间最少的输入。一般情况下，不进行算法在最佳情况下的时间复杂度分析。应用最佳情况分析的一个例子是已经证明基于比较的排序算法的时间复杂度下限为Ω（nlgn），那么就不需要白费力气去想方设法将该类算法改进为线性时间复杂度。

（2）最坏情况。使算法执行时间最多的输入。一般会进行算法在最坏时间复杂度的分析，因为最坏情况是在任何输入下运行时间的一个上限，它给我们提供一个保障，情况不会比这更糟糕。另外，对于某些算法来说，最坏情况还是相当频繁的。而且大致上看，平均情况通常与最坏情况的时间复杂度一样。

（3）平均情况。算法的平均运行时间，一般来说，这种情况很难分析。举个简单的例子，现要排序10个不同的整数，输入就有10！种不同的情况，平均情况的时间复杂度要考虑每一种输入及其该输入的概率。平均情况分析可以按如下三个步骤进行。

①　将所有的输入按其执行时间分类。

②　确定每类输入发生的概率。

③　确定每类输入的执行时间。

下式给出了一般算法在平均情况下的复杂度分析。

其中，pi
表示第i类输入发生的概率；ti
表示第i类输入的执行时间，输入分为m类。

9.2.2　渐进符号

以输入规模n为自变量建立的时间复杂度实际上还是较复杂的，如an^2
＋bn＋c，不仅与输入规模有关，还与系数a、b和c有关。此时可以对该函数做进一步的抽象，仅考虑运行时间的增长率或称为增长的量级，如忽略上式中的低阶项和高阶项的系数，仅考虑n^2
。当输入规模大到使只有与运行时间的增长量级有关时，就是在研究算法的渐进效率。也就是说，从极限角度看，只关心算法运行时间如何随着输入规模的无限增长而增长。下面简单介绍三种常用的标准方法来简化算法的渐进分析。

（1）Ο记号。给出一个函数的渐进上界。给定一个函数g（n），Ο（g（n））表示为一个函数集合｛f（n）:存在正常数c和n_0
，使得对所有的n≥n_0
，有0≤f（n）≤cg（n）｝。

（2）Ω记号。给出一个函数的渐进下界。给定一个函数g（n），Ο（g（n））表示为一个函数集合｛f（n）:存在正常数c和n_0
，使得对所有的n≥n_0
，有0≤cg（n）≤f（n）｝。

（3）Θ记号。给出一个函数的渐进上界和下界，即渐进确界。给定一个函数g（n），Ο（g（n））表示为一个函数集合｛f（n）:存在正常数c_1
、c_2
和n_0
，使得对所有的n≥n_0
，有0≤c_1
g（n）≤f（n）≤c_2
g（n）｝。

图9-1　记号Θ、Ο和Ω的图例

由上述定义可知，f（n）＝Θ（g（n））当且仅当f（n）＝Ο（g（n））和f（n）＝Ω（g（n））。

【例9.1】判断下列等式是否成立？

（1）10n^2
＋4n＋2＝Ο（n^2
）

（2）10n^2
＋4n＋2＝Ο（n^3
）

（3）10n^2
＋4n＋2＝Ο（n）

（4）10n^2
＋4n＋2＝Ω（n^2
）

（5）10n^2
＋4n＋2＝Ω（n^3
）

（6）10n^2
＋4n＋2＝Ω（n）

（7）10n^2
＋4n＋2＝Θ（n^2
）

（8）10n^2
＋4n＋2＝Θ（n^3
）

（9）10n^2
＋4n＋2＝Θ（n）

解：（1）√（2）√（3）×（4）√（5）×（6）√（7）√（8）×（9）×

9.2.3　递归式

从算法的结构上看，算法可以分为非递归形式和递归形式。非递归算法的时间复杂度分析较简单，本节主要讨论递归算法的时间复杂度分析方法。

（1）展开法。将递归式中等式右边的项根据递归式进行替换，称为展开。展开后的项被再次展开，如此下去，直到得到一个求和表达式，得到结果。

【例9.2】求
的解。

解：

（2）代换法。这一名称来源于当归纳假设用较小值时，用所猜测的值代替函数的解。用代换法解递归式时，需要两个步骤：猜测解的形式；用数学归纳法找出使解真正有效的常数。

【例9.3】确定
的上界。

解：（1）猜测其解为T（n）＝Ο（nlgn）。目标是要证明T（n）≤cnlgn，其中c＞0为常数。

（2）先假设这个界对n/2成立，即T（n/2）≤cnlg（n/2）/2。对递归式做替换，得到T（n）≤2cnlg（n/2）/2＋n＝cnlgn－cn＋n≤cnlgn（c≥1）。

尽管代换法提供了一种证明递归式解的正确性的简单方法，但并不存在通用的方法来猜测递归式的正确解，这种猜测需要经验，有时甚至是创造性的。因为往往很难得到“好”的猜测，因此这种方法较难用。

（3）递归树法。递归树法弥补了代换法猜测困难的缺点，它适于提供“好”的猜测，然后用代换法证明。在递归树中，每一个节点都代表递归函数调用集合中每一个子问题的代价。将树中每一层内的代价相加得到一个每层代价的集合，再将每层的代价相加得到递归式所有层次的总代价。当用递归式表示分治算法的时间复杂度时，递归树的方法尤其有用。

【例9.4】确定
的确界。

解：用递归树法求解，图9-2（a）、图9-2（b）、图9-2（c）和图9-2（d）给出了递归树的构造过程。

图9-2　与递归式T（n）＝4T（n/2）＋cn对应的递归树的构造过程

（4）主方法。也称为主定理，给出求解如下形式的递归式的快速方法。

其中，a≥1和b＞1是常数，f（n）是一个渐进的正函数。

【定理9.1】（主定理）设a≥1和b＞1为常数，f（n）为函数，T（n）为定义在非负整数上的递归式，T（n）＝aT（n/b）＋f（n），其中n/b指
n/b
或
n/b
，那么T（n）可能有如下的渐进界。

（1）若对于某常数ε＞0，有
则

（2）若
则

（3）若对于某常数ε＞0，有f（n）＝Ω（nlogb
a+ε
），且对常数c＜1与所有足够大的n，有af（b/n）≤cf（n），则T（n）＝Θ（f（n））。

【例9.5】用主方法求解下列递归式。

（1）T（n）＝9T（n/3）＋n

（2）T（n）＝T（2n/3）＋1

（3）T（n）＝3T（n/4）＋nlgn

解：（1）a＝9，b＝3，f（n）＝n。logb
a＝2，
其中ε＝1，属于情况（1），因此有

（2）a＝1，b＝3/2，f（n）＝1。logb
a＝0，
其中k＝0，属于情况（2），因此有

（3）a＝3，b＝4，f（n）＝nlgn。logb
a≈0.793，
其中ε≈0.2，属于情况（3），因此有T（n）＝Θ（f（n））＝Θ（nlgn）。

9.3　分治法

9.3.1　递归的概念

递归是指子程序（或函数）直接调用自己或通过一系列调用语句间接调用自己，是一种描述问题和解决问题的常用方法。在计算机算法设计与分析中，递归技术是十分有用的。使用递归技术往往使函数的定义和算法的描述简洁且易于理解。还有一些问题，虽然其本身并没有明显的递归结构，但用递归技术来求解使设计出的算法简洁易懂且易于分析，为此在介绍其他算法设计方法之前先讨论它。

递归有两个基本要素：边界条件，即确定递归到何时终止，也称为递归出口；递归模式，即大问题是如何分解为小问题的，也称为递归体。

【例9.6】阶乘函数。

阶乘函数可递归地定义为：

阶乘函数的自变量n的定义域是非负整数。递归式的第一式给出了这个函数的一个初始值，是递归的边界条件。递归式的第二式是用较小自变量的函数值来表示较大自变量的函数值的方式来定义n的阶乘，是递归体。n！可以递归地计算如下：

9.3.2　分治法的基本思想

分治与递归就像一对孪生兄弟，经常同时应用于算法设计之中，并由此产生许多高效的算法。我们知道，任何一个可以用计算机求解的问题所需要的计算时间都与其规模有关。问题的规模越小，解题所需要的计算时间往往也越少，从而也较容易处理。例如，对于n个元素的排序问题，当n＝1时，不需任何比较；当n＝2时，只要做一次比较即可；……而当n较大时，问题就不那么容易处理了。要想直接解决一个较大的问题，有时是相当困难的。分治法的设计思想是将一个难以直接解决的大问题分解成一些规模较小的相同问题以便各个击破，分而治之。如果规模为n的问题可分解成k个子问题，1＜k≤n，这些子问题互相独立且与原问题相同。分治法产生的子问题往往是原问题的较小模式，这就为递归技术提供了方便。

一般来说，分治算法在每一层递归上都有三个步骤。

（1）分解。将原问题分解成一系列子问题。

（2）求解。递归地求解各子问题。若子问题足够小，则直接求解。

（3）合并。将子问题的解合并成原问题的解。

9.3.3　分治法的典型实例

以上讨论的是分治法的基本思想和一般原则，下面用一些具体例子来说明如何针对具体问题用分治思想来设计有效算法。

【例9.7】归并排序。

归并排序算法是成功应用分治法的一个完美的例子，其基本思想是将待排序元素分成大小大致相同的两个子序列，分别对这两个子序列进行排序，最终将排好序的子序列合并为所要求的序列。归并排序算法完全依照上述分治算法的三个步骤进行。

（1）分解。将n个元素分成各含n/2个元素的子序列。

（2）求解。用归并排序对两个子序列递归地排序。

（3）合并。合并两个已经排好序的子序列以得到排序结果。

归并排序算法的伪代码如下：

过程Merge（A，p，q，r）的伪代码为：

其中，A是数组，p、q和r是下标，满足p≤q＜r。Merge显然可在Ο（n）时间内完成，因此合并排序算法对n个元素进行排序所需的计算时间T（n）满足：

解此递归式可知T（n）＝Ο（nlogn）。

【例9.8】最大子段和问题。

给定由n个整数（可能有负整数）组成的序列a_1
，a_2
，…，an
，求该序列形如
的子段和的最大值。当序列中所有整数均为负整数时，其最大子段和为0。依此定义，所求的最优值为

例如，当（a_1
，a_2
，a_3
，a_4
，a_5
，a_6
）＝（-2，11，-4，13，-5，-2）时，最大子段和为

最大子段和问题的分治策略如下。

（1）分解。如果将所给的序列A[1..n]分为长度相等的两段A[1..n/2]和A[n/2＋1..n]，分别求出这两段的最大子段和，则A[1..n]的最大子段和有三种情形。

①　A[1..n]的最大子段和与A[1..n/2]的最大子段和相同。

②　A[1..n]的最大子段和与A[n/2＋1..n]的最大子段和相同。

③　A[1..n]的最大子段和为
且1≤i≤n/2，n/2＋1≤j≤n。

（2）解决。①和②这两种情形可递归求得。对于情形③，容易看出，A[n/2]与A[n/2＋1]在最优子序列中。因此可以在A[1..n/2]中计算出
并在A[n/2＋1..n]中计算出
则s1＋s2即为出现情形③的最优值。

（3）合并。比较在分解阶段的三种情况下的最大子段和，取三者之中的较大者为原问题的解。

据此可设计出求解最大子段和的分治算法如下：

过程MaxSubSum的伪代码为：

其中A为数组，left和right表示数组下标。对应分解中的①和②两种情形，需要分别递归求解，对应情形③，两个并列for循环的时间复杂度为Ο（n），因此得到下列递归式。

解此递归式可知T（n）＝Ο（nlogn）。

【例9.9】循环赛日程安排。

设有n（2k
）位选手参加网球循环赛，循环赛共进行n－1天，每位选手要与其他n－1位选手赛一场，且每位选手每天赛一场，不轮空。试按此要求为比赛安排日程。

设n位选手被顺序编号为1，2，…，n。比赛的日程表是一个n行n－1列的表，第i行列的内容是第i号选手第j天的比赛对手。用分治法设计日程表，就是从其中一半选手（2k－1
位）的比赛日程导出全体（2k
位）选手的比赛日程。从众所周知的只有两位选手的比赛日程出发，反复这个过程，直至为n位选手安排好比赛日程为止。

为了从2k－1
位选手的比赛日程表中导出2k
位选手的比赛日程表，假定只有8位选手（如图9-3所示），若1～4号选手之间的比赛日程填在日程表的左上角（4行3列），5～8号选手之间的比赛日程填在日程表的左下角（4行3列），而左下角的内容可以由左上角对应项加上数4得到。至此，剩下的右上角（4行4列）是为编号小的1～4号选手与编号大的5～8号选手之间的比赛安排日程。例如在第4天，让1～4号选手分别与5～8号选手比赛，以后各天，依次由前一天的日程安排，让5～8号选手循环轮转即可。最后，参照右上角得到右下角的比赛日程，如图9-3所示。由以上分析，不难得到为2k
选手安排比赛的算法思路。

图9-3　循环赛日程安排

循环赛日程安排算法的关键在于寻找这4部分元素之间的对应关系，其伪代码如下：

其中A为数组，n＝2k
表示参加比赛的人数。

9.4　动态规划法

9.4.1　动态规划法的基本思想

动态规划算法与分治法类似，其基本思想也是将待求解问题分解成若干个子问题，先求解子问题，然后从这些子问题的解得到原问题的解。与分治法不同的是，适合于用动态规划法求解的问题，经分解得到的子问题往往不是独立的。若用分治法来解这类问题，则相同的子问题会被求解多次，以至于最后解决原问题需要耗费指数级时间。然而，不同子问题的数目常常只有多项式量级。如果能够保存已解决的子问题的答案，而在需要时再找出已求得的答案，这样就可以避免大量的重复计算，从而得到多项式时间的算法。为了达到这个目的，可以用一个表来记录所有已解决的子问题的答案。不管该子问题以后是否被用到，只要它被计算过，就将其结果填入表中。这就是动态规划法的基本思路。

动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中，可能会有许多可行解，每个解都对应于一个值，我们希望找到具有最优值（最大值或最小值）的那个解。当然，最优解可能会有多个，动态规划算法能找出其中的一个最优解。设计一个动态规划算法，通常可按照以下几个步骤进行。

（1）找出最优解的性质，并刻画其结构特征。

（2）递归地定义最优解的值。

（3）以自底向上的方式计算出最优值。

（4）根据计算最优值时得到的信息，构造一个最优解。

步骤（1）～（3）是动态规划算法的基本步骤。在只需要求出最优值的情形，步骤（4）可以省略。若需要求出问题的一个最优解，则必须执行步骤（4）。此时，在步骤（3）中计算最优值时，通常需记录更多的信息，以便在步骤（4）中根据所记录的信息快速构造出一个最优解。

动态规划法是一个非常有效的算法设计技术，那么何时可以应用动态规划来设计算法呢？对一个给定的问题，若其具有以下两个性质，则可以考虑用动态规划法来求解：

（1）最优子结构。如果一个问题的最优解中包含了其子问题的最优解，就说该问题具有最优子结构。当一个问题具有最优子结构时，提示我们动态规划法可能会适用，但是此时贪心策略可能也是适用的。

（2）重叠子问题。指用来解原问题的递归算法可反复地解同样的子问题，而不是总在产生新的子问题。即当一个递归算法不断地调用同一个问题时，就说该问题包含重叠子问题。此时若用分治法递归求解，则每次遇到子问题都会视为新问题，会极大地降低算法的效率，而动态规划法总是充分利用重叠子问题，对每个子问题仅计算一次，把解保存在一个在需要时就可以查看的表中，而每次查表的时间为常数。

9.4.2　动态规划法的典型实例

【例9.10】0-1背包问题。

有n个物品，第i个物品价值为vi
，重量为wi
，其中vi
和wi
均为非负数，背包的容量为W，W为非负数。现需要考虑如何选择装入背包的物品，使装入背包的物品总价值最大。该问题可以形式化描述如下：

目标函数为

约束条件为
xi
∈｛0，1｝

满足约束条件的任一集合（x_1
，x_2
，…，xn
）是问题的一个可行解，问题的目标是要求问题的一个最优解。考虑一个实例，假设n＝5，W＝17，每个物品的价值和重量如表9-1所示。可将物品1，2和5装入背包，背包未满，获得价值22，此时问题解为（1，1，0，0，1）。也可以将物品4和5装入背包，背包装满，获得价值24，此时解为（0，0，0，1，1）。

表9-1　物品的价值和重量

下面根据动态规划的4个步骤求解该问题。

（1）刻画0-1背包问题的最优解的结构。

可以将背包问题的求解过程看作是进行一系列的决策过程，即决定哪些物品应该放入背包，哪些物品不放入背包。如果一个问题的最优解包含了物品n，即xn
＝1，那么其余x_1
，x_2
，…，xn－1
一定构成子问题1，2，…，n－1在容量W－wn
时的最优解。如果这个最优解不包含物品n，即xn
＝0，那么其余x_1
，x_2
，…，xn－1
一定构成子问题1，2，…，n－1在容量W时的最优解。

（2）递归定义最优解的值。

根据上述分析的最优解的结构递归地定义问题最优解。设c[i，w]表示背包容量为w时，i个物品导致的最优解的总价值，得到下式。显然要求c[n，W]。

（3）计算背包问题最优解的值。

基于上述递归式，以自底向上的方式计算最优解的值，伪代码如下：

上述伪代码的时间复杂度为Ο（nW）。表9-2给出了上述实例填表的信息，从表中很容易看出最优解的值为24。

表9-2　0-1背包问题的动态规划法示例

（4）根据计算的结果，构造问题最优解。

根据上一步计算的c数组，很容易构造问题的最优解。判断c[i，w]与c[i－1，w]的值是否相等，若相等，则说明xi
＝0，否则为1。得到最优解的伪代码如下：

构造最优解的时间复杂度为Ο（n）。上述例子中，C[5，17]＝24≠C[4，17]＝21，x_5
＝1；C[4，8]＝11≠C[3，8]＝10，x_4
＝1；C[2，0]＝C[2，0]＝C[1，0]＝0，x_3
＝x_2
＝x_1
＝0，因此最优解为（0，0，0，1，1）。

【例9.11】矩阵链乘问题。

给定n个矩阵｛A_1
，A_2
，…，An
｝，其中Ai
与Ai＋1
是可乘的，i＝1，2，…，n－1。考虑这n个矩阵的乘积。由于矩阵乘法满足结合律，故计算矩阵的连乘积可以有许多不同的计算次序。这种计算次序可以用加括号的方式确定。若一个矩阵链乘的计算次序完全确定，这时就说该链乘已完全加括号。完全加括号的矩阵链乘可递归地定义为：

（1）单个矩阵是完全加括号的。

（2）矩阵链乘的乘积A是完全加括号的，则A可表示为2个完全加括号的矩阵链乘的乘积B和C的乘积并加括号，即A＝（BC）。

例如，矩阵链乘A_1
A_2
A_3
A_4
可以有以下5种完全加括号的方式：（A_1
（A_2
（A_3
A_4
）））、（A_1
（（A_2
A_3
）A_4
））、（（A_1
A_2
）（A_3
A_4
））和（（（A_1
A_2
）A_3
）A_4
）。每一种加括号的方式确定了一个计算的次序。不同的计算次序与矩阵链乘的计算量有密切关系。

为了说明该问题，回忆两个矩阵相乘的相关概念。矩阵A和矩阵B可乘的条件是矩阵A的列数等于矩阵B的行数，若A是一个p×q矩阵，B是一个q×r矩阵，则其乘积C＝AB是一个p×r矩阵，且标准的两个矩阵相乘所需要的计算量为pqr次乘法操作。

考虑三个矩阵｛A_1
，A_2
，A_3
｝链乘的例子。设这三个矩阵的维数分别为10×100、100×5和5×50。若按第一种加括号方式（（A_1
A_2
）A_3
）计算，则所需要的乘法次数为10×100×5＋10×5×50＝7500。若按第二种加括号方式（A_1
（A_2
A_3
））计算，则所需要的乘法次数为10×5×50＋10×100×50＝75000。第二种加括号方式乘法次数是第一种的10倍。由此可以看出，不同的加括号方式确定的不同的计算次序对矩阵链乘的运算量影响是巨大的。

矩阵链乘问题定义如下：给定n个矩阵｛A_1
，A_2
，…，An
｝，矩阵Ai
的维数为pi－1
×pi
，i＝1，2，…，n，如何给矩阵链乘A_1
×A_2
×…×An
完全加上括号，使得矩阵链乘中乘法次数最少。

矩阵链乘是非常难的问题，若用穷尽搜索法，能够证明需要指数时间来求解。现在考虑用动态规划法来求解矩阵链乘问题。

（1）刻画矩阵链乘问题的结构。

为简便起见，用Ai..j
表示矩阵乘积Ai
Ai＋1
...Aj
的结果，其中i≤j。若问题是非平凡的，即i＜j，那么乘积Ai
Ai＋1
...Aj
一定在Ak
与Ak＋1
之间被分裂，i≤k＜j。问题Ai
Ai＋1
...Aj
完全加括号的开销等于计算矩阵Ai..k
与计算Ak＋1..j
的开销，再加上它们的结果相乘的开销。

问题的最优子结构可刻画如下：假定问题Ai
Ai＋1
...Aj
被完全加括号的最优方式是在Ak
与Ak＋1
之间被分裂，那么分裂之后，最优解Ai
Ai＋1
…Aj
中的子链Ai
Ai＋1
…Ak
一定是问题Ai
Ai＋1
…Ak
的最优加括号方式。同样，最优解Ak＋1
Ak＋2
…Aj
中的子链一定是问题Ak＋1
Ak＋2
…Aj
的最优加括号方式。

（2）递归定义最优解的值。

根据问题的最优解递归地定义问题最优解的开销。设m[i，j]表示计算Ai..j
所需的最小乘法次数，得到下列递归式。对于原问题，计算A1..n
的最小开销则为m[1，n]。

（3）计算最优解的值。

根据上述递归式，以自底向上的方式计算最优开销。假设输入为p=<p_0
，p_1
，…，pn
>，用表m[1..n，1..n]存储m[i，j]，用辅助表s[1..n，1..n]存储哪一个k使得计算m[i，j]时达到最优。最终需要利用s构造问题的一个最优解。算法的伪代码如下。

由上述伪代码很容易得到算法的时间复杂度为Ο（n^3
）。举个例子说明上述过程，假设n＝6，p=<p_0
，p_1
，…，p_6
>=<30，35，15，5，10，20，25>。则根据上述伪代码，图9-4给出了m[i，j]的计算次序，以及m[i，j]和s[i，j]的值。

（4）构造最优解。

上一步给出了计算矩阵乘积的最少乘法次数（最优解的值），但没有给出具体的乘法次序（最优解）。可以由辅助表s的信息构造最优解，伪代码如下。

图9-4　矩阵链乘的动态规划算法示例

上述例子调用OutputOptimalParens（s,1,6）后得到输出结果（（A_1
（A_2
A_3
））（（A_4
A_5
）A_6
））。

【例9.12】最长公共子序列（LCS）。

非形式化的讲，子序列是指从给定序列中随意地（不一定是连续的）去掉若干元素（可能一个也不去掉）后所形成的序列。令序列X＝x_1
x_2
…xm
，序列Y＝y_1
y_2
…yk
是X的子序列，存在X的一个严格递增下标序列＜i_1
，i_2
，…，ik
＞，使得对所有的j＝1，2，…，k有xij
＝yj
。例如，X＝ABCBDAB，Y＝BCDB是X的一个子序列。

给定两个序列X和Y，称序列Z是X和Y的公共子序列，是指Z同时是X和Y的子序列。最长公共子序列问题定义为：给定序列X＝x_1
x_2
…xm
和序列Y＝y_1
y_2
…yn
，求这两个序列的最长公共子序列。

如果用穷举法求解该问题，列举出X的所有子序列，并一一检查其是否是Y的子序列，并随时记录所发现的公共子序列，最终求出最长公共子序列，时间复杂度为Ο（2m
n），是指数级的时间复杂度，对于长序列来说不可行。而动态规划法可以有效地求解最长公共子序列问题。

（1）刻画最长公共子序列问题的最优子结构。

定理9.2证明了LCS具有最优子结构。

【定理9.2】LCS的最优子结构。设X＝x_1
x_2
…xm
和Y＝y_1
y_2
…yn
是两个序列，Z＝z_1
z_2
…zk
是X和Y的一个最长公共子序列。

（i）如果xm
＝yn
，那么zk
＝xm
＝yn
，且Zk－1
是Xm－1
和Yn－1
的一个最长公共子序列。

（ii）如果xm
≠yn
，那么zk
≠xm
，蕴含着Z是Xm－1
和Y的一个最长公共子序列。

（iii）如果xm
≠yn
，那么zk
≠yn
，蕴含着Z是X和Yn－1
的一个最长公共子序列。

（2）递归定义最优解的值。

设l[i，j]表示序列Xi
和Yj
的最长公共子序列的长度，如果i＝0或j＝0，则其中有一个序列长度为0，因此LCS长度为0。由LCS的最优子结构可导出以下递归式。

（3）计算最优解的值。

根据上述递归式，自底向上地求出最优解的值。将l[i，j]的值存储在表l[1..m，1..n]中，以行为主序从左到右计算表l中的元素，同时维持表b[1..m，1..n]，用其中的元素b[i，j]记录使得l[i，j]取最优值的最优子结构，以下给出该算法的伪代码。

从上述伪代码可知，算法的时间复杂度为Ο（mn）。根据上述算法，已知X＝ABCBDAB和Y＝BDCABA，则对应的表l和b如图9-5所示。

图9-5　用动态规划求解LCS问题示例

（4）构造最优解。

用表b中的信息构造X和Y的一个LCS。从b[m，n]开始，在表中沿着箭头的方向跟踪，当b[i，j]＝″↖″时，表示xi
＝yj
为LCS中的元素，伪代码如下所示。在图9-6所示的例子中，过程OutputLCS将产生如下输出：BCBA。

9.5　贪心法

9.5.1　贪心法的基本思想

作为一种算法设计技术，贪心法是一种简单的方法。和动态规划法一样，贪心法也经常用于解决最优化问题。不过与动态规划法不同的是，贪心法在解决问题的策略上是仅根据当前已有的信息做出选择，而且一旦做出了选择，不管将来有什么结果，这个选择都不会改变。换言之，贪心法并不是从整体最优考虑，它所做出的选择只是在某种意义上的局部最优。这种局部最优选择并不能保证总能获得全局最优解，但通常能得到较好的近似最优解。举一个简单的贪心法例子，平时购物找钱时，为使找回的零钱的硬币数最少，从最大面值的币种开始，按递减的顺序考虑各币种，先尽量用大面值的币种，当不足大面值币种的金额时才去考虑下一种较小面值的币种。这就是在采用贪心法。这种方法在这里总是最优，是因为银行对其发行的硬币种类和硬币面值的巧妙安排。如果只有面值分别为1，5和11单位的硬币，而希望找回总额为15单位的硬币，按贪心算法，应找1个11单位面值的硬币和4个1单位面值的硬币，共找回5个硬币。但最优的解答应是3个5单位面值的硬币。

我们知道，贪心算法并不总能得到最优解，那么对一个具体的问题，如何得知是否可以用贪心法来求解，以及能否得到问题的最优解？这个问题很难得到肯定的回答。但是，从许多可以用贪心法求得最优解的问题中看到，这类问题一般具有两个重要的性质。

（1）最优子结构。当一个问题的最优解包含其子问题的最优解时，称此问题具有最优子结构。问题的最优子结构是该问题可以采用动态规划法或者贪心法求解的关键性质。

（2）贪心选择性质。指问题的整体最优解可以通过一系列局部最优的选择，即贪心选择来得到。这是贪心法和动态规划法的主要区别。证明一个问题具有贪心选择性质也是贪心法的一个难点。

9.5.2　贪心法的典型实例

【例9.13】活动选择问题。

活动选择问题是指若干个具有竞争性的活动要求互斥使用某一公共资源时，如何选择最大的相容活动集合。假设有一个需要使用某一资源（如教室等）的n个活动组成的集合S＝｛a_1
，a_2
，…，an
｝。该资源一次只能被一个资源占用。每个活动ai
有个开始时间si
和结束时间fi
，且0≤si
≤fi
＜∞。一旦被选择后，活动ai
就占据半开时间区间[si
，fi
]。如果两个活动ai
和aj
的时间区间互不重叠，则称活动ai
和aj
是兼容的。活动选择问题就是要选择出一个由互相兼容的活动组成的最大子集合。考虑表9-3中的活动集合，其中各活动已经按结束时间的单调递增顺序进行了排序。从表中可以看到，子集｛a_3
，a_9
，a_11
｝由相互兼容的活动组成。然而，它不是最大的子集，子集｛a_1
，a_4
，a_8
，a_11
｝更大。事实上，｛a_1
，a_4
，a_8
，a_11
｝是一个最大的相互兼容活动子集。另外，还有一个最大子集是｛a_2
，a_4
，a_9
，a_11
｝。

表9-3　活动集合

经分析，该问题具有最优子结构，可以用动态规划法求解。但同时该问题还具有贪心选择性质，因此可以用贪心法更简单地求解。

定义集合Sij
＝｛ak
∈S∶fi
≤Sk
＜fk
≤Sj
｝。为了完整地表示问题，加入两个虚拟活动a_0
和an＋1
，其中f_0
＝0，sn＋1
＝∞，这样S＝S0,n＋1
。

【定理9.3】对于任意非空子问题Sij
，设am
是Sij
中具有最早结束时间的活动。

那么，（1）活动am
在Sij
的某个最大兼容活动子集中。

（2）子问题Sim
为空，所以选择am
将使Smj
为唯一可能非空的子问题。

（证明略）

假设对n个活动按其结束时间单调递增进行了排序，排序的时间复杂度为Ο（nlgn）。下面给出解决活动选择问题的贪心算法的递归形式和迭代形式。

递归贪心算法：

迭代贪心算法：

这两个版本都能在Ο（n）的时间复杂度内完成。

【例9.14】背包问题。

背包问题的定义与0-1背包问题类似，但是每个物品可以部分装入背包，即在0-1背包问题中，xi
＝0或者xi
＝1；而在背包问题中，0≤xi
≤1。

为了更好地分析该问题，考虑一个例子：n＝5，W＝100，表9-4给出了各个物品的重量、价值和单位重量的价值。假设物品已经按其单位重量的价值从大到小排好序。

表9-4　物品的基本信息

为了得到最优解，必须把背包放满。现在用贪心策略求解，首先要选出度量的标准。

（1）按最大价值先放背包的原则。

此时，先放物品1和4，获得价值65＋60＝125，背包还剩容量100－30－50＝20，此时物品5是价值最大的，但其重量为40，不能全部放入背包。而且一般将物品2和3放入背包比把物品5的一半放入背包能获得更大的价值。因此把物品2放入背包，得到价值125＋20＝145，剩余容量20－10＝10。此时可再放入物品3的1/3，得到总价值145＋1.5×10＝160，对应的解为｛1，1，1/3，1，0｝。

（2）按最小重量先放背包的原则。

此时，将物品2，3，1和5放入背包中，刚好装满，得到价值20＋30＋65＋40＝155，对应的解为｛1，1，1，0，1｝。

（3）按最大单位重量价值先放背包的原则。

此时，将物品1，2和3放入背包中，得到价值65＋20＋30＝115，剩余容量100－30－10－20＝40。此时可再放入物品4的4/5，得到总价值115＋4/5×60＝163，对应的解为｛1，1，1，4/5，0｝。可以证明，该解为问题的最优解。

假设对n个物品按其单位重量价值从大到小进行了排序，排序的时间复杂度为Ο（nlgn）。下面给出用贪心法解决背包问题的伪代码。

【例9.15】多机调度问题。

假设有n个独立的作业｛1，2，…，n｝，由m台相同的机器｛M_1
，M_2
，…，Mm
｝进行加工处理，作业i所需的处理时间为ti
，每个作业均可在任意一台机器上加工处理，但不可间断或拆分，即一旦一个作业在某台机器上加工处理，便不会再转移到其他的机器上。多机调度问题要求给出一种作业调度方案，使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。

多机调度问题是NP难问题，到目前还没有有效的算法。对于这类问题，用贪心算法求解有时可以得到较好的近似解。贪心法求解多机调度问题时的贪心策略是最长处理时间优先。当m≥n时，只要将机器i上的[0，ti
]时间区间分配给作业i即可；当m＜n时，首先将n个作业按其所需的处理时间从大到小排序，然后依此顺序将作业分配给空闲的机器。例如，假设有7个作业｛1，2，3，4，5，6，7｝，由3台机器｛M_1
，M_2
，M_3
｝加工处理，各个作业所需要的时间分别为｛2，14，4，16，6，5，3｝。首先将这7个作业按其处理时间从大到小排序，则作业｛4，2，5，6，3，7，1｝的处理时间为｛16，14，6，5，4，3，2｝。图9-6给出了按最长处理时间作业有限的原则时，这7个作业的调度情况及其所需要的总加工处理时间。

图9-6　三台机器的调度问题示例

假设对n个作业按其加工处理时间从大到小进行了排序，排序的时间复杂度为Ο（nlgn）。下面给出用贪心法解决多机调度问题的伪代码。

其中T、d和S分别表示n个作业的处理时间数组、m台机器的空闲时间数组、m台机器所处理的作业列表数组。

9.6　回溯法

回溯法有“通用的解题法”之称，用它可以系统地搜索一个问题的所有解或任一解。回溯法是一个既带有系统性又带有跳跃性的搜索算法。它在包含问题的所有解的解空间树中，按照深度优先的策略，从根节点出发搜索解空间树。算法搜索至解空间树的任一节点时，总是先判断该节点是否肯定不包含问题的解。如果肯定不包含，则跳过对以该节点为根的子树的系统搜索，逐层向其祖先节点回溯；否则，进入该子树，继续按深度优先的策略进行搜索。回溯法在用来求问题的所有解时，要回溯到根，且根节点的所有子树都已被搜索遍才结束；而用来求问题的任一解时，只要搜索到问题的一个解就可结束。这种以深度优先的方式系统地搜索问题的解的方法称为回溯法，它适用于解一些组合数较大的问题。

9.6.1　回溯法的算法框架

1．问题的解空间

应用回溯法解问题时，首先应明确定义问题的解空间。问题的解空间应至少包含问题的一个（最优）解。例如，对于有n种可选择物品的0-1背包问题，其解空间由长度为n的0-1向量组成。该解空间包含了对变量的所有可能的0-1赋值。当n＝3时，其解空间是｛（0，0，0），（0，1，0），（0，0，1），（1，0，0），（0，1，1），（1，0，1），（1，1，0），（1，1，1）｝。

定义了问题的解空间后，还应将解空间很好地组织起来，使得用回溯法能方便地搜索整个解空间。通常将解空间表示为树或图的形式。例如，对于n＝3时的0-1背包问题，其解空间用一棵完全二叉树表示，如图9-7所示。

解空间树的第i层到第i＋1层边上的标号给出了变量的值。从树根到叶子的任一路径表示解空间的一个元素。例如，从根节点到节点H的路径对应于解空间中的元素（1，1，1）。

2．回溯法的基本思想

确定了解空间的组织结构后，回溯法从开始节点（根节点）出发，以深度优先的方式搜索整个解空间。这个开始节点就成为一个活节点，同时也成为当前的扩展节点。在当前的扩展节点处，搜索向纵深方向移至一个新节点。这个新节点就成为一个新的活节点，并成为当前扩展节点。如果在当前扩展节点处不能再向纵深方向移动，则当前的扩展节点就成为死节点。换句话说，这个节点不再是一个活节点。此时，应往回移动（回溯）至最近的一个活节点处，并使这个活节点成为当前的扩展节点。回溯法即以这种工作方式递归地在解空间中搜索，直至找到所要求的解或解空间中已无活节点时为止。

例如，对于n＝3时的0-1背包问题，考虑下面的具体实例：w＝[16，15，15]，p＝[45，25，25]，c＝30。其中w表示物品的重量，p表示物品的价值，c表示背包能够容纳的最大重量。从图9-7的根节点开始搜索其解空间。

图9-7　0-1背包问题的解空间树

（1）开始时根节点是唯一的活节点，也是当前的扩展节点。在这个扩展节点处，按照深度优先策略移至节点B或节点C。假设选择先移至节点B。此时节点A和节点B是活节点，节点B成为当前的扩展节点。由于选取了w1（节点A到B的边上标记为1，表示选择物品），故在节点B处剩余背包容量是r＝14，获取的价值是45。

（2）从节点B处可以移至节点D或E。由于移至节点D需要w2＝15的背包容量，而现在的背包容量是r＝14，故移至节点D导致一个不可行的解。而搜索至节点E不需要占用背包容量（节点B到节点E的边上标记为0，表示不需要选择物品），因而是可行的。从而选择移至节点E。此时，E成为新的扩展节点，节点A、B和E是活节点。在节点E处，r＝14，获取的价值为45。

（3）从节点E处可以移至节点J或K。移至节点J导致一个不可行解，而移至节点K是可行的，于是移至节点K，它成为一个新的扩展节点。由于节点K是一个叶节点，故得到一个可行解，这个解对应的价值为45。解x的取值是由根节点到叶节点K的路径所唯一确定，即x＝（1，0，0）。由于在节点K处已不能再向纵深扩展，所以节点K成为死节点。返回到节点E，此时在E处也没有可扩展的节点，它也成为一个死节点。

（4）返回节点B处，节点B同样也成为死节点，从而节点A再次成为当前扩展节点。节点A还可以继续扩展，从而达到节点C。此时r＝30，获取的价值为0。

（5）从节点C可移至节点F或G。假设移至节点F，它成为新的扩展节点。节点A、C和F是活节点。在节点F处，r＝15，获取的价值为25。从节点F移至节点L处，此时r＝0，获取的价值为50。由于L是一个叶节点，而且是迄今为止找到的获取价值最高的可行解，因此记录这个可行解。节点L不可扩展，返回到节点F处。

按此方式继续搜索，可搜索整个解空间。搜索结束后找到的最好解就是0-1背包问题的最优解。

综上所述，运用回溯法解题通常包含以下三个步骤。

（1）针对所给问题，定义问题的解空间。

（2）确定易于搜索的解空间结构。

（3）以深度优先的方式搜索解空间。

3．回溯法的算法框架

回溯法的算法框架有非递归和递归两种方式。

非递归方式：

4．回溯法的限界函数

问题的解空间往往很大，为了有效地进行搜索，需要在搜索的过程中对某些节点进行剪枝。而对哪些节点进行剪枝，需要设计限界函数来判断。因此，限界函数的设计是回溯法的一个核心问题，也是一个很难的问题。设计限界函数的通用的指导原则是尽可能多和尽可能早地“杀掉”不可能产生最优解的活节点。好的限界函数可以大大减少问题的搜索空间，从而大大提高算法的效率。下面通过例子来说明。

9.6.2　回溯法的典型实例

【例9.16】0-1背包问题。

0-1背包问题的定义如例9.10所示。

图9-7给出了0-1背包问题的一个解空间树的示例。在该问题中，目标是为了得到最大的价值，因此可以“杀掉”那些不可能产生最大价值的活节点。那么如何判断哪些节点扩展后不可能产生最大价值呢？考虑贪心策略，先对所有物品按其单位重量价值从大到小排序，对搜索空间树中的某个节点如F，已经确定了某些X（i），1≤i≤k，而其他的X（i），k＋1≤i≤n待定。此时可以将0-1背包问题松弛为背包问题，求从F节点扩展下去，计算能获得的最大价值。若该价值比当前已经得到的某个可行解的值要小，则该节点不必再扩展。

假设所有物品已经按其单位重量价值从大到小排序。假设前k（包含k）个物品是否放入背包已经确定，现在考虑在当前背包的剩余容量下，若是背包问题，那么能获得的最大价值是多少？即求背包中物品的价值上界，伪代码如下所示。

其中v、w、k和W分别表示当前已经获得的价值、背包的重量、已经确定的物品数和背包的总容量。

其中，W、n、w、v、fw、fp和X分别表示背包的总容量、物品个数、重量数组、价值数组、获得最大价值时背包的重量、背包获得的最大价值和问题的最优解。

假设n＝8，W＝110，物品的价值和重量如表9-5所示。

表9-5　物品的基本信息

则根据上述伪代码得到图9-8所示的搜索空间树。

图9-8　0-1背包问题的回溯法示例

图9-8所示树中的节点内若有数据，则上面表示背包当前的重量，下面表示背包当前的价值；节点内若无数据，则旁边的数据表示在已有的选择下，背包能获得的价值的上界。X（i）＝1和X（i）＝0分别表示第i个物品放入和不放入背包中。浅灰色节点表示对应可行解的值，如存在5个可行解，其值分别为139，149，151，96，159，对应的解分别为X_1
＝（1，1，1，1，1，0，0，0），X_2
＝（1，1，1，1，0，1，0，0），X_3
＝（1，1，1，1，0，0，1，0），X_4
＝（1，1，1，1，0，0，0，0），X_5
＝（1，1，1，0，1，1，0，0）。其中X_5
为最优解，其值为159。

【例9.17】n-皇后问题。

这是来源于国际象棋的一个问题。n后问题要求在一个n×n格的棋盘上放置n个皇后，使得它们彼此不受攻击。按照国际象棋的规则，一个皇后可以攻击与之处在同一行或同一列或同一条斜线上的其他任何棋子。因此，n后问题等价于要求在一个n×n格的棋盘上放置n个皇后，使得任何两个皇后不能被放在同一行或同一列或同一条斜线上。

求解过程从空棋盘开始，设在第1行至第m行都已经正确放置了m个皇后的基础上，再在第m＋1行上找合适的位置放第m＋1个皇后。直至在第n行也找到合适的位置放置第n个皇后时，就找到了一个解。接着改变第n行上皇后的位置，希望获得下一个解。另外，在任一行上有n种可选的位置。开始时，位置在第1列，以后改变时，顺次选择第2列、第3列、…、第n列。当第n列也不是一个合理的位置时，就要回溯，去改变前一行的位置。图9-9给出了回溯法求解4皇后问题的搜索过程。

图9-9　回溯法求解4皇后问题的搜索过程

n皇后问题的限界函数可以根据问题的定义直接设计，即任意两个皇后不在同行、同列和同一斜线上，伪代码如下。

用回溯法得到皇后问题的伪代码如下。

9.7　分支限界法

分支限界法类似于回溯法，也是一种在问题的解空间树T上搜索问题解的算法。但在一般情况下，分支限界法与回溯法的求解目标不同。回溯法的求解目标是找出T中满足约束条件的所有解，而分支限界法的求解目标则是找出满足约束条件的一个解，或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解，即在某种意义下的最优解。

由于求解目标不同，导致分支限界法与回溯法在解空间树T上的搜索方式也不相同。回溯法以深度优先的方式搜索解空间树T，而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树T。分支限界法的搜索策略是每一个活节点只有一次机会成为扩展节点。活节点一旦成为扩展节点，就一次性产生其所有儿子节点。在这些儿子节点中，那些导致不可行解或非最优解的儿子节点被舍弃，其余儿子节点被加入活节点表中。此后，从活节点表中取下一节点成为当前扩展节点，并重复上述节点扩展过程。这个过程一直持续到找到所需的解或活节点表为空时为止。人们已经利用分支限界法解决了大量离散最优化的实际问题。

与回溯法相似，限界函数的设计是分支限界法的一个核心问题，也是一个很难的问题。如何设计好限界函数来有效地减小搜索空间是应用分支限界法要考虑的问题。

从活节点表中选择下一扩展节点的不同方式导致不同的分支限界法。最常用的有以下两种。

（1）队列式（FIFO）分支限界法。队列式分支限界法将活节点表组织成一个队列，并按队列的先进先出原则选择下一个节点作为扩展节点。

（2）优先队列式分支限界法。优先队列式分支限界法将活节点表组织成一个优先队列，并按优先队列中规定的节点优先级选取优先级最高的下一个节点作为扩展节点。

优先队列中规定的节点优先级通常用一个与该节点相关的数值p来表示。节点优先级的高低与p值的大小相关。最大优先队列规定p值较大的节点优先级较高。在算法实现时，通常用一个最大堆来实现最大优先队列，用最大堆的Deletemax运算抽取堆中下一个节点成为当前扩展节点。类似地，最小优先队列规定p值较小的节点优先级较高。在算法实现时，常用一个最小堆来实现最小优先队列，用最小堆的Deletemin运算抽取堆中下一个节点成为当前扩展节点。

例如，n＝3时0-1背包问题的一个实例：w＝[16，15，15]，p＝[45，25，25]，c＝30，其解空间树如图9-7所示。

用队列式分支限界法解此问题时，用一个队列来存储活节点表。算法从根节点A出发。

（1）初始时活节点队列为空。

（2）节点A是当前扩展节点，它的两个儿子节点B和C均可谓可行节点，故将这两个儿子节点按照从左到右的顺序加入活节点队列，并且舍弃当前扩展节点A。

（3）按照先进先出的原则，下一个扩展节点是活节点队列的队首节点B。扩展节点B得到其儿子节点D和E。由于D是不可行节点，故被舍去。E是可行节点，被加入活节点队列。此时活节点队列中的元素是C和E。

（4）C成为当前扩展节点，它的两个儿子节点F和G均为可行节点，因此被加入活节点队列。此时活节点队列中的元素是E、F、G。

（5）扩展下一个节点E，得到节点J和K。J是不可行节点，因而被舍去。K是一个可行的叶节点，表示所求问题的一个可行解，其价值为45。此时活节点队列中的元素是F和G。

（6）当前活节点队列的队首节点F成为下一个扩展节点。它的两个儿子节点L和M均为叶节点。L表示获得价值为50的可行解，M表示获得价值为25的可行解。

（7）G是最后一个扩展节点，其儿子节点N和O均为可行叶节点。最后活节点队列为空，算法终止。算法搜索得到最优值为50。

9.8　概率算法

前面讨论的算法对于所有合理的输入都给出正确的输出，概率算法将这一条件放宽，把随机性的选择加入到算法中，在算法执行某些步骤时，可以随机地选择下一步该如何进行，同时允许结果以较小的概率出现错误，并以此为代价，获得算法运行时间的大幅度减少。概率算法的一个基本特征是对所求解问题的同一实例用同一概率算法求解两次，可能得到完全不同的效果。这两次求解所需的时间甚至所得到的结果可能会有相当大的差别。如果一个问题没有有效的确定性算法可以在一个合理的时间内给出解，但是该问题能接受小概率错误，那么采用概率算法就可以快速找到这个问题的解。

一般情况下，概率算法具有以下基本特征。

（1）概率算法的输入包括两部分，一部分是原问题的输入，另一部分是一个供算法进行随机选择的随机数序列。

（2）概率算法在运行过程中，包括一处或多处随机选择，根据随机值来决定算法的运行。

（3）概率算法的结果不能保证一定是正确的，但能限制其出错概率。

（4）概率算法在不同的运行过程中，对于相同的输入实例可以有不同的结果，因此，对于相同的输入实例，概率算法的执行时间可能不同。

概率算法大致分为4类：数值概率算法、蒙特卡罗（Monte Carlo）算法、拉斯维加斯（Las Vegas）算法和舍伍德（Sherwood）算法。

（1）数值概率算法。数值概率算法常用于数值问题的求解。这类算法所得到的往往是近似解，且近似解的精度随计算时间的增加不断提高。在多数情况下，要计算出问题的精确解是不可能的或没有必要的，因此用数值概率算法可得到相当满意的解。

（2）蒙特卡罗算法。蒙特卡罗算法用于求问题的精确解。用蒙特卡罗算法能求得问题的一个解，但这个解未必是正确的。求得正确解的概率依赖于算法所用的时间，算法所用的时间越多，得到正确解的概率就越高。蒙特卡罗算法的主要缺点也在于此，一般情况下，无法有效地判定所得到的解是否肯定正确。

（3）拉斯维加斯算法。拉斯维加斯算法不会得到不正确的解。一旦用拉斯维加斯算法找到一个解，这个解就一定是正确解。拉斯维加斯算法找到正确解的概率随着它所用的计算时间的增加而提高。对于所求解问题的任一实例，用同一拉斯维加斯算法反复对该实例求解足够多次，可使求解失效的概率任意小。

（4）舍伍德算法。舍伍德算法总能求得问题的一个解，且所求得的解总是正确的。当一个确定性算法在最坏情况下的计算复杂度与其在平均情况下的计算复杂度有较大差别时，可在这个确定性算法中引入随机性将它改造成一个舍伍德算法，消除或减少问题的好坏实例间的这种差别。舍伍德算法的精髓不是避免算法的最坏情况行为，而是设法消除这种最坏情形行为与特定实例之间的关联性。

9.9　近似算法

迄今为止，所有的难解问题都没有多项式时间算法，采用回溯法和分支限界法等算法设计技术可以相对有效地解决这类问题。然而，这些算法的时间性能常常是无法保证的。近似算法是解决难解问题的一种有效策略，其基本思想是放弃求最优解，而用近似最优解代替最优解，以换取算法设计上的简化和时间复杂度的降低。近似算法是这样一个过程：虽然它可能找不到一个最优解，但它总会与待求解的问题提供一个解。为了具有实用性，近似算法必须能够给出算法所产生的解与最优解之间的差别或者比例的一个界限，它保证任意一个实例的近似最优解与最优解之间相差的程度。显然，这个差别越小，近似算法越具有实用性。

衡量近似算法性能最重要的标准有如下两个。

（1）算法的时间复杂度。近似算法的时间复杂度必须是多项式阶的，这是近似算法的基本目标。

（2）解的近似程度。近似最优解的近似程度也是设计近似算法的重要目标。近似程度与近似算法本身、问题规模，乃至不同的输入实例有关。

下面通过几个实例来说明近似算法的应用。

【例9.18】顶点覆盖问题。

无向图G＝（V，E）的顶点覆盖是顶点集V的一个子集V′，V′⊆V，使得若（u，v）是G的一条边，则u∈V′或者v∈V′。顶点覆盖V′的大小是它所包含的顶点个数｜V′｜。顶点覆盖问题是求出图G中的最小顶点覆盖，即含有顶点数最少的顶点覆盖。

顶点覆盖问题是一个NP难问题，因此，没有一个多项式时间算法有效的求解。虽然要找到图G的一个最小顶点覆盖是很困难的，但要找到图G的一个近似最小覆盖却很容易。可以采用如下策略：初始时边集E′＝E，顶点集V′＝｛｝，每次从边集E′中任取一条边（u，v），把顶点u和v加入到顶点集V′中，再把与u和v顶点相邻接的所有边从边集E′中删除，直到边集E′为空。显然，最后得到的顶点集V′是无向图的一个顶点覆盖，由于每次把尽量多的相邻边从边集E′中删除，可以期望V′中的顶点数尽量少，但不能保证V′中的顶点数最少。图9-10给出了一个顶点覆盖问题的近似算法求解过程。

图9-10　最小覆盖问题的近似算法求解过程

【例9.19】TSP问题。

TSP问题是指旅行家要旅行n个城市，要求各个城市经历且仅经历一次，然后回到出发城市，并要求所走的路程最短。

TSP问题是著名的NP完全问题，并不存在有效的多项式时间算法，但是可以设计一个近似算法求解。其过程如下：首先采用Prim算法生成图的最小生成树T，如图9-11（b）所示，图中粗线表示最小生成树中的边。然后对T进行深度优先遍历，经过的路线为a→b→c→b→h→b→a→d→e→f→e→g→e→d→a，得到遍历路线为L＝（a,b,c,h,d,e,f,g）。由序列L得到哈密尔顿回路，即近似最优解，如图9-11（d）所示，其路径长度约为19.074。图9-11（e）所示是图9-11（a）的最优解，其路径长度约为16.084。

图9-11　TSP问题的近似算法求解示例

【例9.20】　子集和数问题。

令S＝｛s_1
,s_2
,…,sn
｝是一个正整数的集合，子集和数问题要求在这个正整数集合中，找出其和不超过正整数C的最大和数的子集。

考虑用蛮力法求解子集和数问题，为了求得集合S的所有子集和，先将所有子集和的集合初始化为L_0
＝｛0｝，然后求得子集和中包含s_1
的情况，即L_0
中的每一个元素加上s_1
，用L_0
＋s_1
表示对集合L_0
中的每个元素加上s_1
后得到的新集合，则所有子集和的集合为L_1
＝L_0
＋（L_0
＋s_1
）＝｛0,s_1
｝；再求得子集和中包含s_2
的情况，即L_1
中的每一个元素加上s_2
，所有子集和的集合为L_2
＝L_1
＋（L_1
＋s_2
）＝｛0,s_1
,s_2
,s_1
＋s_2
｝。依此类推，一般情况下，为求得子集和中包含si
（1≤i≤n）的情况，即Li－1
的每一个元素加上Si
，所有子集和的集合为Li
＝Li－1
＋（Li－1
＋si
）。因为子集和要求不超过正整数C，所以每次合并后都要在Li
中删除所有大于C的元素。例如，若S＝｛104，102，201，101｝，C＝308，利用上述算法求解子集和数问题的过程如图9-12所示。

图9-12　蛮力法求解子集和数问题示例

上述算法具有指数时间复杂度，因此也是非常难的问题。现基于上述思想，用近似算法求解，其基本思想是在迭代过程中，对Li
进行适当的修整，使得在子集和不超过一定误差的前提下，尽可能减少Li
中的元素个数，从而获得算法时间性能的提高。具体方法是：用一个修整参数δ（0＜δ＜1），从集合Li
中删去尽可能多的元素得到L1i
，使得每一个从Li
删除的元素y，在集合L1i
中都有一个修整后的元素z满足（1－δ）*y≤z≤y，可以将z看作是被删去元素y在修整后的集合L1i
中的代表。例如，若S＝｛104，102，201，101｝，C＝308，δ＝0.05，利用上述算法求解子集和数问题的过程如图9-13所示。

图9-13　近似算法求解子集和数问题示例

9.10　NP完全性理论

迄今为止，我们接触过的大部分算法都是多项式时间算法，它们在最坏情况下的运行时间为O（nk
），其中n为输入规模，k为某个常数。我们自然会提出一个问题：是否所有问题都能做多项式时间内解决呢？答案是否定的。例如存在一些问题，如著名的停机问题，是任何计算机不论花费多少时间都不能解决的。还有一些问题尽管可以求解，但它们都不能在多项式O（nk
）的时间内得到解决。一般来说，把在多项式时间内可解的问题看作是容易的问题，而把超过多项式时间才能解决的问题看作是难的问题。本节简要介绍NP完全性理论，它是研究计算问题难易以及一类特殊的难解问题的理论。

关于NP完全性的理论研究是基于某种计算模型针对语言识别问题而进行的，本节从算法的角度给出一种非形式化的简单解释。

1．P类问题和NP类问题

在计算复杂性研究中，经常考虑的是判定问题，因为判定问题可以很容易地表达为语言的识别问题，从而方便地在某种计算模型上进行求解。

【定义9.1】判定问题。一个判定问题是仅仅要求回答yes或no的问题。例如，停机问题就是一个判定问题，但是，停机问题不能用任何计算机算法求解，所以不是所有的判定问题都可以在计算机上求解。

在实际应用中，很多问题都不是判定问题。但是，大多数问题可以很容易地转化为相应的判定问题。如排序问题：将一个整数序列调整为非降序序列。其判定形式可以叙述为：给定一个整数序列，是否可以按非降序排列。哈密尔顿回路问题：在图G＝（V，E）中，从某个顶点出发，求经过所有顶点一次且仅一次，再回到出发点的回路。哈密尔顿回路问题的判定形式可以叙述为：在图G＝（V，E）中，是否有一个回路经过所有顶点一次且仅一次，然后回到出发点。

判定问题有一个重要特性：虽然在计算上对问题求解是困难的，但在计算上判定一个待定解是否解决了该问题却是简单的。例如，求解哈密尔顿回路是个难解问题，但是验证一个给定顶点序列是不是哈密尔顿回路却很容易，只需要检查前n个顶点是否互不相同，而且最后一个顶点和第一个顶点是否相同。

【定义9.2】确定性算法。设A是求解问题Ⅱ的一个算法，如果在算法的整个执行过程中，每一步只有一个确定的选择，则称算法A是确定性算法。

【定义9.3】P类问题。如果对于某个判定问题Ⅱ，存在一个非负整数k，对于输入规模为n的实例，能够以O（nk
）的时间运行一个确定性算法，得到yes或no的答案，则该判定问题Ⅱ是一个P类问题。

P类问题是由具有多项式时间的确定性算法来求解的判定问题组成。对于判定问题定义P类问题，主要是为了能够给出较为严格的NP类问题的定义。事实上，所有易解问题都属于P类问题。

【定义9.4】　设A是求解问题Ⅱ的一个算法，如果算法A以如下猜测并验证的方式工作，就称算法A是非确定性算法。

（1）猜测阶段。在这个阶段，对问题的输入实例产生一个任意字符串y，在算法的每一次运行时，串y的值可能不同，因此，猜测以一种非确定的形式工作。

（2）验证阶段。在这个阶段，用一个确定算法验证两件事：首先，检查在猜测阶段产生的串y是否是合适的形式，如果不是，则算法停下来并得到no；另一方面，如果串y是合适的形式，那么算法验证它是否是问题的解，如果是问题的解，则算法停下来并得到yes，否则算法停下来并得到no。

【定义9.5】NP类问题。如果对于某个判定问题Ⅱ，存在一个非负整数k，对于输入规模为n的实例，能够以O（nk
）的时间运行一个非确定性算法，得到yes或no的答案，则该判定问题Ⅱ是一个NP类问题。

对于NP类判定问题，重要的是它必须存在一个确定性算法，能够以多项式时间来检查和验证在猜测阶段所产生的答案。NP类问题是难解问题的一个子类，并不是任何一个在常规计算机上需要指数时间的问题（即难解问题）都是NP类问题。尽管NP类问题是对于判定问题定义的，事实上，可以在多项式时间应用非确定性算法解决的所有问题都属于NP类问题。综上所述，P类问题和NP类问题的主要差别如下。

（1）P类问题可以用多项式时间的确定性算法来进行判定或求解。

（2）NP类问题可以用多项式时间的非确定性算法来进行判定或求解。

如果问题Ⅱ属于P类，则存在一个多项式时间的确定性算法来对它进行判定或求解。显然，对这样的问题Ⅱ，也可以构造一个多项式的非确定性算法来验证其解的正确性。因此，问题Ⅱ也属于NP类问题，即P⊆NP。反之，如果问题Ⅱ属于NP类，则存在一个多项式时间的非确定性算法来猜测并验证它的解，但是，不一定能够构造一个多项式时间的确定性算法来对它进行求解或判定。因此，人们猜测P≠NP。但是，这个不等式是成立还是不成立，至今没有得到证明。

2．NP完全问题

NP完全问题是NP类问题的一个子类，对这个子类中的任何一个问题，如果能够证明用多项式时间的确定性算法来进行求解或判定，那么，NP完全问题中的所有问题都可以通过多项式时间的确定性算法来进行求解或判定。

【定义9.6】问题变换。假设问题Ⅱ′存在一个算法A，对于问题Ⅱ′的输入实例Ⅰ′，算法A求解问题Ⅱ′得到一个输出O′，另外一个问题Ⅱ的输入实例是Ⅰ，对应于输入Ⅰ，问题Ⅱ有一个输出O，则问题Ⅱ变换到问题Ⅱ′是一个三个步骤的过程。

（1）输入转换。把问题Ⅱ的输入Ⅰ转换为问题Ⅱ′的适当输入Ⅰ′。

（2）问题求解。对问题Ⅱ′应用算法A产生一个输出O′。

（3）输出转换。把问题Ⅱ′的输出转换为问题Ⅱ对应于输入Ⅰ的正确输出。

问题变换的一般过程如图9-14所示。

图9-14　问题变换的一般过程

需要强调的是，问题变换的主要目的不是给出解决一个问题的算法，而是给出通过另一个问题理解一个问题的计算时间上下限的一种方式。

【定理9.4】（计算时间下限归约）若已知问题Ⅱ的计算时间下限是T（n），且问题Ⅱ可τ（n）变换到问题Ⅱ′，即Ⅱ∝τ（n）
Ⅱ′，则T（n）-O（τ（n））为问题Ⅱ′的一个计算时间下限。

【定理9.5】（计算时间上限归约）若已知问题Ⅱ的计算时间上限是T（n），且问题Ⅱ可τ（n）变换到问题Ⅱ′，即Ⅱ∝τ（n）
Ⅱ′，则T（n）＋O（τ（n））为问题Ⅱ′的一个计算时间上限。

【定理9.6】设Ⅱ、Ⅱ′和Ⅱ′是三个判定问题，若Ⅱ∝τ（n）
Ⅱ′且Ⅱ′∝τ（n）
Ⅱ″，则Ⅱ∝τ（n）
Ⅱ″。

定理9.4和定理9.5刻画了问题变换与计算复杂性之间的关系，如图9-15所示。而定理9.6则说明了多项式问题变换是可传递的。

图9-15　时间复杂性归约示意图

【定义9.7】NP完全问题。令Ⅱ是一个判定问题，如果问题Ⅱ属于NP类问题，并且对NP类问题中的每一个问题Ⅱ′，都有Ⅱ′∝p
Ⅱ，则称判定问题Ⅱ是一个NP完全问题。

NP完全问题是NP类问题中最难的一类问题，其中任何一个问题至今都没有找到多项式时间算法。而且NP完全问题有一个重要性质：如果一个NP完全问题能在多项式时间内得到解决，那么NP完全问题中的每一个问题都可以在多项式时间内求解。尽管已经进行了多年的研究，目前还没有一个NP完全问题有多项式时间算法。这些问题也许存在多项式时间算法，因为计算机科学是相对新生的科学，肯定还会有新的算法设计技术有待发现；这些问题也许不存在多项式时间算法，但目前缺乏足够的技术来证明这一点。

已经定义了P类问题、NP类问题和NP完全问题等概念，广义上说，P类问题是可以用确定性算法在多项式时间内求解的一类问题，NP类问题是可以用非确定性算法在多项式时间猜测并验证的一类问题，而且P⊆NP。是否存在一些问题属于NP类问题而不属于P类问题呢？至今，还没有人能证明是否P≠NP。若P≠NP，则说明NP类中的所有问题，包括NP完全问题都不存在多项式时间算法；若P＝NP，则说明NP类中的所有问题，包括NP完全问题都具有多项式时间算法。无论哪一种答案，都将为算法设计提供重要的指导和依据。

NP类问题中还有一些问题，人们还不知道它是属于P类问题还是属于NP完全问题，这些问题还在等待人们证明它们的归属。这类问题包括图的同构问题和线性规划问题。

3．典型的NP完全问题

1971年，Cook通过Cook定理证明了可满足问题（SAT问题）是NP完全的。1972年，Karp证明了十几个问题都是NP完全的。这些NP完全问题的证明思想和技巧，以及利用它们证明的几千个NP完全问题，极大地丰富了NP完全理论。下面列出的是一些基本的NP完全问题。

（1）SAT问题。SAT问题也称为合取范式的可满足问题，来源于许多实际的逻辑推理的应用。一个合取范式形如A_1
∧A_2
∧…∧An
，子句Ai
（1≤i≤n）形如a_1
∨a_2
∨…∨ak
，其中，ai
（1≤i≤k）称为文字，为某一布尔变量或该布尔变量的非。SAT问题是指是否存在一组对所有布尔变量的赋值（TRUE或FALSE），使得整个合取范式取值为真。

（2）最大团问题。图G＝（V，E）的团是图G的一个完全子图，即子图中任意两个互异的顶点都有一条边相连。团问题是对于给定的无向图G＝（V，E）和正整数k，是否存在具有k个顶点的团。

（3）图着色问题。给定无向连通图G＝（V，E）和正整数k，是否可以用k种颜色对G中的顶点着色，使得任意两个相邻顶点着色不同。

（4）哈密尔顿问题。在图G＝（V，E）中，从某个顶点出发，求经过所有顶点一次且仅一次，再回到出发点的回路。

（5）TSP问题。给定带权图G＝（V，E）和正整数k，是否存在一条哈密尔顿回路，其路径长度小于k。

（6）顶点覆盖问题。设图G＝（V，E），V′是顶点V的子集，若图G的任一条边至少有一个顶点属于V′，则称为图G的顶点覆盖。

顶点覆盖问题是对于图G＝（V，E）和正整数k，是否存在顶点V的一个子集V′，使得图G的任一条边至少有一个顶点属于V′，且｜V′｜≤k。

（7）最长路径问题。给定一个带权图G＝（V，E）和一个正整数k，对于图G中的任意两个顶点vi
，vj
∈V（1≤i，j≤n，i≠j），是否存在从顶点vi
到顶点vj
的长度大于k的简单路径。

（8）子集和问题。给定一个整数集合S和一个正整数k，判断是否存在S的一个子集S′，使得S′中整数的和为k。

第10章　面向对象技术

面向对象（Object-Oriented, OO）方法是一种非常实用的软件开发方法，它一出现就受到软件技术人员的青睐，现在已经成为计算机科学研究的一个重要领域，并逐渐成为软件开发的一种主要方法。面向对象方法以客观世界中的对象为中心，其分析和设计思想符合人们的思维方式，分析和设计的结果与客观世界的实际比较接近，容易被人们所接受。在面向对象方法中，分析和设计的界线并不明显，它们采用相同的符号表示，能方便地从分析阶段平滑地过渡到设计阶段。此外，在现实生活中，用户的需求经常会发生变化，但客观世界的对象以及对象间的关系相对比较稳定，因此用面向对象方法分析和设计的结果也相对比较稳定。

10.1　面向对象的基本概念

Peter Coad和Edward Yourdon提出用下面的等式识别面向对象方法。

面向对象＝对象（object）＋分类（classification）＋继承（inheritance）＋通过消息的通信（communication with messages）

可以说，采用这4个概念开发的软件系统是面向对象的。

1．对象

在面向对象的系统中，对象是基本的运行时的实体，它既包括数据（属性），也包括作用于数据的操作（行为）。所以，一个对象把属性和行为封装为一个整体。封装是一种信息隐蔽技术，它的目的是使对象的使用者和生产者分离，使对象的定义和实现分开。从程序设计者来看，对象是一个程序模块；从用户来看，对象为他们提供了所希望的行为。在对象内的操作通常叫做方法。一个对象通常可由对象名、属性和操作三部分组成。

在现实世界中，每个实体都是对象，如学生、汽车、电视机和空调等都是现实世界中的对象。每个对象都有它的属性和操作，如电视机有颜色、音量、亮度、灰度、频道等属性，可以有切换频道、增大／减低音量等操作。电视机的属性值表示了电视机所处的状态，而这些属性只能通过其提供的操作来改变。电视机的各组成部分，如显像管、电路板和开关等都封装在电视机机箱中，人们不知道也不关心电视机是如何实现这些操作的。

2．消息

对象之间进行通信的一种构造叫做消息。当一个消息发送给某个对象时，包含要求接收对象去执行某些活动的信息。接收到信息的对象经过解释，然后予以响应。这种通信机制叫做消息传递。发送消息的对象不需要知道接收消息的对象如何对请求予以响应。

3．类

一个类定义了一组大体上相似的对象。一个类所包含的方法和数据描述一组对象的共同行为和属性。把一组对象的共同特征加以抽象并存储在一个类中的能力，是面向对象技术最重要的一点。是否建立了一个丰富的类库，是衡量一个面向对象程序设计语言成熟与否的重要标志。

类是在对象之上的抽象，对象是类的具体化，是类的实例（instance）。在分析和设计时，通常把注意力集中在类上，而不是具体的对象。也不必为每个对象逐个定义，只需对类作出定义，而对类的属性的不同赋值即可得到该类的对象实例。

有些类之间存在一般和特殊关系，即一些类是某个类的特殊情况，某个类是一些类的一般情况。这是一种is-a关系，即特殊类是一种一般类。例如“汽车”类、“轮船”类、“飞机”类都是一种“交通工具”类。特殊类是一般类的子类，一般类是特殊类的父类。同样，“汽车”类还可以有更特殊的类，如“轿车”类、“货车”类等。在这种关系下形成一种层次的关联。

通常把一个类和这个类的所有对象称为“类及对象”或对象类。

4．继承

继承是父类和子类之间共享数据和方法的机制。这是类之间的一种关系，在定义和实现一个类的时候，可以在一个已经存在的类的基础上来进行，把这个已经存在的类所定义的内容作为自己的内容，并加入若干新的内容。图10-1表示了父类A和它的子类B之间的继承关系。

图10-1　类的继承关系

一个父类可以有多个子类，这些子类都是父类的特例，父类描述了这些子类的公共属性和操作。一个子类可以继承它的父类（或祖先类）中的属性和操作，这些属性和操作在子类中不必定义，子类中还可以定义自己的属性和操作。

图10-1中的B只从一个父类A得到继承，叫做“单重继承”。如果一个子类有两个或更多个父类，则称为“多重继承”。

5．多态

在收到消息时，对象要予以响应。不同的对象收到同一消息可以产生完全不同的结果，这一现象叫做多态（polymorphism）。在使用多态的时候，用户可以发送一个通用的消息，而实现的细节则由接收对象自行决定。这样，同一消息就可以调用不同的方法。

多态的实现受到继承的支持，利用类的继承的层次关系，把具有通用功能的消息存放在高层次，而不同的实现这一功能的行为放在较低层次，在这些低层次上生成的对象能够给通用消息以不同的响应。

多态有几种不同的形式，Cardelli和Wegner把它分为4类，如下所示。其中，参数多态和包含多态称为通用的多态，过载多态和强制多态称为特定的多态。

参数多态是应用比较广泛的多态，被称为最纯的多态。包含多态在许多语言中都存在，最常见的例子就是子类型化，即一个类型是另一个类型的子类型。过载（overloading）多态是同一个名字在不同的上下文中所代表的含义。

6．动态绑定（dynamic binding）

绑定是一个把过程调用和响应调用所需要执行的代码加以结合的过程。在一般的程序设计语言中，绑定是在编译时进行的，叫做静态绑定。动态绑定则是在运行时进行的，因此，一个给定的过程调用和代码的结合直到调用发生时才进行。

动态绑定是和类的继承以及多态相联系的。在继承关系中，子类是父类的一个特例，所以父类对象可以出现的地方，子类对象也可以出现。因此在运行过程中，当一个对象发送消息请求服务时，要根据接收对象的具体情况将请求的操作与实现的方法进行连接，即动态绑定。

10.2　面向对象程序设计

面向对象程序设计（Object-Oriented Programming, OOP）的实质是选用一种面向对象程序设计语言（Object-Oriented Programming Language, OOPL），采用对象、类及其相关概念所进行的程序设计。

定义什么是面向对象、什么不是面向对象的程序设计的工作是困难的，这不依赖于程序设计语言（可用C++语言编写纯C语言程序），而依赖于程序设计风格。当然，好的OOPL必须至少支持：

（1）被封装的对象。

（2）类和实例概念。

（3）类间的继承性。

（4）多态。

10.2.1　面向对象的好处

1．面向对象是程序设计新范型

程序设计范型（Programming Paradigm）是人们在程序设计时所采用的基本方式模型。程序设计范型决定了程序设计时采用的思维方式、使用的工具，同时又有一定的应用范畴。

程序设计范型的发展经历了过程程序设计、模块化程序设计、函数程序设计、逻辑程序设计，发展到现在的面向对象程序设计范型。

面向对象程序设计范型是在上述的范型之上发展起来的，它的关键在于加入类和继承性。类和继承性的设置，进一步提高了抽象的程度。

2．面向对象的好处

Ian Graham在《Object-Oriented Methods Principles & Practice》一书中，将面向对象的好处总结如下。

（1）对象技术解决了产品质量和生产率之间的权衡。精心设计的面向对象系统能够作为那些基本上从可重用构件组装而成的系统的基础，从而创造了更高的生产率；重用以前项目中经过测试的那些已经存在的类会使系统具有更高的质量。

（2）面向对象程序设计，特别是继承机制，使得系统具有很高的灵活性和易扩充性。

（3）面向对象是一个能管理复杂性并增强伸缩性的工具。

（4）根据面向对象的观点，以现实世界对应物为基础，把某一领域分割成各种对象进行分析与设计，常常比自顶向下进行功能分解的分析及设计更自然合理。

（5）从概念模型化到分析、设计、编码可以实现无缝传递。

（6）通过封装进行的信息隐蔽有助于建立安全的系统。

10.2.2　面向对象程序设计语言

在程序设计语言中引入面向对象概念实际上不是最近几年才有的，其根源可以追溯到20世纪60年代后期，那时Kristen Nyagaard和Ole-Johan Dahl在挪威计算中心开发出了一种叫做Simual67的语言。这种语言在用于Simula的基础上，第一次引入了类、协同程序（coroutines）和子类的概念，非常类似于今天的面向对象程序设计语言。

20世纪70年代中期，在Xerox公司的Palo Alto研究中心（Xerox PARC）中产生了第一个完整、健全的面向对象程序设计语言——Smalltalk，以后又进行了改进和扩充，形成了Smalltalk-80。这种语言中的每一个元素都分别是一个对象，从语言本身的实现、程序设计环境，到所支持的程序设计风格，都是面向对象的。Simula 67和Smalltalk的开发，是目前面向对象程序设计和面向对象程序设计语言研究中大多数工作的起点。

到今天为止，已经有大约100多种面向对象的和基于对象的程序设计语言。本节以综述的形式介绍几种目前比较流行的或者独特的OOPLs，其中包括Smalltalk、Eiffel、C++和Java。

1．Smalltalk

Smalltalk语言起源于20世纪60年代末期，主要设计者Alan Kay还在犹他大学念研究生时，就认识到可以用Simula语言中的概念来充实他在图形方面的研究工作。Alan Kay到Xerox PARC工作后，将这些早期思想发展为基于图形的交互式个人工作站概念，并萌发了设计一种新语言支持这些概念的想法。为Smalltalk的开发作出重要工作的另外两个人是Adale Goldberg和Dan Ingans。

Smalltalk在Xerox PARC历经了多次重大修改，最终形成了Smalltalk-80，事实上，Smalltalk并不是一种单纯的程序设计语言，而是反映面向对象程序设计思想的程序设计环境。这个系统在系统本身的设计中强调了对象概念的归一性，引入了类、方法、实例等概念和术语，应用了单重继承和动态绑定，成为OOPLs发展过程中的一个引人注目的里程碑。

在Smalltalk-80中，除了对象之外没有其他形式的数据，对一个对象的唯一操作就是向它发消息。在这种语言中，连类也被看成是对象——类是元类的实例。因此，定义一个类就是向元类发一条消息，请求元类执行它的new来生成一个实例，也就是生成这个类，而消息中的参数，就是关于这个类的说明。

Smalltalk-80全面支持面向对象的概念，表10-1给出了它关于这些概念所采用的术语，可以看出二者是完全一致的，表明了这种语言对OOP的深刻影响。

表10-1　Smalltalk-80关于面向对象概念的术语

2．Eiffel

Eiffel语言是继Smalltalk-80之后的另一个“纯”OOPL。这种语言是由OOP领域中著名的专家Bertrand Meyer等人20世纪80年代后期在ISE公司（Interactive Software Engineering Inc.）开发的，它的主要特点是全面的静态类型化、有大量的开发工具、支持多继承。在众多的OOPLs中，Eiffel是较早（1988年）对多重继承提供支持的，它的一些实现策略（如同名冲突处理、异常处理等）已经对后来的OOPLs的设计和实现产生了影响。

Eiffel也全面支持面向对象的概念，表10-2给出了它关于这些概念所采用的术语。

表10-2　Eiffel关于面向对象概念的术语

3．C++

C++语言是一种面向对象的强类型化语言，由AT&T的Bell实验室于1980年推出，目前已被国外许多主要的计算机和软件生产企业选为替代C语言或与C语言并存的基本开发工具。

C++语言是C语言的一个向上兼容的扩充，而不是一种新语言。C++是一种支持多范型的程序设计语言，它既支持面向对象的程序设计，也支持面向过程的程序设计。它融合了Simula67的几种面向对象机制——类、继承、虚拟函数，借鉴了ALGOL68中的操作符过载、变量声明位置不受限制等特性，形成了一种“比Smalltalk更接近于机器、比C语言更接近于问题”的OOPL。

C++支持基本的面向对象概念：对象、类、方法、消息、子类和继承性。它同时支持静态类型和动态类型。表10-3给出了C++中关于这些概念所采用的术语。

表10-3　C++关于面向对象概念的术语

C++完全支持多继承，并且通过使用try/throw/catch模式提供了一个完整的异常处理机制。

C++不提供自动的无用存储单元收集。这必须通过程序员来实现，或者通过编程环境提供合适的代码库来予以支持。

4．Java

Java语言起源于Oak语言，Oak语言被设计成能运行在设备（如微波炉、摄影机等）的嵌入芯片上。

Java编译成伪代码（P码或者字节代码），这需要一个虚拟机来对其进行解释。Java的虚拟机在几乎每一种平台上都可以运行。这实质上使得开发是与机器无关的，并且提供了通用的可移植性。

Java把类的概念和接口（Interface）的概念区分开来，并试图通过只允许接口的多继承来克服多继承的危险。Java支持基本的面向对象概念，表10-4给出了Java中关于这些概念所采用的术语。

Java的异常处理机制与C++的try/throw/catch相类似，但更加严密。在Java中，通过声明轻型线程来处理并发性，这些线程通过副作用和同步协议进行通信。

Java Beans是组件，即类和其所需资源的集合，它们主要被设计用来提供定制的GUI小配件。

Java有自己的对象请求代理技术——RMI（远程方法调用），这使得应用能够跨越网络来调用执行在其他Java应用程序内的方法。从第2版起，Java也包含了一个兼容CORBA的、语言独立的对象请求代理。Java也包含了用于管理与关系数据库进行交互的类JDBC和用于基本图形的类。

表10-4　Java关于面向对象概念的术语

10.2.3　程序设计语言中的OOP机制

作为一种程序设计范型，OOP主要解决程序设计方面的问题，因而与程序设计语言的发展密切相关。特定的OOP概念一般是通过OOPLs中特定的语言机制来体现的。本节用C++语言从程序设计的角度进一步讨论这些概念。

OOP现在已经扩展到系统分析和软件设计的范畴，出现了面向对象分析和面向对象设计的概念，这两部分内容见本章的10.3节和10.4节。

1．类

通常，在介绍OOP的书籍或文章中总是先引入对象的概念，然后从对对象进行抽象的角度来引入类的概念。但是，当设计和实现一个面向对象的程序时，首先接触到的不是对象，而是类和类层次结构。

【例10.1】雇员类的定义。

类具有实例化功能，包括实例生成（由类的Constructor完成）和实例消除（由类的Destructor完成）。类的实例化功能决定了类及其实例具有下面的特征。

（1）同一个类的不同实例具有相同的数据结构，承受的是同一方法集合所定义的操作，因而具有规律相同的行为。

（2）同一个类的不同实例可以持有不同的值，因而可以具有不同的状态。

（3）实例的初始状态（初值）可以在实例化时确定。

2．继承和类层次结构

孤立的类只能描述实体集合的特征同一性，而客观世界中实体集合的划分通常还要考虑实体特征方面有关联的相似性。在OOP中使用继承机制解决这一问题。

在OOPLs中，继承一般通过定义类之间的关系来体现。

在一个面向对象系统中，子类与父类之间的继承关系构成了这个系统的类层次结构，可以用树（对应于单继承）或格（对应于多继承）这样的图来描述。

【例10.2】经理类的定义。

当执行一个子类的实例生成方法时，首先在类层次结构中从该子类沿继承路径上溯至它的一个基类，然后自顶向下地执行该子类所有父类的实例生成方法；最后执行该子类实例生成方法的函数体。当执行一个子类的实例消除方法时，顺序恰好与执行该子类实例生成方法相反：先执行该子类实例消除方法的函数体，再沿继承路径自底向上地执行该子类所有父类的实例消除方法。

与一般数据类型的实例化过程相比，类的实例化过程是一种实例的合成过程，而不仅仅是根据单个类型进行的空间分配、初始化和联编。

OOPLs中的继承机制体现了一条重要的面向对象程序设计原则：开发人员在构造程序时不必从零开始，而只需对差别进行程序设计。支持继承也是OOPLs与传统程序设计语言在语言机制方面最根本的区别。

3．对象、消息传递和方法

对象是类的实例。尽管对象的表示在形式上与一般数据类型十分相似，但是它们之间存在一种本质区别：对象之间通过消息传递方式进行通信。

消息传递源是一种与通信有关的概念，OOP使得对象具有交互能力的主要模型就是消息传递模型。对象被看成用传递消息的方式互相联系的通信实体，它们既可以接收，也可以拒绝外界发来的消息。一般情况下，对象接收它能够识别的消息，拒绝它不能识别的消息。对于一个对象而言，任何外部的代码都不能以任何不可预知或事先不允许的方式与这个对象进行交互。

发送一条消息至少应给出一个对象的名字和要发给这个对象的那条消息的名字。通常，消息的名字就是这个对象中外界可知的某个方法的名字。在消息中，经常还有一组参数（也就是那个方法所要求的参数），将外界的有关信息传给这个对象。

对于一个类来说，它关于方法界面的定义规定了实例的消息传递协议，而它本身则决定了消息传递的合法范围。由于类是先于对象构造而成的，所以一个类为它实例提供了可以预知的交互方式。例如，假设ml是类Manager的一个实例（或对象），当外界要求把这个对象所代表的那位经理的级别改变为2时，就应以下面的方式向这个对象发出一条消息：

4．对象自身引用

对象自身引用（self-reference）是OOPLs中的一种特有结构。这种结构在不同的OOPLs中有不同的名称，在C++和Java中称为this，在Smalltalk-80、Object-C和其他一些OOPLs中则称为self。

以C++语言为例，对于类c和方法c∷m，在c∷m方法体中出现的c的成员名n将被编译程序按this->n来对待。这里的this是一个类型为c*的指针（在Java语言中this是一个引用），它的值由语言中的消息传递机制提供。

对象自身引用的值和类型分别扮演了两种意义的角色：对象自身引用的值使得方法体中引用的成员名与特定的对象相关，对象自身引用的类型则决定了方法体被实际共享的范围。

对象自身引用机制使得在进行方法的设计和实现时并不需要考虑与对象联系的细节，而是从更高一级的抽象层次，也就是类的角度来设计同类型对象的行为特征，从而使得方法在一个类及其子类的范围内具有共性。在程序运行过程中，消息传递机制和对象自身引用将方法与特定的对象动态地联系在一起，使得不同的对象在执行同样的方法体时，可以因对象的状态不同而产生不同的行为，从而使得方法对具体的对象具有个性。

5．重置

重置（overriding）的基本思想是：通过一种动态绑定机制的支持，使得子类在继承父类界面定义的前提下，用适合于自己要求的实现去置换父类中的相应实现。

在OOPLs中，重置机制有相应的语法供开发人员选择使用。在C++语言中，通过虚拟函数（virtual function）的定义来进行重置的声明，通过虚拟函数跳转表（virtual functions jump tables, vtbl）结构来实现重置方法体的动态绑定。在Java语言中，通过抽象方法（abstract method）来进行重置的声明，通过方法查找（method lookup）实现重置方法体的动态绑定。

下面以C++语言为例，说明重置机制的使用。

【例10.3】使用重置修改类Employee和Manager。

6．类属类

类属是程序设计语言中普遍注重的一种参数多态机制，在OOPLs中也不例外。本节主要介绍与类有关的类属。

在C++语言中，类属有专门的术语——template。

【例10.4】类属类。

其中，＜class T＞中的class用来说明T是一个类型变元，但并不意味着T一定是一个类，也可以是基本类型。

类属类可以看成是类的模板。一个类属类是关于一组类的一个特性抽象，它强调的是这些类的成员特征中与具体类型无关的那些部分，而与具体类型相关的那些部分则用变元来表示。这就使得对类的集合也可以按照特性的相似性再次进行划分。类属类的一个重要作用，就是对类库的建设提供了强有力的支持。

重置和类属都是一种多态机制，多态的基本概念以及多态的分类见本章10.1节，对于其他利类的多态不在本节进行介绍。

7．无实例的类

在前几节中曾指出，类是对象的模板，对象是类的实例。那么是否每个类都至少有一个实例？如果在类之间没有定义继承关系，回答是肯定的。这是因为若存在没有实例的类，那么这样的类对程序的行为没有任何贡献，因而是冗余的。相反，如果存在继承关系，那么的确有可能在类层次结构的较高层次上看到始终没有实例的类。

要创建无实例的类，仍然需要语言的支持。在C++和Java语言中，抽象类就是这样的类。在C++中通过在类中定义纯虚拟函数来创建一个抽象类，在Java中通过在类中定义抽象方法来创建一个抽象类，或者直接将一个类声明为抽象类。

【例10.5】用C++语言创建抽象类。

【例10.6】用Java语言创建抽象类。

或者将整个类命名为抽象类，而不管这个类中是否包含抽象方法。

10.2.4　面向对象的程序

1．类库

类库是一种预先定义的程序库，可以由开发人员自己扩充。

类库以程序模块的形式，按照类层次结构把一组类的定义和实现组织在一起。通常，这一组类预先提供的是一些低层功能，如输入输出例程、图形操作原语和基本数据结构等，这些功能覆盖了传统例程库所提供的功能。

开发人员可以直接使用类库中的类，其方式与使用语言中的基本类型完全相同。开发人员可以扩充类库，办法是定义和实现类库中已有类的子类，再将这样的子类加入类库。

要想发挥面向对象的软件构造方式的优点，开发人员必须知道类库是怎样组织起来的。衡量一个开发人员的好坏，要看他是否知道如何来最好地发挥已有类库的优点，看他有没有能力将已有的类库与新的问题紧密地匹配起来，还要看他不得不另外编写的代码是不是最少。

大多数程序设计语言都有类库。最早引入类库的语言是Smalltalk，Smalltalk类库中基本的类层次结构片断如图10-2所示（为了清晰起见，图中略去了表示继承方向的箭头）。

图10-2　Smalltalk类库中基本的类层次结构

2．面向对象程序设计实例

【例10.7】C++语言本身不提供对数组下标越界的判断。为了解决这一问题，本例的程序中定义了相应的模板类，使得对于任意类型的二维数组，可以在访问数组元素的同时，对行下标和列下标进行越界判断，并给出相应的提示信息。

程序如下：

10.3　面向对象开发技术

众所周知，采用系统化的开发方法开发软件（特别是大规模软件）系统才可获得好的系统。什么是“好”系统呢？这个问题应从使用者（外部）和开发者（内部）两个角度来回答。从使用者观点出发，需要系统具有易学易用、界面友好、正确使用时能快速给出正确结果、效率高等优点，还要求系统安全可靠等；从系统开发者和管理者角度出发，要求系统易于修改和扩充、易于理解、易于测试和重用、易于与其他系统兼容和管理等。虽然不同的系统所强调的特性可能不同，但上述所要求的系统特性是基本特性。

相对而言，面向对象的开发技术更有利于开发具有上述特性的软件系统。本章简要介绍面向对象开发技术的基本概念和相关步骤。

10.3.1　面向对象分析

同其他分析方法一样，面向对象分析（Object-Oriented Analysis, OOA）的目的是为了获得对应用问题的理解。理解的目的是确定系统的功能、性能要求。面向对象分析法与功能／数据分析法之间的差别是前期的表述含义不同。功能／数据分析法分开考虑系统的功能要求和数据及其结构，面向对象分析方法是将数据和功能结合在一起作为一个综合对象来考虑。面向对象分析技术可以将系统的行为和信息间的关系表示为迭代构造特征。

面向对象分析包含5个活动：认定对象、组织对象、描述对象间的相互作用、定义对象的操作、定义对象的内部信息。

1．认定对象

在应用领域中，按自然存在的实体确立对象。在定义域中，首先将自然存在的“名词”作为一个对象，这通常是研究问题、定义域实体的良好开始。通过实体间的关系寻找对象常常没有问题，而困难在于寻找（选择）系统关心的实质性对象，实质性对象是系统稳定性的基础。例如在银行应用系统中，实质性对象应包含客户账务、清算等，而门卫值班表不是实质性对象，甚至可不包含在该系统中。

2．组织对象

分析对象间的关系，将相关对象抽象成类，其目的是为了简化关联对象，利用类的继承性建立具有继承性层次的类结构。抽象类时可从对象间的操作或一个对象是另一个对象的一部分来考虑，如房子由门和窗构成，门和窗是房子类的子类。由对象抽象类，通过相关类的继承构造类层次，所以说系统的行为和信息间的分析过程是一种迭代表征过程。

3．对象间的相互作用

描述出各对象在应用系统中的关系，如一个对象是另一个对象的一部分，一个对象与其他对象间的通信关系等。这样可以完整地描述每个对象的环境，由一个对象解释另一个对象，以及一个对象如何生成另一个对象，最后得到对象的界面描述。

4．基于对象的操作

当考虑对象的界面时，自然要考虑对象的操作。其操作有从对象直接标识的简单操作，如创建、增加和删除等；也有更复杂的操作，如将几个对象的信息连接起来。一般而言，避免对象太复杂比较好，当连接的对象太复杂时，可将其标识为新对象。当确定了对象的操作后，再定义对象的内部，对象内部定义包括其内部数据信息、信息存储方法、继承关系以及可能生成的实例数等属性。

分析阶段最重要的是理解问题域的概念，其结果将影响整个工作。经验表明，从应用定义域概念标识对象是非常合理的，完成上述工作后写出规范文档，文档确定每个对象的范围。

早期面向对象的目标之一是简化模型与问题域之间的语义差距。事实上，面向对象分析的基础是软件系统结构，这依赖于人类看待现实世界的方法。当人们理解求解问题的环境时，常采用对象、分类法和层次性这类术语。面向对象分析与功能／数据分析方法相比，面向对象的结果比较容易理解和管理。面向对象分析方法的另一个优点是便于修改，早期阶段的修改容易提高软件的可靠性。

10.3.2　面向对象设计

面向对象设计（Object-Oriented Design, OOD）的含义是设计分析模型和实现相应源代码，在目标代码环境中这种源代码可被执行。通常情况是，由概念模型生成的分析模型被装入到相应的执行环境中时，还需要修改。

同所有其他设计活动一样，要使系统的结构好且效率高，做好相互间的平衡是困难的。分析模型已经提供了概念结构，它将试图长期保存。另外，设计期必须充分考虑系统的稳定性，如目标环境所需的最大存储空间、可靠性和响应时间等，所有这些都会影响系统的结构。

对象标识期间的目标是分析对象，设计过程也是发现对象的过程，称之为再处理。因此，必须有从分析模型到设计模型到程序设计语言的线性转换规则。对象可以用预先开发的源代码实现，称这样的部分为构件（component）。由于这些构件是功能和数据的组装，因此，常常用于简化面向对象环境的产生。

如前所述，面向对象系统的开发需要面向对象的程序设计风格，这与OOPL无直接关系。面向对象是一种程序设计风格，而不只是一种具有构造继承性、封装性和多态的程序设计语言族的命名。

10.3.3　面向对象测试

就测试而言，用面向对象方法开发的系统测试与其他方法开发的系统测试没有什么不同，在所有开发系统中都是根据规范说明来验证系统设计的正确性。程序验证应尽可能早地开始。程序调试步骤是从最底层开始，从单元测试、综合测试到系统测试。单元测试是系统构件的分体测试。将测试好的系统构件接起来看它们之间相互作用的正确性称为综合测试。最后是整个系统的测试，包括软件系统所在相关环境的测试。通常综合测试是一种“主攻”活动，在系统开发期是非常关键的。这一阶段应随时连结已开发的每一部分，再看它们的实际工作，这种“主攻”活动在面向对象系统中是一种实质性的、渐渐增长的测试策略。

面向对象的系统由一些对象和它们相互间的通信组成，这些对象包括了它们的数据属性和操作（动作），比传统系统开发方法中独立工作的子程序大。对象的操作局限于特定数据，一个对象的所有操作自然由同一个设计者开发。这导致单元测试比传统系统的单元大，综合测试尽可能在早期阶段处理，因为通信是系统开发的实质。所有对象有预定义的界面，这也有利于综合测试。当综合测试继续到较高层次时，那么越来越多的对象就会被逐步连接起来。面向对象的综合测试是由底向上的测试。

一般来说，对面向对象软件的测试可分为下列4个层次进行。

（1）算法层。测试类中定义的每个方法，基本上相当于传统软件测试中的单元测试。

（2）类层。测试封装在同一个类中的所有方法与属性之间的相互作用。在面向对象软件中类是基本模块，因此可以认为这是面向对象测试中所特有的模块测试。

（3）模板层。测试一组协同工作的类之间的相互作用。大体上相当于传统软件测试中的集成测试，但是也有面向对象软件的特点（例如，对象之间通过发送消息相互作用）。

（4）系统层。把各个子系统组装成完整的面向对象软件系统，在组装过程中同时进行测试。

软件工程中传统的测试用例设计技术，如逻辑覆盖、等价类划分和边界值分析等方法，仍然可以作为测试类中每个方法的主要技术。面向对象测试的主要目标也是用尽可能低的测试成本和尽可能少的测试用例，发现尽可能多的错误。但是，面向对象程序中特有的封装、继承和多态等机制，也给面向对象测试带来一些新特点，增加了测试和调试的难度。

10.4　面向对象分析与设计方法

在实现一个系统之前应当十分重视对系统需求的理解和交流，这一点已经是公认的准则。结构化分析方法产生于20世纪60年代，基本思想是对一个系统的功能构件进行分解，将一种更易于管理、更加确定的方法引入系统分析阶段。面向对象分析强调的则是对一个系统中对象的特征和行为的定义。

目前，国际上已经出现了多种面向对象的方法，例如Peter Coad和Edward Yourdon的OOA和OOD方法、Booch的OOD方法、OMT（Object Modeling Technique，面向对象建模技术）方法及UML（Unified Modeling Language，统一建模语言）。本节将对这4种方法进行简要介绍。

10.4.1　Peter Coad和Edward Yourdon的OOA和OOD方法

1．OOA

OOA模型由下列5个层次和5个活动组成。

5个层次：主题层、对象类层、结构层、属性层和服务层。

5个活动：标识对象类、标识结构、定义主题、定义属性和定义服务。

在这种方法中定义了两种对象类之间的结构，一种称为分类结构，一种称为组装结构。分类结构就是一般与特殊关系，是一种is a关系。组装结构则反映了对象之间的整体与部分关系，例如“计算机”对象由“显示器”、“主机箱”、“键盘”和“鼠标”等对象组成，而“主机箱”对象又可由“主板”、CPU、“内存”和“风扇”等对象组成。它实际上是一种has a的关系。

主题提供了控制读者在一段时间内考虑和理解模型范围围的一种机制，在OOA中将与某一主题有关的对象用主题框围起来，其目的是减少读者理解系统的复杂程度。

OOA在定义属性的同时，还要识别实例连接。实例连接是一个实例对象与另一个实例对象的映射关系（或者说是一种简单的对应关系）。例如，一个公司有多个职员，一个职员只能在一家公司工作，那么“公司”类的实例与“职员”类的实例间就有1对多的实例连接关系。OOA在定义服务的同时要识别消息连接。当一个对象需要向另一个对象发送消息时，它们之间就存在消息连接。

需要强调的是，这5个活动不是必须顺序进行的，有些分析员喜欢先识别对象类，然后定义属性、服务，再识别结构和主题；有的则喜欢先识别对象类、结构和主题，再定义属性和服务。总之5个活动都完成了，OOA的模型就建立了。5个层次并不是构成软件系统的层次，而是一旦建立了模型，就可以在5个层次上进行表示和复审。

2．OOD

OOA中的5个层次和5个活动继续贯穿在OOD过程中。OOD模型由4个部分和4个活动组成，如图10-3所示。

图10-3　Coad & Yourdon的OOD模型

4个活动是设计问题域部件、设计人机交互部件、设计任务管理部件及设计数据管理部件。

（1）设计问题域部件。OOA的结果恰好就是OOD的问题域部件，分析的结果在OOD中可以被改动或增补，但基于问题域的总体组织框架是长时间稳定的。

（2）设计人机交互部件。人机交互部件在上述结果中加入人机交互的设计和交互的细节，包括窗口和输出报告的设计。可以用原型来帮助实际交互机制的开发和选择。

（3）设计任务管理部件。这部分主要是识别事件驱动任务，识别时钟驱动任务，识别优先任务和关键任务，识别协调者，审查每个任务并定义每个任务。

（4）设计数据管理部件。数据管理部件提供了在数据管理系统中存储和检索对象的基本结构，其目的是隔离数据管理方案（如普通文件、关系数据库和面向对象数据库等）对其他部分的影响。

10.4.2　Booch的OOD方法

Booch认为软件开发是一个螺旋上升的过程，在螺旋上升的每个周期中有以下步骤。

（1）标识类和对象。

（2）确定它们的含义。

（3）标识它们之间的关系。

（4）说明每一个类的界面和实现。

Booch的OOD模型如图10-4所示。

图10-4　Booch的OOD模型

除了类图、对象图、模块图和进程图外，Booch的OOD中还使用了两种动态描述图，一种是刻画特定类实例的状态转换图，另一种是描述对象间事件变化的时序图。

10.4.3　OMT方法

对象建模技术（Object Modeling Technique, OMT）是由J.Rumbaugh等人提出的。OMT定义了三种模型，它们是对象模型、动态模型和功能模型，OMT用这三种模型来描述系统。OMT方法有4个步骤：分析、系统设计、对象设计和实现。OMT方法的每一步都使用这三种模型，通过每一步对三种模型不断地精化和扩充。

1．对象模型、动态模型和功能模型

1）对象模型

对象模型描述系统中对象的静态结构、对象之间的关系、对象的属性、对象的操作。对象模型表示静态的、结构上的、系统的“数据”特征。对象模型为动态模型和功能模型提供了基本的框架。对象模型用包含对象和类的对象图来表示。

OMT的对象模型中除了对象、类、继承外，还有一些其他的概念，下面介绍几个主要的概念。

（1）链（link）和关联（association）。链表示实例对象间的物理或概念上的连接。例如在表10-5中，Joe Doe为Simplex公司工作，工资2000元。关联描述具有公共结构和公共语义的一组链，例如关联works-for描述了一组某人为某公司工作的链。实际上链是关联的一个实例。链可以有属性，称为链属性，链属性表示关联中链的性质。图10-5给出了链、关联和链属性的一个实例。图中的实心圆是关联的阶（也称为重数），阶指出一个类的多少个实例可以与所关联的类的一个实例相关。实心圆表示0或多个，空心圆表示0或1个，没有圆表示1个。

表10-5　人员登记表

图10-5　链、关联和链属性

（2）泛化（Generalization）。泛化是一个类与它的一个或多个细化种类之间的关系，即一般与特殊的关系。被细化的类称为父类，每个细化的种类称为子类，子类可以继承父类的性质。

（3）聚集（Aggregation）。聚集是一种整体与部分的关系，在这种关系中表示整体的对象与表示部分的对象关联。图10-6给出了泛化和聚集的实例。

图10-6　泛化和聚集

（4）模块（module）。模块是组合类、关联和泛化的一种逻辑结构。模块给出了某个主题的视图。

2）动态模型

动态模型描述与时间和操作顺序有关的系统特征——激发事件、事件序列、确定事件先后关系以及事件和状态的组织。动态模型表示瞬时的、行为上的、系统的“控制”特征。动态模型用状态图来表示。每张状态图显示了系统中一个类的所有对象所允许的状态和事件的顺序。

3）功能模型

功能模型描述与值的变换有关的系统特征——功能、映射、约束和函数依赖。功能模型用数据流图来表示。

对象模型、动态模型和功能模型之间具有下述关系。

（1）与功能模型的关系：对象模型展示了功能模型中的动作者、数据存储和流的结构，动态模型展示了执行加工的顺序。

（2）与对象模型的关系：功能模型展示了类上的操作和每个操作的变量，因此它也表示了类之间的“供应者—客户”关系；动态模型展示了每个对象的状态以及它接收事件和改变状态时所执行的操作。

（3）与动态模型的关系：功能模型展示了动态模型中未定义的不可分解的动作和活动的定义，对象模型展示了是谁改变了状态和承受了操作。

2．OMT的步骤

（1）分析。分析是OMT方法的第一步，其目的是建立可理解的现实世界模型。分析从问题陈述入手，通过与客户的不断交互以及对现实世界背景知识的了解，对能反映系统的三个本质特征（对象类及它们之间的关系、动态的控制流和受约束的数据的函数变换）进行分析，并构造出现实世界的模型。分析模型必须简洁、明确地抽象目标系统必须做的事情，而不是如何做这些事。模型中的对象应该是应用领域中的概念，而不是诸如数据结构之类的计算机实现中的概念。一个好的模型应该被应用领域专家而不是被程序员所理解和评价。分析模型不应包含任何与实现有关的考虑。

（2）系统设计。系统设计阶段确定整个系统的体系结构，形成求解问题和建立解答的高层次策略。系统设计人员必须形成的决策有：将系统分解成子系统，标识问题中固有的并发性，将子系统分配到处理器和任务，选择数据存储管理的手段，处理对全局资源的访问，选择软件中控制的实现，处理边界条件，设置折衷的优先级等。

（3）对象设计。在分析的基础上，对象设计阶段建立基于分析模型的设计模型，并考虑实现的细节。设计人员根据系统设计期间建立的策略把实现细节加入到设计模型中。对象设计的焦点是实现每个类的数据结构及所需的算法。分析模型中的对象类仍然是有意义的，但它们中都加入了数据结构和算法。

对象设计期间，设计者必须履行下列步骤：综合考虑三个模型以获得类的操作；设计实现操作的算法，优化数据访问路径，实现与外部交互的控制；调整类结构以增加继承，设计关联，确定对象表示，将类和关联包装到模块中。

（4）实现。实现阶段将对象设计阶段开发的对象类及其关系转换成特定的程序设计语言、数据库或硬件的实现。程序设计是开发周期中相对较小且机械的部分，因为在对象设计中已形成了所有重要的决策。目标语言在某种程度上能影响设计决策，但设计不应依赖于程序设计语言的细节。在实现期间，重要的是应该有一些好的软件工程准则作指导。

10.4.4　UML概述

统一建模语言是面向对象软件的标准化建模语言。由于其简单、统一，又能够表达软件设计中的动态和静态信息，目前已经成为可视化建模语言事实上的工业标准。

从企业信息系统到基于Web的分布式应用，甚至严格的实时嵌入式系统都适合用UML来建模。它是一种富有表达力的语言，可以描述开发所需要的各种视图，然后以此为基础装配系统。UML由三个要素构成：UML的基本构造块、支配这些构造块如何放置在一起的规则和运用与整个语言的一些公共机制。限于篇幅，下面仅对UML中的基本构造块进行讨论。

UML的词汇表包含三种构造块：事物、关系和图。事物是对模型中最具有代表性的成分的抽象；关系把事物结合在一起；图聚集了相关的事物。

1．事物

UML中有4种事物：结构事物、行为事物、分组事物和注释事物。

（1）结构事物（structural thing）。结构事物是UML模型中的名词。它们通常是模型的静态部分，描述概念或物理元素。结构事物包括类（class）、接口（interface）、协作（collaboration）、用例（use case）、主动类（active class）、构件（component）和节点（node）。

各种结构事物的图形化表示如图10-7所示。

图10-7　结构事物的图形表示

（2）行为事物（behavior thing）。行为事物是UML模型的动态部分。它们是模型中的动词，描述了跨越时间和空间的行为。共有两类主要的行为事物：交互（interaction）和状态机（state machine）。

交互由在特定语境中共同完成一定任务的一组对象之间交换的消息组成。一个对象群体的行为或单个操作的行为可以用一个交互来描述。交互涉及一些其他元素，包括消息、动作序列（由一个消息所引起的行为）和链（对象间的连接）。在图形上，把一个消息表示为一条有向直线，通常在表示消息的线段上总有操作名。如图10-8所示。

状态机描述了一个对象或一个交互在生命期内响应事件所经历的状态序列。单个类或一组类之间协作的行为可以用状态机来描述。一个状态机涉及到一些其他元素，包括状态、转换（从一个状态到另一个状态的流）、事件（触发转换的事物）和活动（对一个转换的响应）。在图形上，把状态表示为一个圆角矩形，通常在圆角矩形中含有状态的名称及其子状态，如图10-9所示。

图10-8　消息

图10-9　状态

（3）分组事物（grouping thing）。分组事物是UML模型的组织部分。它们是一些由模型分解成的“盒子”。在所有的分组事物中，最主要的分组事物是包（package）。包是把元素组织成组的机制，这种机制具有多种用途。结构事物、行为事物甚至其他分组事物都可以放进包内。包不像构件（仅在运行时存在），它纯粹是概念上的（即它仅在开发时存在）。包的图形化表示如图10-10所示。

（4）注释事物（annotational thing）。注释事物是UML模型的解释部分。这些注释事物用来描述、说明和标注模型的任何元素。注解（note）是一种主要的注释事物。注解是一个依附于一个元素或者一组元素之上，对它进行约束或解释的简单符号。注解的图形化表示如图10-11所示。

图10-10　包

图10-11　注解

2．关系

UML中有4种关系：依赖、关联、泛化和实现。

（1）依赖（dependency）。依赖是两个事物间的语义关系，其中一个事物（独立事物）发生变化会影响另一个事物（依赖事物）的语义。在图形上，把一个依赖画成一条可能有方向的虚线，如图10-12所示。

（2）关联（association）。关联是一种结构关系，它描述了一组链，链是对象之间的连接。聚集（aggregation）是一种特殊类型的关联，它描述了整体和部分间的结构关系。关联和聚集的图形化表示如图10-13和图10-14所示。

图10-12　依赖

图10-13　关联

图10-14　聚集

在关联上可以标注重复度（multiplicity）和角色（role）。

（3）泛化（generalization）。泛化是一种特殊／一般关系，特殊元素（子元素）的对象可替代一般元素（父元素）的对象。用这种方法，子元素共享了父元素的结构和行为。在图形上，把一个泛化关系画成一条带有空心箭头的实线，它指向父元素，如图10-15所示。

（4）实现（realization）。实现是类元之间的语义关系，其中一个类元指定了由另一个类元保证执行的契约。在两种地方要遇到实现关系：一种是在接口和实现它们的类或构件之间；另一种是在用例和实现它们的协作之间。在图形上，把一个实现关系画成一条带有空心箭头的虚线，如图10-16所示。

图10-15　泛化

图10-16　实现

这4种关系是UML模型中可以包含的基本关系事物。它们也有变体，例如，依赖的变体有精化、跟踪、包含和延伸。

3．UML中的图

图（diagram）是一组元素的图形表示，大多数情况下把图画成顶点（代表事物）和弧（代表关系）的连通图。为了对系统进行可视化，可以从不同的角度画图，这样图是对系统的投影。

UML2.0提供了13种图，分别是类图、对象图、用例图、序列图、通信图、状态图、活动图、构件图、部署图、组合结构图、包图、交互概览图和时序图。

1）类图

类图（class diagram）展现了一组对象、接口、协作和它们之间的关系。在面向对象系统的建模中所建立的最常见的图就是类图。类图给出系统的静态设计视图。包含主动类的类图给出了系统的静态进程视图。

类图中通常包括下述内容（如图10-17所示）。

图10-17　UML类图

（1）类。

（2）接口。

（3）协作。

（4）依赖、泛化和关联关系。

类图中也可以包含注解和约束。类图还可以含有包或子系统，二者都用于把模型元素聚集成更大的组块。

类图用于对系统的静态设计视图建模。这种视图主要支持系统的功能需求，即系统要提供给最终用户的服务。当对系统的静态设计视图建模时，通常以下述三种方式之一使用类图。

（1）对系统的词汇建模。对系统的词汇建模涉及到做出这样的决定：哪些抽象是考虑中的系统的一部分，哪些抽象处于系统边界之外。用类图详细描述这些抽象和它们的职责。

（2）对简单的协作建模。协作是一些共同工作的类、接口和其他元素的群体，该群体提供的一些合作行为强于所有这些元素的行为之和。例如，当对分布式系统的事务语义建模时，不能仅仅盯着一个单独的类来推断要发生什么，而要有相互协作的一组类来实现这些语义。用类图对这组类以及它们之间的关系进行可视化和详述。

（3）对逻辑数据库模式建模。将模式看作为数据库的概念设计的蓝图。在很多领域中，要在关系数据库或面向对象数据库中存储永久信息，可以用类图对这些数据库的模式建模。

2）对象图

对象图（object diagram）展现了一组对象以及它们之间的关系。对象图描述了在类图中所建立的事物的实例的静态快照。对象图一般包括对象和链。

和类图一样，对象图给出系统的静态设计视图或静态进程视图，但它们是从真实的或原型案例的角度建立的。这种视图主要支持系统的功能需求，即系统应该提供给最终用户的服务。利用对象图可以对静态数据结构建模。

当对系统的静态设计视图或静态进程视图建模时，主要是使用对象图对对象结构进行建模。对对象结构建模涉及到在给定时刻抓取系统中对象的快照。对象图表示了交互图表示的动态场景的一个静态画面。可以使用对象图可视化、详述、构造和文档化系统中存在的实例以及它们之间的相互关系。

3）用例图

用例图（use case diagram）展现了一组用例、参与者（Actor）以及它们之间的关系。

用例图通常包括如下内容（如图10-18所示）。

图10-18　UML用例图

（1）用例。

（2）参与者。

（3）扩展关系、包含关系。

用例图用于对系统的静态用例视图进行建模。这个视图主要支持系统的行为，即该系统在它的周边环境的语境中所提供的外部可见服务。

当对系统的静态用例视图建模时，可以用下列两种方式来使用用例图。

（1）对系统的语境建模。对一个系统的语境进行建模，包括围绕整个系统画一条线，并声明有哪些参与者位于系统之外并与系统进行交互。在这里，用例图说明了参与者以及他们所扮演的角色的含义。

（2）对系统的需求建模。对一个系统的需求进行建模，包括说明这个系统应该做什么（从系统外部的一个视点出发），而不考虑系统应该怎样做。在这里，用例图说明了系统想要的行为。通过这种方式，用例图使我们能够把整个系统看作一个黑盒子。可以观察到系统外部有什么，系统怎样与哪些外部事物相互作用，但却看不到系统内部是如何工作的。

4）交互图

序列图、通信图、交互概览图和时序图均被称为交互图，它们用于对系统的动态方面进行建模。一张交互图显示的是一个交互，由一组对象和它们之间的关系组成，包含它们之间可能传递的消息。顺序图是强调消息时间顺序的交互图；通信图则是强调接收和发送消息的对象的结构组织的交互图。

交互图用于对一个系统的动态方面建模。在多数情况下，它包括对类、接口、构件和节点的具体的或原型化的实例以及它们之间传递的消息进行建模，所有这些都位于一个表达行为的脚本的语境中。交互图可以单独使用，来可视化、详述、构造和文档化一个特定的对象群体的动态方面，也可以用来对一个用例的特定的控制流进行建模。

交互图一般包含对象、链和消息。

（1）序列图。序列图（sequence diagram）是场景（scenario）的图形化表示，描述了以时间顺序组织的对象之间的交互活动。如图10-19所示，形成序列图时，首先把参加交互的对象放在图的上方，沿x轴方向排列。通常把发起交互的对象放在左边，下级对象依次放在右边。然后，把这些对象发送和接收的消息沿Y轴方向按时间顺序从上到下放置。这样，就提供了控制流随时间推移的清晰的可视化轨迹。

序列图有两个不同于通信图的特征。

①　序列图有对象生命线。对象生命线是一条垂直的虚线，表示一个对象在一段时间内存在。在交互图中出现的大多数对象存在于整个交互过程中，所以这些对象全都排列在图的顶部，其生命线从图的顶部画到图的底部。但对象也可以在交互过程中创建，它们的生命线从接收到构造型为create的消息时开始。对象也可以在交互过程中撤销，它们的生命线在接收到构造型为destroy的消息时结束（并且给出一个大X的标记表明生命的结束）。

图10-19　UML序列图

②　序列图有控制焦点。控制焦点是一个瘦高的矩形，表示一个对象执行一个动作所经历的时间段，既可以是直接执行，也可以是通过下级过程执行。矩形的顶部表示动作的开始，底部表示动作的结束（可以由一个返回消息来标记）。还可以通过将另一个控制焦点放在它的父控制焦点的右边来显示（由循环、自身操作调用或从另一个对象的回调所引起的）控制焦点的嵌套（其嵌套深度可以任意）。如果想特别精确地表示控制焦点在哪里，也可以在对象的方法被实际执行（并且控制还没传给另一个对象）期间，将那段矩形区域阴影化。

（2）通信图。通信图（communication diagram）强调收发消息的对象的结构组织，在早期的版本中也被称作协作图。通信图强调参加交互的对象的组织。产生一张通信图，首先要将参加交互的对象作为图的顶点，然后把连接这些对象的链表示为图的弧，最后用对象发送和接收的消息来修饰这些链。这就提供了在协作对象的结构组织的语境中观察控制流的一个清晰的可视化轨迹。

通信图有两个不同于序列图的特性。

①　通信图有路径。为了指出一个对象如何与另一个对象链接，可以在链的末端附上一个路径构造型（如构造型《local》，表示指定对象对发送者而言是局部的）。通常只需要显式地表示以下几种链的路径：local（局部）、parameter（参数）、global（全局）以及self（自身），但不必表示association（关联）。

②　通信图有顺序号。为表示一个消息的时间顺序，可以给消息加一个数字前缀（从1号消息开始），在控制流中，每个新消息的顺序号单调增加（如2，3等）。为了显示嵌套，可使用带小数点的号码（1表示第一个消息；1.1表示嵌套在消息1中的第一个消息，1.2表示嵌套在消息1中的第二个消息，等等）。嵌套可为任意深度。还要注意的是，沿同一个链可以显示许多消息（可能发自不同的方向），并且每个消息都有唯一的一个顺序号。

序列图和通信图是同构的，它们之间可以相互转换。

交互概览图是UML2.0新增的交互图之一，它描述交互（特别是关注控制流），但是抽象掉了消息和生命线。它使用活动图的表示法。纯粹的交互概览图中所有的活动都是交互发生。另一种新增的、特别适合实时和嵌入式系统建模的交互图称为时序图，时序图关注沿着线性时间轴、生命线内部和生命线之间的条件改变。它描述对象状态随着时间改变的情况，很像示波器，适合分析周期和非周期性任务。

5）状态图

状态图（state diagram）展现了一个状态机，它由状态、转换、事件和活动组成。状态图关注系统的动态视图，它对于接口、类和协作的行为建模尤为重要，强调对象行为的事件顺序。

状态图通常包括简单状态和组合状态、转换（事件和动作）。如图10-20所示。

图10-20　UML状态图

可以用状态图对系统的动态方面建模。这些动态方面可以包括出现在系统体系结构的任何视图中的任何一种对象的按事件排序的行为，这些对象包括类（各主动类）、接口、构件和节点。

当对系统、类或用例的动态方面建模时，通常是对反应型对象建模。

一个反应型或事件驱动的对象是这样一个对象，其行为通常是由对来自语境外部的事件做出反应来刻画的。反应型对象在接收到一个事件之前通常处于空闲状态。当它接收到一个事件时，它的反应常常依赖于以前的事件。在这个对象对事件做出反应后，它就又变成闲状态，等待下一个事件。对于这种对象，将着眼于对象的稳定状态、能够触发从状态到状态的转换的事件，以及当每个状态改变时所发生的动作。

6）活动图

活动图（activity diagram）是一种特殊的状态图，它展现了在系统内从一个活动到另一个活动的流程。活动图专注于系统的动态视图，它对于系统的功能建模特别重要，并强调对象间的控制流程。

活动图一般包括活动状态和动作状态、转换和对象。

用活动图建模的控制流中，会发生一些事情。可能要对一个设置属性值或返回一些值的表达式求值；也可能要调用对象上的操作，发送一个消息给对象，甚至创建或销毁对象，这些可执行的原子计算被称作动作状态，因为它们是该系统的状态，每个原子计算都代表一个动作的执行。动作状态不能被分解。动作状态是原子的，也就是说事件可以发生，但动作状态的工作不能被中断。最后，动作状态的工作所占用的执行时间一般被看作是可忽略的。

活动状态能够进一步被分解，它们的活动由其他的活动图表示。活动状态不是原子的，它们可以被中断。并且，一般来说，还要考虑到它需要花费一段时间来完成。可以把一个动作状态看作一个活动状态的特例。类似地，可以把一个活动状态看作一个组合，它的控制流由其他的活动状态和动作状态组成。

活动图可以表示分支和汇合。

当对一个系统的动态方面建模时，通常有两种使用活动图的方式。

（1）对工作流建模。此时所关注的是与系统进行协作的参与者所观察到的活动。工作流常常位于软件系统的边缘，用于可视化、详述、构造和文档化开发系统所涉及的业务过程。在活动图的这种用法中，对对象流的建模是特别重要的。

（2）对操作建模。此时是把活动图作为流程图使用，对一个计算的细节部分建模。在活动图的这种用法中，对分文、分叉和汇合状态的建模是特别重要的。用于这种方式的活动图语境包括该操作的参数和它的局部对象。

7）构件图

构件图（component diagram）展现了一组构件之间的组织和依赖。构件图专注于系统的静态实现视图。它与类图相关，通常把构件映射为一个或多个类、接口或协作。

8）部署图

部署图（deployment diagram）展现了运行处理节点以及其中构件的配置。部署图给出了体系结构的静态实施视图。它与构件图相关，通常一个节点包含一个或多个构件。

10.5　设计模式

10.5.1　设计模式的要素

“每一个模式描述了一个在我们周围不断重复发生的问题，以及该问题的解决方案的核心。这样，你就能一次又一次地使用该方案而不必做重复劳动”。设计模式的核心在于提供了相关问题的解决方案。

设计模式一般有如下4个要素。

（1）模式名称（pattern name）。一个助记名，它用一两个词来描述模式的问题、解决方案和效果。命名一个新的模式增加了设计词汇。设计模式允许在较高的抽象层次上进行设计。基于一个模式词汇表，就可以讨论模式并在编写文档时使用它们。模式名可以帮助人们思考，便于人们与其他人交流设计思想及设计结果。找到恰当的模式名也是设计模式工作的难点之一。

（2）问题（problem）。描述了应该在何时使用模式。它解释了设计问题和问题存在的前因后果，可能描述了特定的设计问题，如怎样用对象表示算法等；也可能描述了导致不灵活设计的类或对象结构。有时候，问题部分会包括使用模式必须满足的一系列先决条件。

（3）解决方案（solution）。描述了设计的组成成分，它们之间的相互关系及各自的职责和协作方式。因为模式就像一个模板，可应用于多种不同场合，所以解决方案并不描述一个特定而具体的设计或实现，而是提供设计问题的抽象描述和怎样用一个具有一般意义的元素组合（类或对象组合）来解决这个问题。

（4）效果（consequences）。描述了模式应用的效果及使用模式应权衡的问题。尽管描述设计决策时，并不总提到模式效果，但它们对于评价设计选择和理解使用模式的代价及好处具有重要意义。软件效果大多关注对时间和空间的衡量，它们也表述了语言和实现问题。因为复用是面向对象设计的要素之一，所以模式效果包括它对系统的灵活性、扩充性或可移植性的影响，显式地列出这些效果对理解和评价这些模式很有帮助。

设计模式确定了所包含的类和实例，它们的角色、协作方式以及职责分配。每一个设计模式都集中于一个特定的面向对象设计问题或设计要点，描述了什么时候使用它，在另一些设计约束条件下是否还能使用，以及使用的效果和如何取舍。按照设计模式的目的可以分为三大类，如下表所示。

创建型模式与对象的创建有关；结构型模式处理类或对象的组合；行为型模式对类或对象怎样交互和怎样分配职责进行描述。

10.5.2　创建型设计模式

创建型模式抽象了实例化过程，它们帮助一个系统独立于如何创建、组合和表示它的那些对象。一个类创建型模式使用继承改变被实例化的类，而一个对象创建型模式将实例化委托给另一个对象。

随着系统演化得越来越依赖于对象复合而不是类继承，创建型模式变得更为重要。当这种情况发生时，重心从对一组固定行为的硬编码（hard-coding）转移为定义一个较小的基本行为集，这些行为可以被组合成任意数目的更复杂的行为。这样创建有特定行为的对象要求的不仅仅是实例化一个类。

在这些模式中有两个不断出现的主旋律。第一，它们都将关于该系统使用哪些具体的类的信息封装起来。第二，它们隐藏了这些类的实例是如何被创建和放在一起的。整个系统关于这些对象所知道的是由抽象类所定义的接口。因此，创建型模式在什么被创建，谁创建它，它是怎样被创建的，以及何时创建这些方面给予了很大的灵活性。它们允许用结构和功能差别很大的“产品”对象配置一个系统。配置可以是静态的（即在编译时指定），也可以是动态的（在运行时）。

【例10.8】　Singleton模式。

通常情况下，用户可以对应用系统进行配置，并将配置信息保存在配置文件中。应用系统在启动时首先将配置文件加载到内存中，这些内存配置信息应该有且仅有一份。应用单身模式（Singleton）以保证Configure类只能有一个实例。这样，Configure类的使用者无法定义该类的多个实例，否则会产生编译错误。

程序如下：

10.5.3　结构型设计模式

结构型模式涉及到如何组合类和对象以获得更大的结构。结构型类模式采用继承机制来组合接口或实现。一个简单的例子是采用多重继承方法将两个以上的类组合成一个类，结果这个类包含了所有父类的性质。这一模式尤其有助于多个独立开发的类库协同工作。其中一个例子是类形式的Adapter模式。一般来说，适配器使得一个接口与其他接口兼容，从而给出了多个不同接口的统一抽象。为此，类适配器对一个adaptee类进行私有继承。这样，适配器就可以用adaptee的接口表示它的接口。

结构型对象模式不是对接口和实现进行组合，而是描述了如何对一些对象进行组合，从而实现新功能的一些方法。因为可以在运行时刻改变对象组合关系，所以对象组合方式具有更大的灵活性，而这种机制用静态类组合是不可能实现的。

Composite模式是结构型对象模式的一个实例。它描述了如何构造一个类层次式结构，这一结构由两种类型的对象所对应的类构成。其中的组合对象使得用户可以组合基元对象以及其他的组合对象，从而形成任意复杂的结构。在Proxy模式中，proxy对象作为其他对象的一个方便的替代或占位符。它的使用可以有多种形式，例如可以在局部空间中代表一个远程地址空间中的对象，也可以表示一个要求被加载的较大的对象，还可以用来保护对敏感对象的访问。Proxy模式还提供了对对象的一些特有性质的一定程度上的间接访问，从而可以限制、增强或修改这些性质。Flyweight模式为了共享对象定义了一个结构。至少有两个原因要求对象共享：效率和一致性。Flyweight的对象共享机制主要强调对象的空间效率。使用很多对象的应用必须考虑每一个对象的开销。使用对象共享而不是进行对象复制，可以节省大量的空间资源。但是，仅当这些对象没有定义与上下文相关的状态时，它们才可以被共享。Flyweight的对象没有这样的状态。任何执行任务时需要的其他一些信息仅当需要时才传递过去。由于不存在与上下文相关的状态，因此Flyweight对象可以被自由地共享。

如果说Flyweight模式说明了如何生成很多较小的对象，那么Facade模式则描述了如何用单个对象表示整个子系统。模式中的facade用来表示一组对象，facade的职责是将消息转发给它所表示的对象。Bridge模式将对象的抽象和其实现分离，从而可以独立地改变它们。

Decorator模式描述了如何动态地为对象添加职责。Decorator模式是一种结构型模式，这一模式采用递归方式组合对象，从而允许添加任意多的对象职责。例如，一个包含用户界面组件的Decorator对象可以将边框或阴影这样的装饰添加到该组件中，或者它可以将窗口滚动和缩放这样的功能添加到组件中。可以将一个Decorator对象嵌套在另外一个对象中，就可以很简单地增加两个装饰，添加其他的装饰也是如此。因此，每个Decorator对象必须与其组件的接口兼容并且保证将消息传递给它。Decorator模式在转发一条信息之前或之后都可以完成它的工作（例如绘制组件的边框）。许多结构型模式在某种程度上具有相关性。

10.5.4　行为设计模式

行为模式涉及到算法和对象间职责的分配。行为模式不仅描述对象或类的模式，还描述它们之间的通信模式。这些模式刻画了在运行时难以跟踪的复杂的控制流。它们将用户的注意力从控制流转移到对象间的联系方式上来。

行为类模式使用继承机制在类间分派行为。本章包括两个这样的模式，其中Templa teMethod较为简单和常用。模板方法是一个算法的抽象定义，它逐步地定义该算法，每一步调用一个抽象操作或一个原语操作，子类定义抽象操作以具体实现该算法。另一种行为类模式是Interpreter，它将一个文法表示为一个类层次，并实现一个解释器作为这些类的实例上的一个操作。

行为对象模式使用对象复合而不是继承。一些行为对象模式描述了一组对等的对象怎样相互协作以完成其中任一个对象都无法单独完成的任务。这里一个重要的问题是对等的对象。

如何互相了解对方。对等对象可以保持显式的对对方的引用，但那会增加它们的耦合度。在极端情况下，每一个对象都要了解所有其他的对象。Mediator在对等对象间引入一个mediator对象以避免这种情况的出现。mediator提供了松耦合所需的间接性。

Chain of Responsibility提供更松的耦合。它让用户通过一条候选对象链隐式地向一个对象发送请求。根据运行时刻情况任一候选者都可以响应相应的请求。候选者的数目是任意的，可以在运行时刻决定哪些候选者参与到链中。

Observer模式定义并保持对象间的依赖关系。典型的Observer的例子是Smalltalk中的模型／视图／控制器，其中一旦模型的状态发生变化，模型的所有视图都会得到通知。

其他的行为对象模式常将行为封装在一个对象中并将请求指派给它。Strategy模式将算法封装在对象中，这样可以方便地指定和改变一个对象所使用的算法。Command模式将请求封装在对象中，这样它就可作为参数来传递，也可以被存储在历史列表里，或者以其他方式使用。State模式封装一个对象的状态，使得当这个对象的状态对象变化时，该对象可改变它的行为。Visitor封装分布于多个类之间的行为，而Iterator则抽象了访问和遍历一个集合中的对象的方式。

【例10.9】Observer模式。

在一公文处理系统中，开发者定义了一个公文类OfficeDoc，其中定义了公文具有的属性和处理公文的相应方法。当公文的内容或状态发生变化时，关注此OfficeDoc类对象的相应的DocExplorer对象都要更新其自身的状态。一个OfficeDoc对象能够关联一组DocExplorer对象。当OfficeDoc对象的内容或状态发生变化时，所有与之相关联的DocExplorer对象都将得到通知，这种应用被称为观察者模式。

程序如下：

第11章　标准化和软件知识产权基础知识

11.1　标准化基础知识

11.1.1　标准化的基本概念

1．标准、标准化的概念

标准（Standard）是对重复性事物和概念所做的统一规定。范（Specification）、规程（Code）都是标准的一种形式。

标准化（Standardization）是在经济、技术、科学及管理等社会实践中，以改进产品、过程和服务的适用性，防止贸易壁垒，促进技术合作，促进最大社会效益为目的，对重复性事物和概念通过制定、发布和实施标准达到统一，获最佳秩序和社会效益的过程。

2．标准化的范围和对象

标准化涉及的范围包括生产、经济、技术、科学及管理等社会实践中具有的重复性事物和概念以及需要建立统一技术要求的各个领域。在这些领域中，凡具有多次重复使用和需要制定标准的具体产品，以及各种定额、规划、要求、方法和概念等，都可称为标准化的对象。如产品的品种、规格、形式；产品的性能、质量；包装的尺寸、材质、样式；信息表示、信息处理技术；试验方法、测试方法、检验方法、操作方法和抽样方法等各类方法；量值、单位、术语、符号、代码、标志；日常操作、安全操作、劳动保护、安全卫生、保护环境等方面的规范、规程或规定；市场调查、研制开发、物资采购、加工工艺、质量控制、质量检验、销售、售后服务等各阶段的管理事项，等等。

标准化对象一般可分为两大类，一类是标准化的具体对象，即需要制定标准的具体事物；另一类是标准化的总体对象，即各种具体对象的全体所构成的整体，通过它可以研究各种具体对象的共同属性、本质和普遍规律。

3．标准化过程模式

标准是标准化活动的产物，其目的和作用都是通过制定和贯彻具体的标准来体现的。标准化不是一个孤立的事物，而是一个活动过程。标准化活动过程一般包括标准产生（调查、研究、形成草案、批准发布）子过程、标准实施（宣传、普及、监督、咨询）子过程和标准更新（复审、废止或修订）子过程等。

1）标准的制定

制定标准的过程，实际上就是总结和积累人类社会实践经验的过程，每一个新标准的产生，都标志着某一领域或某项活动的经验被规范化。标准的产生一般包括调查研究、制定计划（立项）、起草标准、征求意见、审查、批准发布等标准生成阶段。这个过程中产生的问题，有程序性的问题（如征求意见、审查等）；更有实质性的问题，即标准的适用性、可行性、先进性方面的问题。这个过程随标准化对象的不同而不同。起草标准时一般需要吸收新的科研成果、新的技术成就和新的实践经验，以便通过标准的形式去推广新成果、新技术和新经验。为保证标准的编写质量，便于资料管理，体现其严肃性，标准文件有统一的格式。标准从制定到批准发布的一整套工作程序和审批制度，体现了标准产生的科学规律，表现出其法律特性。ISO和IEC是两个国际标准化组织，为规范国际标准的产生过程发布了导则性文件。我国依据该导则性文件制定了相应的国家标准，以规范国家标准的制定程序。

2）标准的实施

标准的实施过程，实际上就是推广和普及已被规范化的实践经验的过程。标准的实施过程包括哪些活动内容，没有统一的规定，一般有标准的宣传、贯彻执行和监督检查等。标准化活动是一项有组织的活动，并且始终是一个组织的行为。通常，国家、区域和行业的标准化管理组织以及标准化团体，通过宏观管理（标准规划、计划和协调）；健全以国际标准、区域性标准、国家标准、行业标准为主的标准文献资料；建立健全行业和企业标准备案管理制度，提高生产、经销和服务单位执行标准的自觉性。我国强制性标准的实施是通过强制性的监督检查来推动，依法开展标准的实施与监督。对于推荐性标准的实施，尚无明确有效的措施，需要建立和完善社会主义市场经济体制下的技术法规。《标准化法》、《国家标准管理办法》等法规，规定了我国标准化工作的方针、政策、任务和标准化体制等。它们是我国推行标准化，实施标准化管理和监督的重要依据。企业根据企业和市场的需求及技术发展趋势的需要，开展标准立项的前期研究，为标准制定提供可靠的依据；开展标准化审查，制止不符合强制性标准规定、不符合标准化技术政策的技术和产品；确定重点标准的实施与监督的项目计划，对各个环节进行全面动态跟踪管理。

3）标准的更新

已经实现了标准化的事物，实施一段时间后，有可能突破原先的规定，有新的需求，使某些环节的标准失去意义，需要对它再制定标准。标准的更新是实践经验的深化和提高的过程。通过信息反馈总结经验和问题，依据客观环境的新变化和新要求，提出标准修订的新目标，更新标准。人类社会实践是一个永不止息的活动，标准复审、标准确认和标准修订也是一个永无止境的过程。

4．标准的分类

标准化工作是一项复杂的系统工程，标准为适应不同的要求从而构成一个庞大而复杂的系统，为便于研究和应用的目的，可以从不同的角度和属性将标准进行分类。

1）根据适用范围分类

根据标准制定的机构和标准适用的范围，可分为国际标准、国家标准、行业标准、企业（机构）标准及项目（课题）标准。

（1）国际标准（International Standard）。国际标准是指国际标准化组织（ISO）、国际电工委员会（IEC）所制定的标准，以及ISO出版的《国际标准题内关键词索引（KWIC Index）》中收录的其他国际组织制定的标准。国际标准在世界范围内统一使用，各国可以自愿采用，不强制使用，但由于国际标准集中了一些先进工业国家的技术经验，各个国家为外贸上的利益和本国利益考虑往往都积极采用国际标准。

（2）国家标准（National Standard）。国家标准是由政府或国家级的机构制定或批准的、适用于全国范围的标准，是一个国家标准体系的主体和基础，国内各级标准必须服从且不得与之相抵触。常见的国家标准如下。

①　中华人民共和国国家标准（GB）。GB是我国最高标准化机构中华人民共和国国家技术监督局所公布实施的标准，简称为“国标”。

②　美国国家标准（ANSI）。ANSI是美国国家标准协会（American National standards institute, ANSI）制定的标准。

③　英国国家标准（British Standard, BS）。BS是英国标准学会（BSI）制定的标准。

④　日本工业标准（Japanese Industrial Standard, JIS）。JIS是日本工业标准调查会（JISC）制定的标准。

（3）区域标准（Regional Standard）。区域标准（也称地区标准）泛指世界上按地理、经济或政治划分的某一区域标准化团体所通过的标准。它是为了某一区域的利益建立的标准。通常，地区标准主要是指太平洋地区标准会议（PASC）、欧洲标准化委员会（CEN）、亚洲标准咨询委员会（ASAC）、非洲地区标准化组织（ARSO）等地区组织所制定和使用的标准。

（4）行业标准（Specialized Standard）。由行业机构、学术团体或国防机构制定，并适用于某个业务领域的标准。有如下一些标准。

①　美国电气和电子工程师学会标准（IEEE）。IEEE通过的标准常常要报请ANSI审批，使其具有国家标准的性质。因此，IEEE公布的标准常冠有ANSI字头。例如，ANSI/IEEE Str828-1983（软件配置管理计划标准）。

②　中华人民共和国国家军用标准（GJB）。GJB是由我国国防科学技术工业委员会批准，适用于国防部门和军队使用的标准。例如，1988年发布实施的GJB473-88（军用软件开发规范）。我国各主管部、委（局）批准发布，在其范围内统一使用的标准。

③　美国国防部标准（Department Of Defense-Standards, DOD-STD）。DOD-STD适用于美国国防部门。美国军用标准MIL-S（Military-Standards）适用于美军内部。

（5）企业标准（Company Standard）。由企业或公司批准、发布的标准，某些产品标准由其上级主管机构批准、发布。

例如，美国IBM公司通用产品部（General Products Division）1984年制定的“程序设计开发指南”，仅供该公司内部使用。

（6）项目规范（Project Specification）。由某一科研生产项目组织制定，且为该项任务专用的软件工程规范。例如，计算机集成制造系统（CIMS）的软件工程规范。

根据《中华人民共和国标准化法》的规定，我国标准分为国家标准、行业标准、地方标准和企业标准4类。这4类标准主要是适用的范围不同，不是标准技术水平高低的分级。

（1）国家标准。由国务院标准化行政主管部门制定的需要全国范围内统一的技术要求。

（2）行业标准。没有国家标准而又需在全国某个行业范围内统一的技术标准，由国务院有关行政主管部门制定并报国务院标准化行政主管部门备案的标准。

（3）地方标准。没有国家标准和行业标准而又需在省、自治区、直辖市范围内统一的工业产品的安全、卫生要求，由省、自治区、直辖市标准化行政主管部门制定并报国务院标准化行政主管部门和国务院有关行业行政主管部门备案的标准。

（4）企业标准。企业生产的产品没有国家标准、行业标准和地方标准，由企业自行组织制定、作为组织生产依据的相应标准，或者在企业内制定适用的，比国家标准、行业标准或地方标准更严格的企业（内控）标准，并按省、自治区、直辖市人民政府的规定备案的标准（不含内控标准）。

2）根据标准的性质分类

根据标准的性质可分为技术标准、管理标准和工作标准。

（1）技术标准（Technique Standard）。技术标准是针对重复性的技术事项而制定的标准，是从事生产、建设及商品流通时需要共同遵守的一种技术依据。按其标准化对象特征和作用，可分为基础标准、产品标准、方法标准、安全卫生与环境保护标准等；按其标准化对象在生产流程中的作用，可分为零部件标准、工装标准、设备维修保养标准及检查标准等；按标准的强制程度，可分为强制性与推荐性标准；按标准在企业中的适用范围，又可分为公司标准、工厂标准和科室标准等。

（2）管理标准（Administrative Standard）。管理标准是管理机构为行使其管理职能而制定的具有特定管理功能的标准，主要用于规定人们在生产活动和社会实践中的组织结构、职责权限、过程方法、程序文件、资源分配以及方针、目标、措施、影响管理的因素等事宜，它是合理组织国民经济，正确处理各种生产关系，正确实现合理分配，提高生产效率和效益的依据。在实际工作中通常按照标准所起的作用不同，将管理标准分为技术管理标准、生产组织标准、经济管理标准、行政管理标准、业务管理标准和工作标准等。

（3）工作标准（Work Standard）。为协调整个工作过程，提高工作质量和效率，针对具体岗位的工作制定的标准。对工作的内容、方法、程序和质量要求所制定的标准，称为工作标准。工作标准的内容包括各岗位的职责和任务、每项任务的数量、质量要求及完成期限，完成各项任务的程序和方法，与相关岗位的协调、信息传递方式，工作人员的考核与奖罚方法等。对生产和业务处理的先后顺序、内容和要达到的要求所作的规定称为工作程序标准。工作程序标准是工作标准的一种，其目的是使各项工作条理化、标准化和规范化，以求得最佳工作秩序、工作质量和工作效率。以管理工作为对象所制定的标准，称为管理工作标准。管理工作标准的内容主要包括工作范围、内容和要求；与相关工作的关系；工作条件；工作人员的职权与必备条件；工作人员的考核、评价及奖惩办法等。

3）根据标准化的对象和作用分类

根据标准的对象和作用，标准可分为基础标准、产品标准、方法标准、安全标准、卫生标准、环境保护标准和服务标准等。

4）根据法律的约束性分类

根据标准的法律约束性，可分为强制性标准和推荐性标准。

（1）强制性标准。根据《标准化法》的规定，企业和有关部门对涉及其经营、生产、服务、管理有关的强制性标准都必须严格执行，任何单位和个人不得擅自更改或降低标准。对违反强制性标准而造成不良后果以至重大事故者，由法律、行政法规规定的行政主管部门依法根据情节轻重给予行政处罚，直至由司法机关追究刑事责任。

强制性标准是国家技术法规的重要组成，它符合世界贸易组织贸易技术壁垒协定关于“技术法规”定义，即“强制执行的规定产品特性或相应加工方法的包括可适用的行政管理规定在内的文件。技术法规也可包括或专门规定用于产品、加工或生产方法的术语、符号、包装标志或标签要求”，为使我国强制性标准与WTO/TBT规定衔接，其范围限制在国家安全、防止欺诈行为、保护人身健康与安全、保护动物植物的生命和健康以及保护环境等方面。

（2）推荐性标准。在生产、交换、使用等方面，通过经济手段或市场调节而自愿采用的一类标准称为推荐性标准。这类标准不具有强制性，任何单位均有权决定是否采用，违反这类标准，不构成经济或法律方面的责任。应当指出的是，推荐性标准一经接受并采用，或由各方商定后同意纳入经济合同中，就成为各方必须共同遵守的技术依据，具有法律上的约束性。

推荐性标准是指导性标准，由公认机构批准的，非强制性的，为了通用或反复使用的目的，为产品或相关生产方法提供规则、指南或特性的文件。标准也可以包括或专门规定用于产品、加工或生产方法的术语、符号、包装标准或标签要求。由于推荐性标准是协调一致的文件，不受政府和社会团体的利益干预，能更科学地规定特性或指导生产，《标准化法》鼓励企业积极采用推荐性标准。为了防止企业利用标准欺诈消费者，要求采用低于推荐性标准的企业标准组织生产的企业向消费者明示其产品标准水平。

5．标准的代号和编号

1）国际标准ISO的代号和编号

国际标准ISO的代号和编号的格式为：ISO＋标准号＋［杠＋分标准号］＋冒号＋发布年号（方括号中的内容可有可无），例如，ISO8402：1987和ISO9000-1：1994是ISO标准的代号和编号。

2）国家标准的代号和编号

我国国家标准的代号由大写汉语拼音字母构成，强制性国家标准代号为GB，推荐性国家标准的代号为GB/T。

国家标准的编号由国家标准的代号、标准发布顺序号和标准发布年代号（4位数）组成。

（1）强制性国家标准：GB　XXXXX—XXXX。

（2）推荐性国家标准：GB/T　XXXXX—XXXX。

3）行业标准的代号和编号

（1）行业标准代号。行业标准代号由汉语拼音大写字母组成，由国务院各有关行政主管部门提出其所管理的行业标准范围的申请报告，国务院标准化行政主管部门审查确定并正式公布该行业标准代号。已正式公布的行业代号有QJ（航天）、SJ（电子）、JB（机械）和JR（金融系统）等。

（2）行业标准的编号。行业标准的编号由行业标准代号、标准发布顺序及标准发布年代号（4位数）组成，表示方法如下。

●　强制性行业标准编号：XX　XXXX—XXXX。

●　推荐性行业标准编号：XX/T　XXXX—XXXX。

4）地方标准的代号和编号

（1）地方标准的代号。由大写汉语拼音DB加上省、自治区、直辖市行政区划代码的前两位数字（如北京市11、天津市12、上海市31等），再加上斜线T组成推荐性地方标准；不加斜线T为强制性地方标准，表示方法如下。

●　强制性地方标准：DBXX。

●　推荐性地方标准：DBXX/T。

（2）地方标准的编号。地方标准的编号由地方标准代号、地方标准发布顺序号和标准发布年代号（4位数）三部分组成，表示方法如下。

●　强制性地方标准：DBXX　XXX—XXXX。

●　推荐性地方标准：DBXX/T　XXX—XXXX。

5）企业标准的代号和编号

（1）企业标准的代号。企业标准的代号由汉语大写拼音字母Q加斜线再加企业代号组成，企业代号可用大写拼音字母、阿拉伯数字或两者兼用组成。企业代号按中央所属企业和地方企业分别由国务院有关行政主管部门或省、自治区、直辖市政府标准化行政主管部门会同同级有关行政主管部门加以规定。例如，Q/XXX。企业标准一经制定颁布，即对整个企业具有约束性，是企业法规性文件，没有强制性企业标准和推荐性企业标准之分。

（2）企业标准的编号。企业标准的编号由企业标准代号、标准发布顺序号和标准发布年代号（4位数）组成，表示方法：Q/XXX　XXXX—XXXX

6．国际标准和国外先进标准

由于国际贸易广泛开展，产品在国际市场上的竞争越来越激烈，要求产品不但要具有高的质量，好的性能，还要具有广泛的通用性、互换性。这就需要采用统一的、先进的标准，按照国际上统一的标准生产，避免采用的标准不一致，带来国际贸易障碍。国际标准和国外先进标准集中了一些先进工业国家的技术经验，世界各国都积极采用国际标准或先进的标准。

1）国际标准

国际标准是指国际标准化组织、国际电工委员会所制定的标准，以及ISO出版的《国际标准题内关键词索引（KWIC Index）》中收录的其他国际组织制定的标准。1983年3月出版的KWIC索引（第1版）中共收录了24个国际组织制定的7600个标准，其中ISO标准占68％，IEC标准占18.5％，其他22个国际组织的标准共968个，占13.5％。1989年出版的KWIC索引（第2版）共收录了ISO与IEC制定的800个标准，以及其他27个国际组织的1200多条标准。ISO推荐列入KWIC索引的有27个国际组织，一些未列入《KWIC Index》的国际组织所制定的某些标准也被国际公认。这27个国际组织制定的标准化文献主要有国际标准、国际建议、国际公约、国际公约的技术附录和国际代码，也有经各国政府认可的强制性要求。对国际贸易业务服务和信息交流具有重要影响。

2）国外先进标准

国外先进标准是指国际上有权威的区域性标准；世界上经济发达国家的国家标准和通行的团体标准；包括知名企业标准在内的其他国际上公认的先进标准。主要有如下几种标准。

（1）国际上有权威的区域性标准。如欧洲标准化委员会（CEN）、欧洲电工标准化委员会（CENELEC）、欧洲广播联盟（EBU）、亚洲大洋洲开放系统互联研讨会（AOW）、亚洲电子数据交换理事会（ASEB）等制定的标准。

（2）世界经济技术发达国家的国家标准。如美国国家标准、德国国家标准（DIN）、英国国家标准、日本国工业标准、瑞典国家标准（SIS）、法国国家标准（NF）、瑞士国家标准（SNV）、意大利国家标准（UNI）和俄罗斯国家标准（TOCTP）等。

（3）国际公认的行业性团体标准。如美国材料与实验协会标准（ASTM）、美国石油学会标准（API）、美国军用标准（MIL）、美国电气制造商协会标准（NEMA）、美国电影电视工程师协会标准（SMPTE）、美国机械工程师协会标准（ASME）和英国石油学会（IP）等。

（4）国际公认的先进企业标准。如美国IBM公司、美国HP公司、芬兰诺基亚公司和瑞士钟表公司等企业标准等。

3）采用国际标准和国外先进标准的原则

（1）根据我国国民经济发展的需要，确定一定时期采用国际标准和国外先进标准的方向、任务。国民经济发展的需要是采用国际标准和国外先进标准的出发点。当国民经济处于建立社会主义经济体系初期，采用国际标准和国外先进标准就是要从战略上、从国家长远利益上考虑突出国际标准中的重大基础标准、通用方法标准的采用问题。当国民经济发展到一定阶段，如产品质量要赶超世界先进水平时，对国际标准和国外先进标准中的先进产品标准和质量标准就成为采用的重要对象。

（2）很多国际标准是国际上取得多年实际经验后被公认的，一般说来不必都去进行实践验证。为加快采用国际标准和国外先进标准的速度，一般都简化制定手续，基本上采取“先拿来用，然后实践验证，再补充修改”的模式。

（3）促进产品质量水平的提高是当前采用国际标准和国外先进标准的一项重要原则。产品质量问题首先有标准问题，只有采用了先进的国际标准或先进的国外标准，才能提高我国的标准水平。只有提高了标准水平，才能有力地促进产品质量的提高。要赶超世界先进水平，就要采用国际标准和国外先进标准。

（4）要紧密结合我国实际情况、自然资源和自然条件，需符合国家的有关法令、法规和政策，做到技术先进、经济合理、安全可靠、方便使用、促进生产力发展。

（5）对于国际标准中的基础标准、方法标准、原材料标准和通用零部件标准，需要先行采用。通过的基础标准、方法标准以及有关安全、卫生和环境保护等方面的标准，一般应与国际标准协调一致。

（6）在技术引进和设备进口中采用国际标准，应符合《技术引进和设备进口标准化审查管理办法（试行）》中的规定。例如，原则上不引进和进口英制设备，等等。

（7）当国际标准不能满足要求，或尚无国际标准时，应参照上述原则，积极采用国外先进标准。

4）采用程度

采用国际标准或国外先进标准的程度，分为等同采用、等效采用和非等效采用。

（1）等同采用。指国家标准等同于国际标准，仅有或没有编辑性修改。编辑性修改是指不改变标准技术的内容的修改。如纠正排版或印刷错误；标点符号的改变；增加不改变技术内容的说明、指示等。因此，可以认为等同采用就是指国家标准与国际标准相同，不做或稍做编辑性修改，编写方法完全相对应。

（2）等效采用。指国家标准等效于国际标准，技术内容上只有很小差异。编辑上不完全相同，编写方法不完全相对应。如奥地利标准ONORMS 5022内河船舶噪声测量标准中，包括一份试验报告的推荐格式，而相应的国际标准ISO 2922中没有此内容。

（3）非等效采用。指国家标准不等效于国际标准，在技术上有重大技术差异。即国家标准中有国际标准不能接受的条款，或者在国际标准中有国家标准不能接受的条款。在技术上有重大差异的情况下，虽然国家标准制定时是以国际标准为基础，并在很大程度上与国际标准相适应，但不能使用“等效”这个术语。通常包括以下三种情况。

①　国家标准包含的内容比国际标准少。国家标准较国际要求低或选国际标准中部分内容。国家标准与国际标准之间没有互相接受条款的“逆定理”情况。

②　国家标准包含的内容比国际标准多。国家标准增加了内容或类型，且具有较高要求等，也没有“逆定理”情况。

③　国家标准与国际标准有重叠。部分内容是完全相同或技术上相同，但在其他内容上却互不包括对方的内容。

采用国际标准或国外先进标准，按国家标准GB161的规定编写。采用程度符号用缩写字母表示，等同采用idt或IDT表示，等效采用eqv或EQV表示，非等效采用neq或NEQ表示。

①　等同采用：GB XXXX—XXXX（idt ISO XXXX—XXXX）。

②　等效采用：GB XXXX—XXXX（eqv ISO XXXX—XXXX）。

③　非等效采用：GB XXXX—XXXX（neq ISOXXXX—XXXX）。

11.1.2　信息技术标准化

信息技术标准化是围绕信息技术开发、信息产品的研制和信息系统建设、运行与管理而开展的一系列标准化工作。其中主要包括信息技术术语、信息表示、汉字信息处理技术、媒体、软件工程、数据库、网络通信、电子数据交换、电子卡、管理信息系统和计算机辅助技术等方面的标准化。

1．信息编码标准化

编码是一种信息表现形式。在一定条件下，它对事物或概念的描述比自然语言要直接、简洁、准确和有力。

编码是一种信息交换的技术手段。对信息进行编码实际上是对文字、音频、图形和图像等信息进行处理，使之量化，从而便于利用各种通信设备进行信息传递和利用计算机进行信息处理。作为一种信息交换的技术手段，必须保证信息交换的一致性。为了统一编码系统，人们借助了标准化这个工具，制定了各种标准代码，如国际上比较通用的ASCⅡ码（美国信息交换标准代码）。

2．汉字编码标准化

汉字编码是对每一个汉字按一定的规律用若干个字母、数字、符号表示出来。汉字编码的方法很多，主要有数字编码，如电报码、四角号码字典中的汉字号码都是用数字对汉字进行编码；拼音编码，即用汉字的拼音字母对汉字进行编码；字形编码，即用汉字的偏旁部首和笔划结构与各个英文字母相对应，再用英文字母的组合代表相应的汉字。对每一种汉字编码，计算机内部都有一种相应的二进制内部码，不同的汉字编码，在使用上不能替换。若把各种编码方案都存入计算机供人们选择，在技术上和使用效果上则是困难的和不经济的。我国在汉字编码标准化方面取得的突出成就就是信息交换用汉字编码字符集国家标准的制定。该字符集共有6集。其中，GB 2312—80信息交换用汉字编码字符集是基本集，收入常用基本汉字和字符7445个。GB 7589—87和GB 7590—87分别是第二辅助集和第四辅助集，各收入现代规范汉字7426个。GB/T 12345—90是辅助集，它与第三辅助集和第五辅助集分别是与基本集、第二辅助集和第四辅助集相对应的繁体字的汉字字符集。除汉字编码标准化外，汉字信息处理标准化的内容还包括汉字键盘输入的标准化；汉字文字识别输入和语音识别输入的标准化；汉字输出字体和质量的标准化；汉字属性和汉语词语的标准化等。

3．软件工程标准化

随着软件工程学科的发展，人们对计算机软件的认识逐渐深入。软件工作的范围从只是使用程序设计语言编写程序，扩展到整个软件生存期。软件工程的目的是改善软件开发的组织，降低开发成本，缩短开发时间，提高工作效率，提高软件质量。它在内容上包括软件开发的软件概念形成、需求分析、计划组织、系统分析与设计、结构程序设计、软件调试、软件测试和验收、安装和检验、软件运行和维护，以及软件运行的终止。同时还有许多技术管理工作，如过程管理、产品管理、资源管理，以及确认与验证工作，如评审与审计、产品分析等。软件工程最显著的特点就是把个别的、自发的、分散的、手工的软件开发变成一种社会化的软件生产方式。软件生产的社会化必然要求软件工程实行标准化。软件工程标准的类型也是多方面的，常常是跨越软件生存期各个阶段。所有这些方面都应逐步建立标准或规范。软件工程标准化的主要内容包括过程标准（如方法、技术和度量等）、产品标准（如需求、设计、部件、描述、计划和报告等）、专业标准（如道德准则、认证等）、记法标准（如术语、表示法和语言等）、开发规范（准则、方法和规程等）、文件规范（文件范围、文件编制、文件内容要求、编写提示）、维护规范（软件维护、组织与实施等）以及质量规范（软件质量保证、软件配置管理、软件测试和软件验收等）等。

我国1983年5月成立“计算机与信息处理标准化技术委员会”，下设13个分技术委员会，其中程序设计语言分技术委员会和软件工程技术委员会与软件相关。我国推行软件工程标准化工作的总原则是向国际标准靠拢，对于能够在我国适用的标准全部按等同采用的方法，以促进国际交流。虽然我国的软件工程标准化工作仍处于起步阶段，但在提高我国软件工程水平，促进软件产业的发展以及加强与国外的软件交流等方面必将起到应有的作用。现已得到国家批准的软件工程国家标准如下。

1）基础标准

（1）信息处理—程序构造及其表示法的约定GB/T 13502—92。

（2）信息处理系统—计算机系统配置图符号及其约定GB/T 14085—93。

（3）软件工程术语标准GB/T 11457—89。

（4）软件工程标准分类法GB/T 15538—95。

2）开发标准

（1）软件开发规范GB 8566—88。

（2）计算机软件单元测试GB/T 15532—95。

（3）软件维护指南GB/T 14079—93。

3）文档标准

（1）计算机软件产品开发文件编制指南GB 8567—88。

（2）计算机软件需求说明编制指南GB/T 9385—88。

（3）计算机软件测试文件编制指南GB/T 9386—88。

4）管理标准

（1）计算机软件配置管理计划规范GB/T 12505—90。

（2）计算机软件质量保证计划规范GB/T 12504—90。

（3）计算机软件可靠性和可维护性管理GB/T 14394—93。

（4）信息技术、软件产品评价、质量特性及其使用指南GB/T 16260—96。

11.1.3　标准化组织

1．国际标准化组织

ISO和IEC是世界上两个最大、最具有权威的国际标准化组织。目前，由ISO确认并公布的国际标准化组织还有国际计量局（BIPM）、联合国教科文组织（UNESCO）、世界卫生组织（WHO）、世界知识产权组织（WIPO）、国国际信息与文献联合会（FID）、国际法制计量组织（OIML）等27个国际组织。

（1）国际标准化组织（International Organization for Standardization, ISO）。国际标准化组织是世界上最大的非政府性的，由各国标准化团体（ISO成员团体）组成的世界性联合专门机构。它成立于1947年2月，其宗旨是世界范围内促进标准化工作的发展，以利于国际资源的交流和合理配置，扩大各国在知识、科学、技术和经济领域的合作。其主要活动是制定国际标准，协调世界范围内的标准化工作，组织各成员国和技术委员会进行交流，以及与其他国际性组织进行合作，共同研究有关标准问题，出版ISO国际标准。制定国际标准的工作通常由ISO的技术委员会完成，各成员团体若对某技术委员会确立的项目感兴趣，均有权参加该委员会的工作。与ISO保持联系的各国际组织（官方的或非方的）也可参加有关工作。此外，ISO还负责协调世界范围内的标准化工作，组织各成员国和技术委员会进行情报交流，并和其他国际性组织保持联系和合作，共同研究感兴趣的有关标准化问题。在电工技术标准化方面，ISO与IEC保持密切合作关系。ISO的工作语言是英文、法文、俄文，会址设在日内瓦。

ISO的成员团体分正式成员和通信成员。正式成员是指由各国最有代表性的标准化机构代表其国家或地区参加，并且只允许每个国家有一个组织参加。通信成员是尚未建立全国性标准化机构的国家，一般不参与ISO的技术工作，但可参会了解工作进展，当条件成熟时，可以通过一定程序成为正式成员。1947年，ISO成立时只有25个成员团体，但经过50年的发展，现有团体（国家标准化机构）135个，其中成员团体（正式成员）90个、通信成员35个、注册成员9个。

成员全体大会是ISO的最高权力机构。理事会是ISO常务机构，由正、副主席，司库和18个理事国代表组成，每年召开一次会议，理事会成员任期三年，每年改选1/3的成员。理事会下设若干专门委员会，其中之一是技术委员会（TC），技术委员会完成ISO的技术工作，ISO按专业性质设立技术委员会，各技术委员会根据工作需要可设立若干分委员会（SC），TC和SC下面可设立若干工作组（WG）。TC和SC的成员分为积极参加成员（P成员）和观察员（O成员）两种。P成员可参与TC、SC的技术工作，而O成员则只能了解工作进度和得到技术组织的信息资料，不参加技术工作。每个TC或SC均从P成员中任命一个成员主持秘书处并领导该委员会或分委员会。ISO现有技术组织2871个，其中技术委员会191个、分技术委员会572个、工作组2063个，临时专题小组45个。

（2）国际电工委员会（International Electrotechnical Commission, IEC）。国际电工委员会成立于1906年，是世界上最早的非政府性国际电工标准化机构，是联合国经社理事会（ECOSOC）的甲级咨询组织。自1947年ISO成立后，IEC曾作为一个电工部并入ISO，但在技术上和财务上仍保持独立。1976年，双方又达成新协议，IEC从ISO中分离出来，两组织各自独立，自愿合作，互为补充，共同建立国际标准化体系，IEC负责有关电气工程及电子领域国际标准化工作，其他领域则由ISO负责。

IEC的工作领域包括电工领域各个方面，如电力、电子、电信和原子能方面的电工技术等。理事会是IEC的最高权利机构，会址设在日内瓦。IEC理事会下设执行委员会和合格评定局，执行委员会负责管理技术委员会和技术咨询委员会；合格评定局管理各认证委员会，在组织上自成体系。它是世界范围的自愿认证机构，其宗旨是促进国家或国际间的自由贸易。按照严格的认证程序，以国际标准为依据对电工产品生产厂的技术力量和管理水平实行全面的审核和评审；对要求认证的生产的元器件，按标准要求进行测试检验。对符合质量要求的产品授以合格证书，以确保产品质量达到和保持标准要求的质量水平。

（3）区域标准化组织。区域标准化组织是指同处一个地区的某些国家组成的标准化组织。区域是指世界上按地理、经济或民族利益划分的区域。参加组织的机构有的是政府性的，有的是非政府性的，是为发展同一地区或毗邻国家间的经济及贸易，维护该地区国家的利益，协调本地区各国标准和技术规范而建立的标准化机构。其主要职能是制定、发布和协调该地区的标准。

①　欧洲标准化委员会（CEN）。CEN成立于1961年，是由欧洲经济共同体（EEC）、欧洲自由联盟（EFTA）所属国家的标准化机构所组成，主要任务是协调各成员国的标准，制定必要的欧洲标准（EN），实行区域认证制度。

②　欧洲电工标准化委员会（CEN EL EC）。CEN EL EC成立于1972年，是由欧洲电工标准协调委员会（CEN EL）和欧洲电工协调委员会共同市场小组（CEN EL COM）合并组成，主要是协调各成员国电器和电子领域的标准，以及电子元器件质量认证，制定部分欧洲标准。

③　亚洲标准咨询委员会（ASAC）。ASAC成立于1967年，由联合国亚洲与太平洋经社委员会协商建立，主要是在ISO、IEC标准的基础上，协调各成员国标准化活动，制定区域性标准。

④　国际电信联盟（International Telecommunication Union, ITU）。ITU于1865年5月在巴黎成立，1947年成为联合国的专门机构，是世界各国政府的电信主管部门之间协调电信事务的一个国际组织，研究制定有关电信业务的规章制度，通过决议提出推荐标准，收集有关情报。ITU的目的和任务是维持和发展国际合作，以改进和合理利用电信，促进技术设施的发展及有效应用，以提高电信业务的效率。

（4）行业标准化组织。行业标准化组织是指制定和公布适应于某个业务领域标准的专业标准化团体，以及在其业务领域开展标准化工作的行业机构、学术团体或国防机构。

①　美国电气电子工程师学会（Institute of Electrical and Electronics Engineers, IEEE）。IEEE是由美国电气工程师学会（AIEE）和美国无线电工程师学会（IRE）于1963年合并而成，是美国规模最大的专业学会。IEEE主要制定的标准内容有电气与电子设备、试验方法、元器件、符号、定义以及测试方法等。近年来，该学会专门成立了软件标准分技术委员会（SESS），积极开展了软件标准化活动，取得了显著成果，受到了软件界的关注。IEEE通过的标准常常要报请ANSI审批，使其具有国家标准的性质。因此，IEEE公布的标准常冠有ANSI字头。例如，ANSI/IEEE Str 828-1983软件配置管理计划标准。

②　美国国防部批准、颁布适用于美国军队内部使用的标准，代号为DOD（采用公制计量单位的以DOD表示）和MIL。

③　我国国防科学技术工业委员会批准、颁布适合于国防部门和军队使用的标准，代号为GJB。例如，1988年发布实施的GJB473-88军用软件开发规范。

（5）国家标准化组织。国家标准化组织是指在国家范围内建立的标准化机构，以及政府确认（或承认）的标准化团体，或者接受政府标准化管理机构指导并具有权威性的民间标准化团体。这些组织主要如下。

①　美国国家标准学会（American National Standards Institute, ANSI）。ANSI是非赢利性质的民间标准化团体，但它实际上已成为美国国家标准化中心，美国各界标准化活动都围绕它开展。通过它使政府有关系统和民间系统相互配合，起到了政府和民间标准化系统之间的桥梁作用。ANSI协调并指导美国全国的标准化活动，给标准制定、研究和使用单位以帮助，提供国内外标准化情报。ANSI本身很少制定标准，主要是将其他专业标准化机构的标准经协商后冠以ANSI代号，成为美国国家标准。

②　英国标准学会（British Standards Institution, BSI）。BSI是世界上最早的全国性标准化机构，它是政府认可的、独立的、非盈利性民间标准化团体。主要任务是为增产节约而努力协调生产者和用户之间的关系，促进生产，达到标准化；制定和修订英国标准，并促进其贯彻执行；以学会名义，对各种标志进行登记，并颁发许可证；必要时采取各种行动，保护学会利益；对外代表英国参加国际或区域标准化活动。

③　德国标准化学会（Deutsches Institution fur Normung, DIN）。DIN始建于1917年，当时称为德国工业标准委员会（NADI），1926年改为德国标准委员会（DNA），1975年又改名为联邦德国标准化学会。DIN是一个经注册的公益性民间标准化团体，前联邦政府承认它为联邦德国和西柏林的标准化机构。

④　法国标准化协会（Association Francaise de Normalisation, AFNOR）。AFNOR成立于1926年，它是一个公益性的民间团体，也是一个被政府承认，为国家服务的组织。1941年5月24日颁布的一项法令确认AFNOR接受法国政府的标准化管理机构“标准化专署”指导，按政府指导开展工作，并定期向标准化专员汇报工作。AFNOR负责标准的制、修订工作，宣传、出版、发行标准，实施产品质量认证。

11.1.4　ISO 9000标准简介

1．ISO 9000标准

ISO 9000标准是一系列标准的统称。ISO 9000系列标准由ISO/TC176制定。TC176是ISO的第176个技术委员会（质量管理和质量保证技术委员会），专门负责制定质量管理和质量保证技术的标准。经过TC176多年的协调以及有关国家质量管理专家近10年的不懈努力，总结了美国、英国和加拿大等工业发达国家的质量保证技术实践的经验，于1986年6月15日正式发布了ISO 8402《质量—术语》标准，又于1987年3月正式公布了ISO 9000～ISO9004的5项标准，这5项标准与ISO 8402：1986一起统称为ISO 9000：1987系列标准。随着工业、经济的不断发展，国际贸易交往的日益频繁，产品质量成为各方面关注的焦点。因此，质量管理的对象也从硬件逐渐扩展到了软件、文档和服务。按照全面质量管理的PDCA工作模式，TC176于1990年在第九届年会上提出了《90年代国际质量标准的实施策略》，决定对ISO 9000标准进行修改。通过对ISO 9001/2/3/4标准的技术内容修订（局部修改），形成1994版。在ISO 9000-1：1994中增加了过程和过程网络等基本概念，为进一步修改提供了理论基础。之后，引入与PDCA循环模式统一的过程方法模式，从总体结构和原则到具体的技术内容对ISO 9000系列标准进行了全面的修改，形成2000年版。2000年12月15日，ISO 9000：2000系列标准正式发布实施。

ISO 9000系列标准的质量管理模式为企业管理注入新的活力和生机，给质量管理体系提供评价基础，为企业进行世界贸易带来质量可信度。从ISO 9000系列标准的演变过程可见，ISO 9001：1987系列标准从自我保证的角度出发，更多关注的是企业内部的质量管理和质量保证；ISO 9001：1994系列标准则通过20个质量管理体系要素，把用户要求、法规要求及质量保证的要求纳入标准的范围中；ISO 9001：2000系列标准在标准构思和标准目的等方面体现了具有时代气息的变化，过程方法的概念，顾客需求的考虑，以及将持续改进的思想贯穿于整个标准，把组织的质量管理体系满足顾客要求的能力和程度体现在标准的要求之中。

2．ISO 9000：2000系列标准文件结构

ISO 9000：2000族标准现有14项标准，由4个核心标准，1个支持标准，6个技术报告，3个小册子和1个技术规范构成，如表11-1所示。

表11-1　ISO 9000：2000系列标准文件结构

3．ISO 9000：2000核心标准简介

（1）ISO 9000：2000《质量管理体系　基础和术语》。该标准描述了质量管理体系的基础，并规定了质量管理体系的术语和基本原理。术语标准是讨论问题的前提，统一术语是为了明确概念，建立共同的语言。

该标准在总结了质量管理经验的基础上，明确了一个组织在实施质量管理中必须遵循的8项质量管理原则，也是ISO 9000：2000族标准制定的指导思想和理论基础。该标准提出的10个部分87个术语，在语言上强调采用非技术性语言，使所有潜在用户易于理解。为便于使用，在标准附录中，推荐了以“概念图”方式来描述相关术语的关系。

（2）ISO 9001：2000《质量管理体系要求》。该标准提供了质量管理体系的要求，供组织证实其提供满足顾客和适用法规要求产品的能力时使用。组织通过有效地实施体系，包括过程的持续改进和预防不合格，使顾客满意。该标准是用于第三方认证的唯一质量管理体系要求标准，通常用于企业建立质量管理体系以及申请认证。它主要通过对申请认证组织的质量管理体系提出各项要求来规范组织的质量管理体系。主要分为5大模块的要求，即质量管理体系、管理职责、资源管理、产品实现、测量分析和改进，构成一种过程方法模式的结构，符合PDCA循环规则，且通过持续改进的环节使质量管理体系的水平达到螺旋式上升的效应，其中每个模块中又有许多分条款。

（3）ISO 9004：2000《质量管理体系业绩改进指南》。该标准给出了改进质量管理体系业绩的指南，描述了质量管理体系应包括持续改进的过程，强调通过改进过程，提高组织的业绩，使组织的顾客和其他相关方满意。

该标准和ISO 9001：2000协调一致并可一起使用的质量管理体系标准，两个标准采用相同的原则，但应注意其适用范围不同，而且ISO 9004标准不拟作为ISO 9001标准的实施指南。通常情况下，当组织的管理者希望超越ISO 9001标准的最低要求，追求增长的业绩改进时，一般以ISO 9004标准作为指南。

（4）ISO 19011：2001《质量管理体系和环境管理体系审核指南》。该标准提供了质量管理体系和环境管理体系审核的基本原则、审核方案的管理、环境和质量管理体系的实施以及对环境和质量管理体系评审员资格要求提供了指南。

该标准是ISO/TC 176与ISO/TC 207（环境管理技术委员会）联合制订的，按照“不同管理体系，可以共同管理和审核”的原则，在术语和内容方面兼容了质量管理体系和环境管理体系两方面特点。

4．ISO 9000：2000系列标准确认的8项原则

ISO 9000族质量管理体系在ISO 9000：2000和ISO 9004：2000标准中提及的8项质量管理原则是该标准中一个非常重要的内容，是整个ISO 9000族质量管理体系标准的精髓和纲领。它是标准的理论基础，又是组织领导者进行质量管理的基本原则。增加了贯彻8项质量管理的基本原则的要求，并且以此为主线，贯彻于各个过程中，体现在标准的全部内容上。8项质量管理原则是以顾客为中心、领导作用、全员参与、过程方法、管理的系统方法、持续改进、基于事实的决策方法、互利的供方关系。

11.1.5　ISO/IEC 15504过程评估标准简介

ISO/IEC15504由ISO/IEC JTC1/SC7/WG10与其项目组软件过程改进和能力确定（Software Process Improve-ment and Capability determination, SPICE）和国际项目管理机构共同完成，并收集整理了来自20多个国家的工业、政府以及大学专家的意见和建议，同时得到世界各地软件工程师的帮助，包括与美国SEI、加拿大贝尔合作。

ISO/IEC 15504提供了一个软件过程评估的框架，它可以被任何软件企业用于软件的设计、管理、监督、控制以及提高获得、供应、开发、操作、升级和支持的能力。ISO/IEC 15504提供了一种有组织的、结构化的软件过程评估方法，以便实施软件过程的评估。在ISO/IEC 15504中定义的过程评估办法旨在为描述工程评估结果的通用方法提供一个基本原则，同时也对建立在不同但兼容的模型和方法上的评估进行比较。过程评估有两个主要的使用环境：软件过程改进或者软件过程能力评定。在软件过程改进环境中，过程评估提供了诸多方法，用于在企业内部根据所选择过程的性能来描述当前实践的特性。根据企业的商业需要对结果进行分析以确定该过程内在的优点、不足和风险，从而可以判断过程是否有效地实现其目标以及导致质量低下、耗时过多、成本过高的主要原因。评估结果可以软件过程改进的优先顺序提供相应的基础和依据。在确定软件过程能力的环境中，评估是通过与目标过程能力的对比分析，评估过程的能力，从而确定所评估的过程对实现有关项目的风险情况。在ISO/IEC 15504文件中涉及了过程评估的各个方面，其文档主要包括以下几个部分。

1．概念和绪论指南

该部分给出了关于软件过程改进和过程评估概念及其在过程能力确定方面的总体信息。它描述了ISO/IEC 15504文档的各部分是如何组织在一起的，并为选择和使用各部分提供指南。此外，本部分还解释了ISO/IEC 15504中所包含的要求对执行评估的适用性；支持工具的建立与选择以及在附加过程的建立和发展方面所起的作用。

2．过程和过程能力参考模型

该部分从内容上说是在比较高的层次上详细定义了一个用于过程评估的二维参考模型。此模型中描述了过程和过程能力。通过将过程中的特点与不同的能力等级相比较，可以用此模型中定义的一系列过程和框架对过程能力加以评估。

3．实施评估

为了确保等级评定的一致性和可重复性（即标准化），ISO/IEC 15504为软件过程评估提供了一个框架并为进行评审提出了最低要求。这些要求有助于确保评估输出内在的一致性，并为评级和验证与要求的一致性提供了依据。该部分以及与该部分有关的内容详细定义了实施评估时的需要，这样得到的评估结果才有可重复性、可信性以及可持续性。

4．评估实施指南

通过这部分内容，可以指导使用者如何进行软件过程评估。这个具有普通意义的指导可适用于所有企业，同时也适用于采用不同的方法、技术以及支持工具的过程评估。它包括如何选择并使用兼容的评估，如何选择用于支持评估的方法，如何选择适合于评估的工具与手段。该部分内容对过程评估作了概述，并且以指南形式对用于评估的兼容模型、文件化的评估过程和工具的使用和选择等方面的需求作了解释。

5．评估模型和标志指南

这部分内容为支持过程评估提出了一个评估模型的范例，此评估模型与第二部分所描述的参考模型相兼容，具体表述了任何兼容评估模型都期望具有的核心特征。该指南是以此评估模型中所包含的指示标志的形式给出的，这些指示标志可在过程改进程序中加以使用，还有助于评价和选择评估模型、方法或工具。采用这种方式并结合可靠的方法，有可能对过程能力做出一致的且可重复的评估。

6．评估师能力指南

这部分提供了关于评估师进行软件过程评估的资格和准备的指南。它详细说明了一些可用于验证评估师胜任能力和相应的教育、培训和经验，还包括可能用于验证胜任能力和证实受教育程度、培训情况和经验的一些机制。

7．过程改进应用指南

该部分提供了关于使用软件过程评估作为首要方法去理解一个企业软件过程的当前状态，以及使用评估结果去形成并优化改进方案方面的指南。该过程改进指南涉及过程改进综述、过程改进方法、文化问题和管理等专题，包括一个过程量度的总体框架。指南用于指导在连续循环里进行软件过程改进时把软件过程评估当成改进框架和方法的一部分使用。一个企业可以根据它的具体情况和需要从参考模型中选择所有的或一部分软件过程用于评估或改进。

8．确定供方能力应用指南

该部分内容为过程能力确定目的而进行的过程评审提供应用指南。它讲述了为对过程能力加以判断，应如何定义输入和如何运用评估结果。这不仅可直接用于对当前状况进行判断，而且也可以对复杂情况加以判断，例如对未来的预测。该部分中关于过程能力的判断方法不仅适合于任何希望确定其自身软件过程的过程能力的企业，也同样适应于对供应商的能力进行判断。

9．词汇

本部分定义了ISO/IEC TR 15504整个技术报告中使用的术语。术语首先按字母排列顺序以便于参考，然后再按逻辑类进行分类以便于理解（将相互相关的术语安排在一类）。

11.2　知识产权基础知识

11.2.1　知识产权基本概念

1．工业产权的定义

知识产权（也称为智慧财产权）是现代社会发展中不可缺少的一种法律制度。知识产权保护制度为促进知识的积累与交流，丰富人们的精神生活，提高全民族的科学文化素质，推动经济的发展以及社会进步起到了极其重要的作用。知识产权是指人们基于自己的智力活动创造的成果和经营管理活动中的经验、知识而依法享有的权利。我国《民法通则》规定，知识产权是指民事权利主体（公民、法人）基于创造性的智力成果。

根据有关国际公约规定（世界知识产权组织公约第二条），知识产权的保护对象包括下列各项有关权利。

（1）文学、艺术和科学作品。

（2）表演艺术家的表演以及唱片和广播节目。

（3）人类一切活动领域的发明。

（4）科学发现。

（5）工业品外观设计。

（6）商标、服务标记以及商业名称和标志。

（7）制止不正当竞争。

（8）在工业、科学、文学艺术领域内由于智力创造活动而产生的一切其他权利。

在世界贸易组织协议的知识产权协议中，第一部分第一条所规定的知识产权范围，还包括“未披露过的信息专有权”，这主要是指工商业经营者所拥有的经营秘密和技术秘密等商业秘密。知识产权保护制度是随着科学技术的进步而不断发展和完善的。随着科学技术的迅速发展，知识产权保护对象的范围不断扩大，不断涌现新型的智力成果，如计算机软件、生物工程技术、遗传基因技术和植物新品种等，这些都是当今世界各国所公认的知识产权的保护对象。知识产权可分为工业产权和著作权两类。

（1）工业产权。根据保护工业产权巴黎公约第一条的规定，工业产权包括专利、实用新型、工业品外观设计、商标、服务标记、厂商名称、产地标记或原产地名称、制止不正当竞争等项内容。此外，商业秘密、微生物技术和遗传基因技术等也属于工业产权保护的对象。近年来，在一些国家可以通过申请专利对计算机软件进行专利保护。对于工业产权保护的对象，可以分为“创造性成果权利”和“识别性标记权利”。发明、实用新型和工业品外观设计等属于创造性成果权利，它们都表现出比较明显的智力创造性。其中，发明和实用新型是利用自然规律做出的解决特定问题的新的技术方案，工业品外观设计是确定工业品外表的美学创作，完成人需要付出创造性劳动。商标、服务标记、厂商名称、产地标记或原产地名称以及我国反不正当竞争法第5条中规定的知名商品所特有的名称、包装、装潢等为识别性标记权利。

（2）著作权。著作权（也称为版权）是指作者对其创作的作品享有的人身权和财产权。人身权包括发表权、署名权、修改权和保护作品完整权等；财产权包括作品的使用权和获得报酬权，即以复制、表演、播放、展览、发行、摄制电影、电视、录像或者改编、翻译、注释、编辑等方式使用作品的权利，以及许可他人以上述方式使用作品并由此获得报酬的权利。关于著作权保护的对象，按照《保护文学艺术作品伯尔尼公约》第二条规定，包括文学、科学和艺术领域内的一切作品，不论其表现形式或方式如何，诸如书籍、小册子和其他著作；讲课、演讲和其他同类性质作品；戏剧或音乐作品；舞蹈艺术作品和哑剧作品；配词或未配词的乐曲；电影作品以及与使用电影摄影艺术类似的方法表现的作品；图画、油画、建筑、雕塑、雕刻和版画；摄影作品以及使用与摄影艺术类似的方法表现的作品；与地理、地形建筑或科学技术有关的示意图、地图、设计图、草图和立体作品等。

有些智力成果可以同时成为这两类知识产权保护的客体，例如，计算机软件和实用艺术品受著作权保护的同时，权利人还可以通过申请发明专利和外观设计专利获得专利权，成为工业产权保护的对象。在美国和欧洲的一些国家，如果计算机软件自身包含技术构成，软件又能实现某方面的技术效果，如工业自动化控制等，则不应排除专利保护。按照世界知识产权组织公约，科学发现也被列为知识产权。我国民法通则第97条规定了科学发现权的法律地位，但很难将其归属工业产权或著作权。可见，新产生的一些知识产权不一定就归为这两个类别。知识产权所保护的对象是依赖人类智力劳动创造的，特别是高科技创新产业进发出的呈现千姿百态的知识财产，都属人类智力劳动的成果，法律都赋予它们民事权利。

2．知识产权的特点

（1）无形性。知识产权是一种无形财产权。知识产权的客体指的是智力创作性成果（也称为知识产品），是一种没有形体的精神财富。它是一种可以脱离其所有者而存在的无形信息，可以同时为多个主体所使用，在一定条件下不会因多个主体的使用而使该项知识财产自身遭受损耗或者灭失。

（2）双重性。某些知识产权具有财产权和人身权双重性，例如著作权，其财产权属性主要体现在所有人享有的独占权以及许可他人使用而获得报酬的权利，所有人可以通过独自实施获得收益，也可以通过有偿许可他人实施获得收益，还可以像有形财产那样进行买卖或抵押；其人身权属性主要是指署名权等。有的知识产权具有单一的属性，例如，发现权只具有名誉权属性，而没有财产权属性；商业秘密只具有财产权属性，而没有人身权属性；专利权、商标权主要体现为财产权。

（3）确认性。无形的智力创作性成果不像有形财产那样直观可见，因此，智力创作性成果的财产权需要依法审查确认，以得到法律保护。例如，我国的发明人所完成的发明，其实用新型或者外观设计，已经具有价值和使用价值，但是，其完成人尚不能自动获得专利权，完成人必须依照专利法的有关规定，向国家专利局提出专利申请，专利局依照法定程序进行审查，申请符合专利法规定条件的，由专利局做出授予专利权的决定，颁发专利证书，只有当专利局发布授权公告后，其完成人才享有该项知识产权。又如，商标权的获得，大多数国家（包括中国）都实行注册制，只有向国家商标局提出注册申请，经审查核准注册后，才能获得商标权。文学艺术作品以及计算机软件的著作权虽然是自作品完成其权利即自动产生，但有些国家也要实行登记或标注版权标记后才能得到保护。

（4）独占性。由于智力成果具有可以同时被多个主体所使用的特点，因此，法律授予知识产权一种专有权，具有独占性。未经权利人许可，任何单位或个人不得使用，否则就构成侵权，应承担相应的法律责任。法律对各种知识产权都规定了一定的限制，但这些限制不影响其独占性特征。少数知识产权不具有独占性特征，例如技术秘密的所有人不能禁止第三人使用其独立开发完成的或者合法取得的相同技术秘密，可以说，商业秘密不具备完全的财产权属性。

（5）地域性。知识产权具有严格的地域性特点，即各国主管机关依照本国法律授予的知识产权，只能在其本国领域内受法律保护，例如中国专利局授予的专利权或中国商标局核准的商标专用权，只能在中国领域内受保护，其他国家则不给予保护，外国人在我国领域外使用中国专利局授权的发明专利，不侵犯我国专利权。所以，我国公民、法人完成的发明创造要想在外国受保护，必须在外国申请专利。著作权虽然自动产生，但它受地域限制，我国法律对外国人的作品并不都给予保护，只保护共同参加国际条约国家的公民作品。同样，公约的其他成员国也按照公约规定，对我国公民和法人的作品给予保护。还有按照两国的双边协定，相互给予对方国民的作品保护。

（6）时间性。知识产权具有法定的保护期限，一旦保护期限届满，权利将自行终止，成为社会公众可以自由使用的知识。至于期限的长短，依各国的法律确定。例如，我国发明专利的保护期为20年，实用新型专利权和外观设计专利权的期限为10年，均自专利申请日起计算。我国公民的作品发表权的保护期为作者终生及其死亡后50年。我国商标权的保护期限自核准注册之日起10年内有效，但可以根据其所有人的需要无限地延长权利期限，在期限届满前6个月内申请续展注册，每次续展注册的有效期为10年，续展注册的次数不限。如果商标权人逾期不办理续展注册，其商标权也将终止。商业秘密受法律保护的期限是不确定的，该秘密一旦被公众所知悉，即成为公众可以自由使用的知识。

3．中国知识产权法规

目前，我国已形成了比较完备的知识产权保护的法律体系，保护知识产权的法律主要有中华人民共和国著作权法、中华人民共和国专利法、中华人民共和国继承法、中华人民共和国公司法、中华人民共和国合同法、中华人民共和国商标法、中华人民共和国产品质量法、中华人民共和国反不正当竞争法、中华人民共和国刑法、中华人民共和国计算机信息系统安全保护条例、中华人民共和国计算机软件保护条例和中华人民共和国著作权法实施条例等。

11.2.2　计算机软件著作权

1．计算机软件著作权的主体与客体

1）计算机软件著作权的主体

计算机软件著作权的主体是指享有著作权的人。根据著作权法和《计算机软件保护条例》的规定，计算机软件著作权的主体包括公民、法人和其他组织。著作权法和《计算机软件保护条例》未规定对主体的行为能力限制，同时对外国人、无国籍人的主体资格，奉行“有条件”的国民待遇原则。

（1）公民。公民（即指自然人）通过以下途径取得软件著作权主体资格。

①　公民自行独立开发软件（软件开发者）。

②　订立委托合同，委托他人开发软件，并约定软件著作权归自己享有。

③　通过转让途径取得软件著作财产权主体资格（软件权利的受让者）。

④　公民之间或与其他主体之间，对计算机软件进行合作开发而产生的公民群体或者公民与其他主体成为计算机软件作品的著作权人。

⑤　根据《继承法》的规定，通过继承取得软件著作财产权主体资格。

（2）法人。法人是具有民事权利能力和民事行为能力，依法独立享有民事权利和承担义务的组织。计算机软件的开发往往需要较大投资和较多的人员，法人则具有资金来源丰富和科技人才众多的优势，因而法人是计算机软件著作权的重要主体。法人取得计算机软件著作权主体资格一般通过以下途径。

①　由法人组织并提供创作物质条件所实施的开发，并由法人承担社会责任。

②　通过接受委托、转让等各种有效合同关系而取得著作权主体资格。

③　因计算机软件著作权主体（法人）发生变更而依法成为著作权主体。

（3）其他组织。其他组织是指除去法人以外的能够取得计算机软件著作权的其他民事主体，包括非法人单位、合作伙伴等。

2）计算机软件著作权的客体

计算机软件著作权的客体是指著作权法保护的计算机软件著作权的范围（受保护的对象）。根据《著作权法》第三条和《计算机软件保护条例》第二条的规定，著作权法保护的计算机软件是指计算机程序及其有关文档。著作权法对计算机软件的保护是指计算机软件的著作权人或者其受让者依法享有著作权的各项权利。

（1）计算机程序。《根据计算机软件保护条例》第三条第一款的规定，计算机程序是指为了得到某种结果而可以由计算机等具有信息处理能力的装置执行的代码化指令序列，或者可被自动转换成代码化指令序列的符号化语句序列。计算机程序包括源程序和目标程序，同一程序的源程序文本和目标程序文本视为同一软件作品。

（2）计算机软件的文档。根据《计算机软件保护条例》第三条第二款的规定，计算机程序的文档是指用自然语言或者形式化语言所编写的文字资料和图表，用来描述程序的内容、组成、设计、功能规格、开发情况、测试结果及使用方法等。文档一般以程序设计说明书、流程图和用户手册等表现。

2．计算机软件受著作权法保护的条件

《计算机软件保护条例》规定，依法受到保护的计算机软件作品必须符合下列条件。

（1）独立创作。受保护的软件必须由开发者独立开发创作，任何复制或抄袭他人开发的软件不能获得著作权。当然，软件的独创性不同于专利的创造性。程序的功能设计往往被认为是程序的思想概念，根据著作权法不保护思想概念的原则，任何人都可以设计具有类似功能的另一件软件作品。但是，如果用了他人软件作品的逻辑步骤的组合方式，则对他人软件构成侵权。

（2）可被感知。受著作权法保护的作品应当是作者创作思想在固定载体上的一种实际表达。如果作者的创作思想未表达出来不可以被感知，就不能得到著作权法的保护。因此，《计算机软件保护条例》规定，受保护的软件必须固定在某种有形物体上，例如固定在存储器、磁盘和磁带等设备上，也可以是其他的有形物，如纸张等。

（3）逻辑合理。逻辑判断功能是计算机系统的基本功能。因此，受著作权法保护的计算机软件作品必须具备合理的逻辑思想，并以正确的逻辑步骤表现出来，才能达到软件的设计功能。毫无逻辑性的计算机软件不能计算出正确结果，也就毫无价值。

根据《计算机软件保护条例》第六条的规定，除计算机软件的程序和文档外，著作权法不保护计算机软件开发所用的思想、概念、发现、原理、算法、处理过程和运算方法。也就是说，利用已有的上述内容开发软件，并不构成侵权。因为开发软件时所采用的思想、概念等均属计算机软件基本理论的范围，是设计开发软件不可或缺的理论依据，属于社会公有领域，不能被个人专有。

3．计算机软件著作权的权利

1）计算机软件的著作人身权

《中华人民共和国著作权法》规定，软件作品享有两类权利，一类是软件著作权的人身权（精神权利）；另一类是软件著作权的财产权（经济权利）。《计算机软件保护条例》规定，软件著作权人享有发表权和开发者身份权，这两项权利与软件著作权人的人身权是不可分离的。

（1）发表权。发表权是指决定软件作品是否公之于众的权利，即指软件作品完成后，以复制、展示、发行或者翻译等方式使软件作品在一定数量不特定人的范围内公开。发表权具体内容包括软件作品发表的时间、发表的形式以及发表的地点等。

（2）开发者身份权（也称为署名权）。开发者身份权是指作者为表明身份在软件作品中署自己名字的权利。署名可有多种形式，既可以署作者的姓名，也可以署作者的笔名，或者作者自愿不署名。对一部作品来说，通过署名即可对作者的身份给予确认。我国著作权法规定，如无相反证明，在作品上署名的公民、法人或非法人单位为作者。因此，作品的署名对确认著作权的主体具有重要意义。开发者的身份权不随软件开发者的消亡而丧失，且无时间限制。

2）计算机软件的著作财产权

著作权中的财产权是指能够给著作权人带来经济利益的权利。这种经济利益的实现，要依靠著作权人对作品使用才能获得。财产权通常是指由软件著作权人控制和支配，并能够为权利人带来一定经济效益的权利。《计算机软件保护条例》规定，软件著作权人享有下述软件财产权。

（1）使用权。即在不损害社会公共利益的前提下，以复制、修改、发行、翻译和注释等方式合作软件的权利。

（2）复制权。即将软件作品制作一份或多份的行为。复制权就是版权所有人决定实施或不实施上述复制行为或者禁止他人复制其受保护作品的权利。

（3）修改权。即对软件进行增补、删节，或者改变指令、语句顺序等以提高、完善原软件作品的作法。修改权即指作者享有的修改或者授权他人修改软件作品的权利。

（4）发行权。发行是指为满足公众的合理需求，通过出售、出租等方式向公众提供一定数量的作品复制件。发行权即以出售或赠与方式向公众提供软件的原件或者复制件的权利。

（5）翻译权。翻译是指以不同于原软件作品的一种程序语言转换该作品原使用的程序语言，而重现软件作品内容的创作。简单地说，也就是指将原软件从一种程序语言转换成另一种程序语言的权利。

（6）注释权。软件作品的注释是指对软件作品中的程序语句进行解释，以便更好地理解软件作品。注释权是指著作权人对自己的作品享有进行注释的权利。

（7）信息网络传播权。以有线或者无线信息网络方式向公众提供软件作品，使公众可在其个人选定的时间和地点获得软件作品的权利。

（8）出租权。即有偿许可他人临时使用计算机软件的复制件的权利，但是软件不是出租的主要标的的除外。

（9）使用许可权和获得报酬权。即许可他人以上述方式使用软件作品的权利（许可他人行使软件著作权中的财产权）和依照约定或者有关法律规定获得报酬的权利。

（10）转让权。即向他人转让软件的使用权和使用许可权的权利。软件著作权人可以全部或者部分转让软件著作权中的财产权。

3）软件合法持有人的权利

根据《计算机软件保护条例》的规定，软件的合法复制品所有人享有下述权利。

（1）根据使用的需要把软件装入计算机等能存储信息的装置内。

（2）根据需要进行必要的复制。

（3）为了防止复制品损坏而制作备份复制品。这些复制品不得通过任何方式提供给他人使用，并在所有人丧失该合法复制品所有权时，负责将备份复制品销毁。

（4）为了把该软件用于实际的计算机应用环境或者改进其功能性能而进行必要的修改。但是，除合同约定外，未经该软件著作权人许可，不得向任何第三方提供修改后的软件。

4．计算机软件著作权的行使

1）软件经济权利的许可使用

软件经济权利的许可使用是指软件著作权人或权利合法受让者，通过合同方式许可他人使用其软件，并获得报酬的一种软件贸易形式。许可使用的方式可分为以下几种。

（1）独占许可使用。权利人通过书面合同授权，被授权方可以根据合同规定的方式、条件和时间确定独占性，权利人不得将软件使用权授予第三方，权利人自己不能使用该软件。

（2）独家许可使用。权利人通过书面合同授权，被授权方可以根据合同规定的方式、条件和时间确定独占性，权利人不得将软件使用权授予第三方，权利人自己可以使用该软件。

（3）普通许可使用。权利人通过书面合同授权，被授权方可以根据合同规定的方式、条件和时间确定独占性，权利人可以将软件使用权授予第三方，权利人自己可以使用该软件。

（4）法定许可使用和强制许可使用。在法律特定的条款下，不经软件著作权人许可，使用其软件。

2）软件经济权利的转让使用

软件经济权利的转让使用是指软件著作权人将其享有的软件著作权中的经济权利全部转移给他人。软件经济权利的转让将改变软件权利的归属，原始著作权人的主体地位随着转让活动的发生而丧失，软件著作权受让者成为新的著作权主体。《计算机软件保护条例》规定，软件著作权转让必须签订书面合同。同时，软件转让活动不能改变软件的保护期。转让方式包括出买、赠与、抵押和赔偿等，可以定期转让或者永久转让。

5．计算机软件著作权的保护期

根据《著作权法》和《计算机软件保护条例》的规定，计算机软件著作权的权利自软件开发完成之日起产生，保护期为50年。保护期满，除开发者身份权以外，其他权利终止。一旦计算机软件著作权超出保护期，软件就进入公有领域。计算机软件著作权人的单位终止和计算机软件著作权人的公民死亡均无合法继承人时，除开发者身份权以外，该软件的其他权利进入公有领域。软件进入公有领域后成为社会公共财富，公众可无偿使用。

6．计算机软件著作权的归属

我国著作权法对著作权的归属采取了“创作主义”原则，明确规定著作权属于作者，除非另有规定。《计算机软件保护条例》第九条规定“软件著作权属于软件开发者，本条例另有规定的情况除外。”这是我国计算机软件著作权归属的基本原则。

计算机软件开发者是计算机软件著作权的原始主体，也是享有权利最完整的主体。软件作品是开发者从事智力创作活动所取得的智力成果，是脑力劳动的结晶。其开发创作行为使开发者直接取得该计算机软件的著作权。因此，《计算机软件保护条例》第九条明确规定“软件著作权属于软件开发者”，即以软件开发的事实来确定著作权的归属，谁完成了计算机软件的开发工作，软件的著作权就归谁享有。

1）职务开发软件著作权的归属

职务软件作品是指公民在单位任职期间为执行本单位工作任务所开发的计算机软件作品。《计算机软件保护条例》第十三条做出了明确的规定，即公民在单位任职期间所开发的软件，如果是执行本职工作的结果，即针对本职工作中明确指定的开发目标所开发的，或者是从事本职工作活动所预见的结果或自然的结果；则该软件的著作权属于该单位。根据《计算机软件保护条例》规定，可以得出这样的结论：当公民作为某单位的雇员时，如其开发的软件属于执行本职工作的结果，该软件著作权应当归单位享有。若开发的软件不是执行本职工作的结果，其著作权就不属单位享有。如果该雇员主要使用了单位的设备，按照《计算机软件保护条例》第十三条第三款的规定，不能属于该雇员个人享有。

对于公民在非职务期间创作的计算机程序，其著作权属于某项软件作品的开发单位，还是从事直接创作开发软件作品的个人，可按照《计算机软件保护条例》第十三条规定的三条标准确定。

（1）所开发的软件作品不是执行其本职工作的结果。任何受雇于一个单位的人员，都会被安排在一定的工作岗位和分派相应的工作任务，完成分派的工作任务就是他的本职工作。本职工作的直接成果也就是其工作任务的不断完成。当然，具体工作成果又会产生许多效益、产生范围更广的结果。但是，该条标准指的是雇员本职工作最直接的成果。若雇员开发创作的软件不是执行本职工作的结果，则构成非职务计算机软件著作权的条件之一。

（2）开发的软件作品与开发者在单位中从事的工作内容无直接联系。如果该雇员在单位担任软件开发工作，引起争议的软件作品不能与其本职工作中明确指定的开发目标有关，软件作品的内容也不能与其本职工作所开发的软件的功能、逻辑思维和重要数据有关。雇员所开发的软件作品与其本职工作没有直接的关系，则构成非职务计算机软件著作权的第二个条件。

（3）开发的软件作品未使用单位的物质技术条件。开发创作软件作品所使用的物质技术条件，即开发软件作品所必须的设备、数据、资金和其他软件开发环境，不属于雇员所在的单位所有。没有使用受雇单位的任何物质技术条件构成非职务软件著作权的第三个条件。

雇员进行本职工作以外的软件开发创作，必须同时符合上述三个条件，才能算是非职务软件作品，雇员个人才享有软件著作权。常有软件开发符合前两个条件，但使用了单位的技术情报资料、计算机设备等物质技术条件的情况。处理此种情况较好的方法是对该软件著作权的归属应当由单位和雇员双方协商确定，如对于公民在非职务期间利用单位物质条件创作的与单位业务范围无关的计算机程序，其著作权属于创作程序的作者，但作者许可第三人使用软件时，应当支付单位合理的物质条件使用费，如计算机机时费等。若通过协商不能解决，按上述三条标准作出界定。

2）合作开发软件著作权的归属

合作开发软件是指两个或两个以上公民、法人或其他组织订立协议，共同参加某项计算机软件的开发并分享软件著作权的形式。《计算机软件保护条例》第十条规定：“由两个以上的自然人、法人或者其他组织合作开发的软件，其著作权的归属由合作开发者签订书面合同约定。无书面合同或者合同未作明确约定，合作开发的软件可以分割使用的，开发者对各自开发的部分可以单独享有著作权；但是，行使著作权时，不得扩展到合作开发的软件整体的著作权。合作开发的软件不能分割使用的，其著作权由合作开发者共同享有，通过协商一致行使；如不能协商一致，又无正当理由，任何一方不得阻止他方行使除转让权以外的其他权利，但是所得收益应合理分配给所有合作开发者。”根据此规定，对合作开发软件著作权的归属应掌握以下4点。

（1）由两个以上的单位、公民共同开发完成的软件属于合作开发的软件。对于合作开发的软件，其著作权的归属一般是由各合作开发者共同享有；但如果有软件著作权的协议，则按照协议确定软件著作权的归属。

（2）由于合作开发软件著作权是由两个以上单位或者个人共同享有，因而为了避免在软件著作权的行使中产生纠纷，规定“合作开发的软件，其著作权的归属由合作开发者签订书面合同约定。”。

（3）对于合作开发的软件著作权按以下规定执行：“无书面合同或者合同未作明确约定，合作开发的软件可以分割使用的，开发者对各自开发的部分可以单独享有著作权；但是，行使著作权时，不得扩展到合作开发的软件整体的著作权。合作开发的软件不能分割使用的，其著作权由合作开发者共同享有，通过协商一致行使；如不能协商一致，又无正当理由，任何一方不得阻止他方行使除转让权以外的其他权利，但是所得收益应合理分配给所有合作开发者。”

（4）合作开发者对于软件著作权中的转让权不得单独行使。因为转让权的行使将涉及软件著作权权利主体的改变，所以软件的合作开发者在行使转让权时，必须与各合作开发者协商，在征得同意的情况下方能行使该项专有权利。

3）委托开发的软件著作权归属

委托开发的软件作品属于著作权法规定的委托软件作品。委托开发软件作品著作权关系的建立，一般由委托方与受委托方订立合同而成立。委托开发软件作品关系中，委托方的责任主要是提供资金、设备等物质条件，并不直接参与开发软件作品的创作开发活动。受托方的主要责任是根据委托合同规定的目标开发出符合条件的软件。关于委托开发软件著作权的归属，《计算机软件保护条例》第十一条规定：“接受他人委托开发的软件，其著作权的归属由委托者与受委托者签订书面合同约定；无书面合同或者合同未作明确约定的，其著作权由受托人享有。”根据该条的规定，委托开发的软件著作权的归属按以下标准确定。

（1）委托开发软件作品需根据委托方的要求，由委托方与受托方以合同确定的权利和义务的关系而进行开发的软件。因此，软件作品著作权归属应当作为合同的重要条款予以明确约定。对于当事人已经在合同中约定软件著作权归属关系的，如事后发生纠纷，软件著作权的归属仍应当根据委托开发软件的合同来确定。

（2）若在委托开发软件活动中，委托者与受委托者没有签订书面协议，或者在协议中未对软件著作权归属作出明确的约定，则软件著作权属于受委托者，即属于实际完成软件的开发者。

4）接受任务开发的软件著作权归属

根据社会经济发展的需要，对于一些涉及国家基础项目或者重点设施的计算机软件，往往采取由政府有关部门或上级单位下达任务方式，完成软件的开发工作。对于下达任务开发的软件，其著作权的归属关系，《计算机软件保护条例》第十二条作出了明确的规定：“由国家机关下达任务开发的软件，著作权的归属与行使由项目任务书或者合同规定；项目任务书或者合同中未作明确规定，软件著作权由接受任务的法人或者其他组织享有。”根据该规定，国家或上级下达任务开发的软件著作权归属应按以下两条标准确定。

（1）下达任务开发的软件著作权的归属关系，首先应以项目任务书的规定或者双方的合同约定为准。

（2）下达任务的项目任务书或者双方订立的合同中未对软件著作权归属作出明确的规定或者约定的，其软件著作权属于接受并实际完成开发软件任务的单位。

5）计算机软件著作权主体变更后软件著作权的归属

计算机软件著作权的主体，因一定的法律事实而发生变更，如作为软件著作权人的公民的死亡，单位的变更，软件著作权的转让以及人民法院对软件著作权的归属作出裁判等。软件著作权主体的变更必然引起软件著作权归属的变化。对此，《计算机软件保护条例》也作了一些规定。因计算机软件主体变更引起的权属变化有以下几种。

（1）公民继承的软件权利归属。《计算机软件保护条例》第十五条规定：“在软件著作权的保护期内，软件著作权的继承者可根据《中华人民共和国继承法》的有关规定，继承本条例第八条项规定的除署名权以外的其他权利。”按照该条的规定，软件著作权的合法继承人依法享有继承被继承人享有的软件著作权的使用权、使用许可权和获得报酬权等权利。继承权的取得、继承顺序等均按照继承法的规定进行。

（2）单位变更后软件权利归属。《计算机软件保护条例》第十五条规定：“软件著作权属于法人或其他组织的，法人或其他组织变更、终止后，其著作权在本条例规定的保护期内由承受其权利义务的法人或其他组织享有。”按照该条的规定，作为软件著作权人的单位发生变更（如单位的合并、破产等），而其享有的软件著作权仍处在法定的保护期限内，可以由合法的权利承受单位享有原始著作权人所享有的各项权利。依法承受软件著作权的单位，成为该软件的后续著作权人，可在法定的条件下行使所承受的各项专有权利。一般认为，“各项权利”包括署名权等著作人身权在内的全部权利。

（3）权利转让后软件著作权归属。《计算机软件保护条例》第二十条规定：“转让软件著作权的，当事人应当订立书面合同。”计算机软件著作财产权按照该条的规定发生转让后，必然引起著作权主体的变化，产生新的软件著作权归属关系。软件权利的转让应当根据我国有关法规以签订、执行书面合同的方式进行。软件权利的受让者可依法行使其享有的权利。

（4）司法判决、裁定引起的软件著作权归属问题。计算机软件著作权是公民、法人和其他组织享有的一项重要的民事权利。因而在民事权利行使、流转的过程中，难免发生涉及计算机软件著作权作为标的物的民事、经济关系，也难免发生争议和纠纷。争议和纠纷发生后由人民法院的民事判决、裁定而产生软件著作权主体的变更，引起软件著作权归属问题。因司法裁判引起软件著作权的归属问题主要有4类：一类是由人民法院对著作权归属纠纷中权利的最终归属作出司法裁判，从而变更了计算机软件著作权原有归属；第二类是计算机软件的著作权人为民事法律关系中的债务人（债务形成的原因可能多种多样，如合同关系或者损害赔偿关系等），人民法院将其软件著作财产权判归债权人享有抵债；第三类是人民法院作出民事判决判令软件著作权人履行民事给付义务，在判决生效后执行程序中，其无其他财产可供执行，将软件著作财产权执行给对方折抵债务；第四类是根据破产法的规定，软件著作权人被破产还债，软件著作财产权作为法律规定的破产财产构成的“其他财产权利”，作为破产财产由人民法院判决分配。

（5）保护期限届满权利丧失。软件著作权的法定保护期限可以确定计算机软件的主体能否依法变更。如果软件著作权已过保护期，该软件进入公有领域，便丧失了专有权，也就没有必要改变权利主体了。根据软件保护条例的规定，计算机软件著作权主体变更必须在该软件著作权的保护期限内进行，转让活动的发生不改变该软件著作权的保护期。这也就是说，转让活动也不能延长该软件著作权的保护期限。

7．计算机软件著作权侵权的鉴别

侵犯计算机软件著作权的违法行为的鉴别，主要依靠保护知识产权的相关法律来判断。违反著作权、计算机软件保护条例等法律禁止的行为，便是侵犯计算机著作权的违法行为，这是鉴别违法行为的本质原则。对于法律规定不禁止，也不违反相关法律基本原则的行为，不认为是违法行为。在法律无明文具体条款规定的情况下，违背著作权法和计算机软件保护条例等法律的基本原则，以及社会主义公共生活准则和社会善良风俗的行为，也应该视为违法行为。在一般情况下，损害他人著作财产权或人身权的行为，总是违法行为。

1）计算机软件著作权侵权行为

根据《计算机软件保护条例》第二十三条的规定，凡是行为人主观上具有故意或者过失对著作权法和计算机软件保护条例保护的计算机软件人身权和财产权实施侵害行为的，都构成计算机软件的侵权行为。该条规定的侵犯计算机软件著作权的情况，是认定软件著作权侵权行为的法律依据。计算机软件侵权行为主要有以下几种。

（1）未经软件著作权人的同意而发表或者登记其软件作品。软件著作人享有对软件作品公开发表权，未经允许著作权人以外的任何其他人都无权擅自发表特定的软件作品。如果实施这种行为，就构成侵犯著作权人的发表权。

（2）将他人开发的软件当作自己的作品发表或者登记。此种行为主要侵犯了软件著作权的开发者身份权和署名权。侵权行为人欺世盗名，剽窃软件开发者的劳动成果，将他人开发的软件作品假冒为自己的作品而署名发表。只要行为人实施了这种行为，不管其发表该作品是否经过软件著作人的同意，都构成侵权。

（3）未经合作者的同意将与他人合作开发的软件当作自己独立完成的作品发表或者登记。此种侵权行为发生在软件作品的合作开发者之间。作为合作开发的软件，软件作品的开发者身份为全体开发者，软件作品的发表权也应由全体开发者共同行使。如果未经其他开发者同意，又将合作开发的软件当作自己的独创作品发表，即构成本条规定的侵权行为。

（4）在他人开发的软件上署名或者更改他人开发的软件上的署名。这种行为是指在他人开发的软件作品上添加自己的署名，或者替代软件开发者署名，以及将软件作品上开发者的署名进行更改的行为。这种行为侵犯了软件著作人的开发者身份权及署名权。此种行为与第（2）条规定行为的区别主要是对已发表的软件作品实施的行为。

（5）未经软件著作权人或者其合法受让者的许可，修改、翻译其软件作品。此种行为是侵犯了著作权人或其合法受让者的使用权中的修改权、翻译权。对不同版本计算机软件，新版本往往是旧版本的提高和改善。这种提高和改善实质上是对原软件作品的修改、演绎。此种行为应征得软件作品原版本著作权人的同意，否则构成侵权。如果征得软件作品著作人的同意，修改和改善新增加的部分，创作者应享有著作权。

（6）未经软件著作权人或其合法受让者的许可，复制或部分复制其软件作品。此种行为侵犯了著作权人或其合法受让者的使用权中的复制权。计算机软件的复制权是计算机软件最重要的著作财产权，也是通常计算机软件侵权行为的对象。这是由于软件载体价格相对低廉，复制软件简单易行，效率极高，而销售非法复制的软件即可获得高额利润。因此，复制是常见的侵权行为，是防止和打击的主要对象。当软件著作权经当事人的约定合法转让给转让者以后，软件开发者未经允许不得复制该软件，否则也构成本条规定的侵权行为。

（7）未经软件著作权人及其合法受让者同意，向公众发行、出租其软件的复制品。此种行为侵犯了著作权人或其合法受让者的发行权与出租权。

（8）未经软件著作权人或其合法受让者同意，向任何第三方办理软件权利许可或转让事宜，这种行为侵犯了软件著作权人或其合法受让者的使用许可权和转让权。

（9）未经软件著作权人及其合法受让者同意，通过信息网络传播著作权人的软件。这种行为侵犯了软件著作权人或其合法受让者的信息网络传播权。

（10）侵犯计算机软件著作权存在着共同侵权行为。两人以上共同实施《计算机软件保护条例》第二十三条和第二十四条规定的侵权行为，构成共同侵权行为。对行为人并没有实施《计算机软件保护条例》第二十三条和第二十四条规定的行为，但实施了向侵权行为人进行侵权活动提供设备、场所或解密软件，或者为侵权复制品提供仓储、运输条件等行为，构成共同侵权应当在行为人之间具有共同故意或过失行为。其构成的要件有两个，一是行为人的过错是共同的，而不论行为人的行为在整个侵权行为过程中所起的作用如何；二是行为人主观上要有故意或过失的过错。如果这两个要件具备，各个行为人实施的侵权行为虽然各不相同，也同样构成共同侵权。两个要件如果缺乏一个，不构成共同的侵权，或者是不构成任何侵权。

2）不构成计算机软件侵权的合理使用行为

我国《计算机软件保护条例》第八条第四项和第十六条规定，获得使用权或使用许可权（视合同条款）后，可以对软件进行复制而无须通知著作权人，也不构成侵权。对于合法持有软件复制品的单位、公民在不经著作权人同意的情况下，也享有复制与修改权。合法持有软件复制品的单位、公民，在不经软件著作权人同意的情况下，可以根据自己使用的需要将软件装入计算机，为了存档也可以制作复制品，为了把软件用于实际的计算机环境或者改进其功能时也可以进行必要的修改，但是复制品和修改后的文本不能以任何方式提供给他人。超过以上权利，即视为侵权行为。区分合理使用与非合理使用的判别标准一般有如下几个。

（1）软件作品是否合法取得。这是合理使用的基础。

（2）使用目的是非商业营业性。如果使用的目的是为商业性营利，就不属合理使用的范围。

（3）合理使用一般为少量的使用。所谓少量的界限，根据其使用的目的以行业惯例和人们一般常识所综合确定。超过通常被认为的少量界限，即可被认为不属合理使用。

我国《计算机软件保护条例》第十七条规定：“为了学习和研究软件内含的设计思想和原理，通过安装、显示、传输或者存储软件的方式使用软件的，可以不经软件著作权人许可，不向其支付报酬。”

3）计算机著作权软件侵权的识别

计算机软件明显区别于其他著作权法保护的客体，它具有以下特点。

（1）技术性。计算机软件的技术性是指其创作开发的高技术性。具有一定规模的软件的创作开发，一般开发难度大、周期长、投资高，需要良好组织，严密管理且各方面人员配合协作，借助现代化高技术和高科技工具生产创作。

（2）依赖性。计算机程序的依赖性是指人们对其的感知依赖于计算机的特性。著作权保护的其他作品一般都可以依赖人的感觉器官所直接感知。但计算机程序则不能被人们所直接感知，它的内容只能依赖计算机等专用设备才能被充分表现出来，才能被人们所感知。

（3）多样性。计算机程序的多样性是指计算机程序表达的多样性。计算机程序的表达较著作权法保护的其他对象特殊，其既能以源代码表达，还可以以目标代码和微码等表达，表达形式多样。计算机程序表达的存储媒体也多种多样，同一种程序分别可以被存储在纸张、磁盘、磁带、光盘和集成电路上等。计算机程序的载体大多数精巧灵便。此外，计算机程序的内容与表达难以严格区别界定。

（4）运行性。计算机程序的运行性是指计算机程序功能的运行性。计算机程序不同于一般的文字作品，它主要的功能在于使用。也就是说，计算机程序的功能只能通过对程序的使用、运行才能充分体现出来。计算机程序采用数字化形式存储、转换，复制品与原作品一般无明显区别。

根据计算机软件的特点，对计算机软件侵权行为的识别可以通过将发生争议的某一计算机程序与比照物（权利明确的正版计算机程序）进行对比和鉴别，从两个软件的相似性或是否完全相同来判断，做出侵权认定。软件作品常常表现为计算机程序的不唯一性，两个运行结果相同的计算机程序，或者两个计算机软件的源代码程序不相似或不完全相似，前者不一定构成侵权，而后者不一定不构成侵权。

8．软件著作权侵权的法律责任

当侵权人侵害他人的著作权、财产权或著作人身权，造成权利人财产上的或非财产的损失，侵权人不履行赔偿义务，法律即强制侵权人承担赔偿损失的民事责任。

1）民事责任

侵犯计算机著作权以及有关权益的民事责任是指公民、法人或其他组织因侵犯著作权发生的后果依法应承担的法律责任。我国《计算机软件保护条例》第二十三条规定了侵犯计算机著作权的民事责任，即侵犯著作权或者与著作权有关的权利的，侵权人应当按照权利人的实际损失给予赔偿；实际损失难以计算的，可以按照侵权人的违法所得给予赔偿。赔偿数额还应当包括权利人为制止侵权行为所支付的合理开支。权利人的实际损失或者侵权人的违法所得不能确定的，由人民法院根据侵权行为的情节，判决给予五十万元以下的赔偿。有下列侵权行为的，应当根据情况承担停止侵害、消除影响、公开赔礼道歉或赔偿损失等民事责任。

（1）未经软件著作权人许可发表或者登记其软件的。

（2）将他人软件当作自己的软件发表或者登记的。

（3）未经合作者许可，将与他人合作开发的软件当作自己单独完成的作品发表或者登记的。

（4）在他人软件上署名或者涂改他人软件上的署名的。

（5）未经软件著作权人许可，修改、翻译其软件的。

（6）其他侵犯软件著作权的行为。

2）行政责任

我国《计算机软件保护条例》第二十四条规定了相应的行政责任，即对侵犯软件著作权行为，著作权行政管理部门应当责令停止违法行为，没收非法所得，没收、销毁侵权复制品，并可处以每件一百元或者货值金额二至五倍的罚款。有下列侵权行为的，应当根据情况承担停止侵害、消除影响、公开赔礼道歉或赔偿损失等行政责任。

（1）复制或者部分复制著作权人软件的。

（2）向公众发行、出租、通过信息网络传播著作权人软件的。

（3）故意避开或者破坏著作权人为保护其软件而采取的技术措施的。

（4）故意删除或者改变软件权利管理电子信息的。

（5）许可他人行使或者转让著作权人的软件著作权的。

3）刑事责任

侵权行为触犯刑律的，侵权者应当承担刑事责任。我国《刑法》第二百一十七条、第二百一十八条和第二百二十条规定，构成侵犯著作权罪、销售侵权复制品罪的，由司法机关追究刑事责任。

11.2.3　计算机软件的商业秘密权

关于商业秘密的法律保护，各国采取不同的法律，有的制定单行法，有的规定在反不正当竞争法中，有的适用一般侵权行为法。我国反不正当竞争法规定了商业秘密的保护问题。

1．商业秘密

1）商业秘密的定义

《反不正当竞争》中商业秘密定义为“指不为公众所知悉的、能为权利人带来经济利益、具有实用性并经权利人采取保密措施的技术信息和经营信息”。经营秘密和技术秘密是商业秘密的基本内容。经营秘密，即未公开的经营信息，是指与生产经营销售活动有关的经营方法、管理方法、产销策略、货源情报、客户名单、标底和标书内容等专有知识。技术秘密，即未公开的技术信息，是指与产品生产和制造有关的技术诀窍、生产方案、工艺流程、设计图纸、化学配方和技术情报等专有知识。

2）商业秘密的构成条件

商业秘密的构成条件是：商业秘密必须具有未公开性，即不为公众所知悉；商业秘密必须具有实用性，即能为权利人带来经济效益；商业秘密必须具有保密性，即采取了保密措施。

3）商业秘密权

商业秘密是一种无形的信息财产。与有形财产相区别，商业秘密不占据空间，不易被权利人所控制，不发生有形损耗，其权利是一种无形财产权。商业秘密的权利人与有形财产所有权人一样，依法享有占有、使用和收益的权利，即有权对商业秘密进行控制与管理，防止他人采取不正当手段获取与使用；有权依法使用自己的商业秘密，而不受他人干涉；有权通过自己使用或者许可他人使用以至转让所有权，从而取得相应的经济利益；有权处理自己的商业秘密，包括放弃占有、无偿公开、赠与或转让等。

4）商业秘密的丧失

一项商业秘密受到法律保护的依据，是必须具备上述构成商业秘密的三个条件，当缺少上述三个条件之一就会造成商业秘密丧失保护。

2．计算机软件与商业秘密

《反不正当竞争》保护计算机软件，是以计算机软件中是否包含着“商业秘密”为必要条件的。而计算机软件是人类知识、智慧、经验和创造性劳动的成果，本身就具有商业秘密的特征，即包含着技术秘密和经营秘密。即使是软件尚未开发完成，在软件开发中所形成的知识内容也可构成商业秘密。

1）计算机软件商业秘密的侵权

侵犯商业秘密，是指行为人（负有约定的保密义务的合同当事人；实施侵权行为的第三人；侵犯本单位商业秘密的行为人）未经权利人（商业秘密的合法控制人）的许可，以非法手段（包括直接从权利人那里窃取商业秘密并加以公开或使用；通过第三人窃取权利人的商业秘密并加以公开或使用）获取计算机软件商业秘密并加以公开或使用的行为。根据我国《反不正当竞争法》第十条的规定，侵犯计算机软件商业秘密的具体表现形式主要如下。

（1）以盗窃、利诱、胁迫或其他不正当手段获取权利人的计算机软件商业秘密。盗窃商业秘密，包括单位内部人员盗窃、外部人员盗窃、内外勾结盗窃等手段；以利诱手段获取商业秘密，通常指行为人向掌握商业秘密的人员提供财物或其他优惠条件，诱使其向行为人提供商业秘密；以胁迫手段获取商业秘密，是指行为人采取威胁、强迫手段，使他人在受强制的情况下提供商业秘密；以其他不正当手段获取商业秘密。

（2）披露、使用或允许他人使用以不正当手段获取的计算机软件商业秘密。披露是指将权利人的商业秘密向第三人透露或向不特定的其他人公开，使其失去秘密价值；使用或允许他人使用是指非法使用他人商业秘密的具体情形。以非法手段获取商业秘密的行为人，如果将该秘密再行披露或使用，即构成双重的侵权；倘若第三人从侵权人那里获悉了商业秘密而将秘密披露或使用，同样构成侵权。

（3）违反约定或违反权利人有关保守商业秘密的要求，披露、使用或允许他人使用其所掌握的计算机软件商业秘密。合法掌握计算机软件商业秘密的人，可能是与权利人有合同关系的对方当事人，也可能是权利人的单位工作人员或其他知情人，他们违反合同约定或单位规定的保密义务，将其所掌握的商业秘密擅自公开，或自己使用，或许可他人使用，即构成侵犯商业秘密。

（4）第三人在明知或应知前述违法行为的情况下，仍然从侵权人那里获取、使用或披露他人的计算机软件商业秘密。这是一种间接的侵权行为。

2）计算机软件商业秘密侵权的法律责任

根据我国《反不正当竞争法》和《刑法》的规定，计算机软件商业秘密的侵权者将承担行政责任、民事责任以及刑事责任。

（1）侵权者的行政责任。我国《反不正当竞争法》第二十五条规定了相应的行政责任，即对侵犯商业秘密的行为，监督检查部门应当责令停止违法行为，而后可以根据侵权的情节依法处以1万元以上20万元以下的罚款。

（2）侵权者的民事责任。计算机软件商业秘密的侵权者的侵权行为对权利人的经营造成经济上的损失时，侵权者应当承担经济损害赔偿的民事责任。我国《反不正当竞争法》第二十条规定了侵犯商业秘密的民事责任，即经营者违反该法规定，给被侵害的经营者造成损害的，应当承担损害赔偿责任。被侵害的经营者的合法权益受到损害的，可以向人民法院提起诉讼。

（3）侵权者的刑事责任。侵权者以盗窃、利诱、胁迫或其他不正当手段获取权利人的计算机软件商业秘密；披露、使用或允许他人使用以不正当手段获取的计算机软件商业秘密；违反约定或违反权利人有关保守商业秘密的要求，披露、使用或允许他人使用其所掌握的计算机软件商业秘密，其侵权行为对权利人造成重大损害的，侵权者应当承担刑事责任。我国《刑法》第二百一十九条规定了侵犯商业秘密罪，即实施侵犯商业秘密行为，给商业秘密的权利人造成重大损失的，处3年以下有期徒刑或者拘役，并处或者单处罚金；造成特别严重后果的，处3年以上7年以下有期徒刑，并处罚金。

11.2.4　专利权概述

1．专利权的保护对象与特征

发明创造是产生专利权的基础。发明创造是指发明、实用新型和外观设计，是我国专利法主要保护的对象。我国《专利法实施细则》第2条第1款规定：“专利法所称的发明，是指对产品、方法或者其改进所提出的技术方案”。实用新型（也称小发明）则因国而异，我国《专利法实施细则》第2条第2款规定：“实用新型是指对产品的形状、构造或者其组合所提出的新的技术方案”。外观设计是指对产品的形状、图案、色彩或者它们的结合所做出的富有美感的并适于工业应用的新设计。

专利的发明创造是无形的智力创造性成果，不像有形财产那样直观可见，必须经专利主管机关依照法定程序审查确定，在未经审批以前，任何一项发明创造都不得成为专利。

下列各项属于专利法不适用的对象，因此不授予专利权。

（1）违反国家法律、社会公德或者妨害公共利益的发明创造。

（2）科学发现，即人们通过自己的智力劳动对客观世界已经存在的但未揭示出来的规律、性质和现象等的认识。

（3）智力活动的规则和方法，即人们进行推理、分析、判断、运算、处理、记忆等思维活动的规则和方法。

（4）病的诊断和治疗方法。即以活的人或者动物为实施对象，并以防病治病为目的，是医护人员的经验体现，而且因被诊断和治疗的对象不同而有区别，不能在工业上应用，不具有实用性。

（5）动物和植物品种，但是动物植物品种的生产方法，可以依照专利法规定授予专利权。

（6）用原子核变换方法获得的物质，即用核裂变或核聚变方法获得的单质或化合物。

2．授予专利权的条件

授予专利权的条件是指一项发明创造获得专利权应当具备的实质性条件。一项发明或者实用新型获得专利权的实质条件为新颖性、创造性和实用性。

（1）新颖性。新颖性是指在申请日以前没有同样的发明或实用新型在国内外出版物公开发表过，在国内公开使用过或以其他方式为公众所知，也没有同样的发明或实用新型由他人向专利局提出过申请并且记载在申请日以后公布的专利申请文件中。在某些特殊情况下，尽管申请专利的发明或者实用新型在申请日或者优先权日前公开，但在一定的期限内提出专利申请的，仍然具有新颖性。我国专利法规定，申请专利的发明创造在申请日以前6个月内，有下列情况之一的，不丧失新颖性。

①　在中国政府主办或者承认的国际展览会上首次展出的。

②　在规定的学术会议或者技术会议上首次发表的。

③　他人未经申请人同意而泄露其内容的。

（2）创造性。创造性是指同申请日以前已有的技术相比，该发明有突出的实质性特点和显著的进步，该实用新型有实质性特点和进步。例如，申请专利的发明解决了人们渴望解决但一直没有解决的技术难题；申请专利的发明克服了技术偏见；申请专利的发明取得了意想不到的技术效果；申请专利的发明在商业上获得成功。一项发明专利是否具有创造性，前提是该项发明具备新颖性。

（3）实用性。实用性是指该发明或者实用新型能够制造或者使用，并且能够产生积极的效果，即不造成环境污染、能源或者资源的严重浪费，损害人体健康。如果申请专利的发明或者实用新型缺乏技术手段；申请专利的技术方案违背自然规律；利用独一无二自然条件所完成的技术方案，则不具有实用性。

我国专利法规定，外观设计获得专利权的实质条件为新颖性和美观性。新颖性是指申请专利的外观设计与其申请日以前已经在国内外出版物上公开发表的外观设计不相同或者不相近似；与其申请日前已在国内公开使用过的外观设计不相同或者不相近似。美观性是指外观设计被使用在产品上时能使人产生一种美感，增加产品对消费者的吸引力。

3．专利的申请

1）专利申请权

公民、法人或者其他组织依据法律规定或者合同约定享有的就发明创造向专利局提出专利申请的权利（专利申请权）。一项发明创造产生的专利申请权归谁所有，主要有由法律直接规定的情况和依合同约定的情况。专利申请权可以转让，不论专利申请权在哪一个时间段转让，原专利申请人便因此丧失专利申请权，由受让人获得相应的专利申请权。专利申请权可以被继承或赠与。专利申请人死亡后，其依法享有的专利申请权可以作为遗产，由其合法继承人继承。

2）专利申请人

专利申请人是指对某项发明创造依法律规定或者合同约定享有专利申请权的公民、法人或者其他组织。专利申请人包括职务发明创造的单位；非职务发明创造的专利申请人为完成发明创造的发明人或者设计人；共同发明创造的专利申请人是共同发明人或者设计人，或者其所属单位；委托发明创造的专利申请人为合同约定的人；受让人。

3）专利申请的原则

专利申请人及其代理人在办理各种手续时都应当采用书面形式。一份专利申请文件只能就一项发明创造提出专利申请，即“一份申请一项发明”原则。两个或者两个以上的人分别就同样的发明创造申请专利的，专利权授给最先申请人。

4）专利申请文件

发明或者实用新型申请文件包括请求书、说明书、说明书摘要和权利要求书。外观设计专利申请文件包括请求书、图片或照片。

5）专利申请日

专利申请日（也称关键日）是专利局或者专利局指定的专利申请受理代办处收到完整专利申请文件的日期。如果申请文件是邮寄的，以寄出的邮戳日为申请日。

6）专利申请的审批

专利局收到发明专利申请后，一个必要程序是初步审查，经初步审查认为符合本法要求的，自申请日起满18个月，即行公布（公布申请），专利局可根据申请人的请求，早日公布其申请。自申请日起三年内，专利局可以根据申请人随时提出的请求，对其申请进行实质审查。实质审查是专利局对申请专利的发明的新颖性、创造性和实用性等依法进行审查的法定程序。

我国专利法规定：“实用新型和外观设计专利申请经初步审查没有发现驳回理由的，专利局应当做出授予实用新型专利权或者外观设计专利权的决定，发给相应的专利证书，并予以登记和公布”。由此规定可知，对实用新型和外观设计专利申请只进行初步审查，不进行实质审查。

7）申请权的丧失与恢复

专利法及其实施细则有许多条款规定，如果申请人在法定期间或者专利局所指定的期限内未办理相应的手续或者没有提交有关文件，其申请就被视为撤回或者丧失提出某项请求的权利，或者导致有关权利终止后果。因耽误期限而丧失权利之后，可以在自障碍消除后2个月内，最迟自法定期限或者指定期限届满后2年内或者自收到专利局通知之日起2个月内，请求恢复其权利。

4．专利权行使

1）专利权的归属

根据《中华人民共和国专利法》的规定，执行本单位的任务或者主要是利用本单位的物质条件所完成的职务发明创造，申请专利的权利属于该单位。申请被批准后，专利权归该单位持有（单位为专利权人）。执行本单位的任务所完成的职务发明创造是指：

（1）在本职工作中做出的发明创造。

（2）履行本单位交付的本职工作之外的任务所做出的发明创造。

（3）工作变动（退职、退休或者调离）后短期内做出的，与其在原单位承担的本职工作或者原单位分配的任务有关的发明创造。

本单位的物质技术条件包括本单位的资金、设备、零部件、原材料或者不对外公开的技术资料等。

非职务发明创造，申请专利的权利属于发明人或者设计人；在中国境内的外资企业和中外合资经营企业的工作人员完成的职务发明创造，申请专利的权利属于该企业，申请被批准后，专利权归申请的企业或者个人所有；两个以上单位协作或者一个单位接受其他单位委托的研究、设计任务所完成的发明创造，除另有协议的以外，申请专利的权利属于完成或者共同完成的单位，申请被批准后，专利权归申请的单位所有或者持有。

2）专利权人的权利

专利权是一种具有财产权属性的独占权以及由其衍生出来和相应处理权。专利权人的权利包括独占实施权、转让权、实施许可权、放弃权和标记权等。专利权人有缴纳专利年费（也称专利维持费）和实际实施已获专利的发明创造两项基本义务。

专利权人通过专利实施许可合同将其依法取得的对某项发明创造的实施权转移给非专利权人行使。任何单位或者个人实施他人专利的，除《中华人民共和国专利法》第十四条规定的以外，都必须与专利权人订立书面实施许可合同，向专利权人支付专利使用费。被许可人无权允许合同规定以外的任何单位或者个人实施该专利。专利实施许可的种类包括独占许可、独家许可、普通许可和部分许可。

5．专利权的限制

根据《中华人民共和国专利法》的规定，发明专利权的保护期限为自申请日起20年；实用新型专利权和外观设计专利权的保护期限为自申请日起10年。发明创造专利权的法律效力所及的范围如下。

（1）发明或者实用新型专利权的保护范围以其权利要求的内容为准，说明书及附图可以用于解释权利要求。

（2）外观设计专利权的保护范围以表示在图片或者照片中的该外观设计专利产品为准。

公告授予专利权后，任何单位或个人认为该专利权的授予不符合专利法规定条件的，可以向专利复查委员会提出宣告该专利权无效的请求。专利复审委员会对这种请求进行审查，做出宣告专利权无效或维持专利权的决定。我国专利法规定，提出无效宣告请求的时间（启动无效宣告程序的时间）始于“自专利局公告授予专利权之日起”。

专利权因某种法律事实的发生而导致其效力消灭的情形称为专利权终止。导致专利权终止的法律事实如下。

（1）保护期限届满。

（2）在专利权保护期限届满前，专利权人以书面形式向专利局声明放弃专利权。

（3）在专利权的保护期限内，专利权人没有按照法律的规定交年费。专利权终止日应为上一年度期满日。

专利法允许第三人在某些特殊情况下，可以不经专利权人许可而实施其专利，且其实施行为并不构成侵权的一种法律制度。专利权限制的种类包括强制许可、不视为侵犯专利权的行为和国家计划许可。

6．专利侵权行为

专利侵权行为是指在专利权的有效期限内，任何单位或者个人在未经专利权人许可，也没有其他法定事由的情况下，擅自以营利为目的实施专利的行为。专利侵权行为主要包括如下方面。

（1）为生产经营目的制造、使用、销售其专利产品，或者使用其专利方法以及使用、销售依照该专利方法直接获得的产品。

（2）为生产经营目的制造、销售其外观设计专利产品。

（3）进口依照其专利方法直接获得的产品。

（4）产品的包装上标明专利标记和专利号。

（5）用非专利产品冒充专利产品的或者用非专利方法冒充专利方法等。

对未经专利权人许可，实施其专利的侵权行为，专利权人或者利害关系人可以请求专利管理机关处理。在专利侵权纠纷发生后，专利权人或者利害关系人既可以请求专利管理机关处理，又可以请求人民法院审理。侵犯专利权的诉讼时效为2年，自专利权人或者利害关系人知道或者应当知道侵权行为之日起计算。如果诉讼时效期限届满，专利权人或者利害关系人不能再请求人民法院保护，同时也不能再向专利管理机关请求保护。

11.2.5　企业知识产权的保护

从事计算机软件科研开发、技术贸易的高新技术企业通常需要不断开发和积累本企业的技术成果，以保持企业技术的优势，需要保护好企业拥有的知识产权，以能够利用本企业的知识产权为自身带来经济效益，否则将会影响企业的生存和发展。高新技术企业大都是以知识创新开发产品，当知识产品进入市场后，则完全依赖于对其知识产权的保护，如果没有保护或保护不好，将影响企业大规模的实施知识开发投资，企业也不可能更好地生存与发展。知识产权是一种无形的产权，是企业的重要财富，应当把保护软件知识产权作为现代企业制度的一项基本内容。

1．企业知识产权的管理

企业往往会面临软件知识产权的权利归属、软件人才的管理、软件技术的保密以及软件成果的有效利用等一系列问题，这些问题的解决不可能仅仅依靠企业的某些领导的行政干预，更有效的途径是依靠企业自身的管理制度。软件的权利归属不清、人才外流、管理不善而导致软件知识产权泄密和流失的问题，软件的研发及经营活动中不规范的问题，只有通过企业自身的知识产权管理制度来有效地控制和管理，以保护企业有关知识产权。企业知识产权管理制度，不仅应当形成激励机制，还应当建立明确的约束机制，防止发生知识产权纠纷。通过制度管理掌握主动权，以确保企业生存与发展，在技术发展中处于不败的地位。

专利权、商标权和著作权等知识产权是企业的经营资源，应建立承担企业知识产权管理的知识产权机构，有效地管理和利用知识产权。参与到软件立项、开发、销售及售后服务的每个环节，把握住企业的技术开发动向以及其他公司的技术开发动向，以知识产权的角度向开发人员、研究人员、管理人员提出建议，并为对外事务提供法律保障。

2．知识产权的保护和利用

目前，计算机技术和软件技术的知识产权法律保护已形成以《著作权法》保护为主，《著作权法》（包括《计算机软件保护条例》）、《专利法》、《商标法》、《反不正当竞争法》和《合同法》实施交叉和重叠保护为辅的趋势。例如，源程序及设计文档作为软件的表现形式用《著作权法》保护，同时作为技术秘密又受《反不正当竞争法》的保护。由于软件具有技术含量高的特点，使得对软件法律保护成为一种综合性的保护，对于企业来说，仅依靠某项法律或法规不能解决软件的所有知识产权问题。应在保护企业计算机软件成果知识产权方面实施综合性的保护，例如，在新技术的开发中重视技术秘密的管理，也应重视专利权的取得，而在命名新产品名称时，也应重视商标权的取得，以保护企业的知识产权。企业软件保护成果知识产权的一般途径如下。

（1）明确软件知识产权归属。明确知识产权是归企业还是制作、设计、开发人员所有，避免企业内部产生权属纠纷。

（2）及时对软件技术秘密采取保密措施。对企业的软件产品或成果中的技术秘密，应当及时采取保密措施，以便把握市场优势。一旦发生企业“技术秘密”被泄露的情况，则便于认定为技术秘密，依法追究泄密行为人的法律责任，保护企业的权益。

（3）依靠专利保护新技术和新产品。专利知识产权是企业重要的经营资源，依靠专利保护企业开发出的新技术或者新产品，使得企业在市场竞争中占据优势来发展自己的产业。我国采用的是先申请原则，如果有相同技术内容的专利申请，只有最先提出专利申请的企业或者个人才能获得专利权。企业的软件技术或者产品构成专利法律要件的，应当尽早办理申请专利权登记事宜，不能因企业自身的延误，造成企业软件成果新颖性的丧失，而失去申请专利的时机。

（4）软件产品进入市场之前的商标权和商业秘密保护。企业的软件产品已经冠以商品专用标识或者服务标识，要尽快完成商标或者服务标识的登记注册，保护软件产品的商标专用权。

（5）软件产品进入市场之前进行申请软件著作权登记。申请软件著作权登记以起到公示的作用。软件著作权登记只要求软件的独创性，并不以软件的技术水平作为著作权是否有效的条件，不能等到软件达到某种技术水平后再进行登记，若其他企业或者个人抢先登记，则不利于企业权益的保护。

3．建立经济约束机制，规范调整各种关系

软件企业需要按照经济合同规范各种经济活动，明确权利与义务的关系。建立企业内部以及企业与外部的各种经济约束机制。从目前存在的比较突出的问题来看，软件企业应建立以下各项合同规范。

（1）劳动关系合同。软件企业与企业职工、外聘人员之间应建立合法的劳动关系，以及应就企业的商业秘密（技术秘密和经营秘密）的保密事宜进行约定，建立劳动利益关系合同以及保守企业商业秘密的协议。一些目前不宜马上实行劳动合同的单位，也通过建立或者健全本单位的有关规章制度的方式进行过渡，以鼓励企业员工的创造性劳动，明确企业开发过程中产生的软件技术成果归属关系，以预防企业技术人员流动时造成的技术流失和技术泄密等问题。

（2）软件开发合同。软件企业与外单位合作开发、委托外单位开发软件时，应建立软件权利归属关系等事宜的协议，可按照有关规定签订软件开发合同，约定软件开发各方面尚未开发的软件享有的权利与义务的关系，以及软件技术成果开发完成后的权利归属关系和经济利益关系等。如果软件开发方在合作中发现了合同的缺陷，应及早对合同进行补充和完善。

（3）软件许可使用（或者转让）合同。软件企业在经营本企业的软件产品时，应当建立“许可证”（或是转让合同）制度，用软件许可合同（授权书）或者转让合同的方式，来明确规定软件使用权的许可（转让）方式、条件、范围和时间等事宜，避免因合同条款的约定不清楚、不明确而导致当事人之间发生扯皮等不愉快的事情，或者因合同条款无法界定而引发的软件侵权纠纷。

第12章　软件系统分析与设计

软件设计师考试结合了当前软件设计开发中的主流技术和工程应用背景，强调了主流设计技术的应用问题，除要求掌握基本的概念和理论基础知识外，还需要正确地针对实践应用中的问题，提出合理的解决方案，将一系列的设计方法与原则应用在实际系统的分析、设计和开发环节中。编者委员会总结近几年的软件设计师考试所涉及的应用性内容，其主要技术领域可归纳为以下几点。

（1）结构化分析与设计。

（2）数据库分析与设计。

（3）面向对象分析与设计。

（4）算法分析与设计。

（5）面向过程的程序设计与实现。

（6）面向对象的程序设计与实现。

12.1　结构化分析与设计

结构化分析将数据和处理作为分析对象，数据的分析结果表示了现实世界中实体的属性及其之间的相互关系，而处理的分析结果则展现了系统对数据的加工和转换。面向数据流建模是目前仍然被广泛使用的方法之一，而DFD则是面向数据流建模中的重要工具，DFD将系统建模成输入—处理—输出的模型，即流入软件的数据对象，经由处理的转换，最后以结果数据对象的形式流出软件。DFD使用分层的方式表示，第一个数据流模型有时被称为第0层DFD或者环境数据流图。从整体上表现系统，随后的数据流图将改进第0层图，并增加细节信息。

除DFD外，在进行建模时，还可结合数据字典和加工处理说明对DFD进行补充。数据字典以一种准确的和无二义的方式定义所有被加工引用的数据流和数据存储，通常包括数据流条目、数据存储条目和数据项条目。数据流条目描述DFD中数据流的组成，数据存储条目描述DFD中数据存储文件的组成，而数据项条目则描述数据流或数据存储中所使用的数据项。加工处理的说明则可采用结构化自然语言、判定表和判定树等多种形式进行详细描述，其目的在于说明加工做什么。

掌握上述的工具后，即可对问题进行结构化的分析，其实施步骤如下。

（1）确定系统边界，画出系统环境图。

（2）自顶向下，画出各层数据流图。

（3）定义数据字典。

（4）定义加工说明。

（5）将图、字典以及加工组成分析模型。

在实际使用DFD进行数据流建模时，需要注意以下原则。

（1）加工处理和数据流的正确使用。如一个加工必须既有输入又有输出；数据流只能和加工相关，即从加工流向加工、数据源流向加工或加工流向数据源。

（2）每个数据流和数据存储都要在数据字典中有定义，数据字典将包括各层数据流图中数据元素的定义。

（3）数据流图中最底层的加工处理必须有加工处理说明。

（4）父图和子图必须平衡，即父图中某加工的输入输出（数据流）和分解这个加工的子图的输入输出数据流必须完全一致。这种一致性不一定要求数据流的名称和个数一一对应，但它们在数据字典中的定义必须一致，数据流或数据项既不能多也不能少。

（5）加工处理说明和数据流图中加工处理涉及的元素保持一致。例如，在加工处理说明中，输入数据流必须说明其如何使用，输出数据流说明如何产生或选取，数据存储说明如何选取、使用或修改。

（6）一幅图中的图元个数控制在7±2以内。

DFD、数据字典和处理加工说明可以充分地描述系统的分析模型，其后需要对分析模型进行变换从而得到系统的总体设计模型。系统总体设计模型可以采用层次图、HIPO图和结构图来表达，但不论是哪一种图形工具，都反映了模块间的调用关系。

在分析模型的基础上进行设计时，主要是针对DFD进行变换从而得到模块的调用关系图，因此，需要掌握数据流的变换设计与事务设计。面向数据流的设计方法把数据流图映射成软件结构，数据流图的类型决定了映射的方法，数据流图可分为变换型数据流图和事务型数据流图。变换型数据流图具有明显的输入、变换（或称主加工）和输出；而事务型数据流图则是数据沿输入通路到达一个处理时，这个处理根据输入数据的类型在若干动作序列中选择一个来执行。变换设计的核心在于确定输入流和输出流的边界，从而孤立出变换中心；事务设计的核心在于将事务类型判断处理变换成调度模块以选择后续的输出分支模块。

经过总体设计阶段的工作，已经确定了软件的模块结构和接口描述，但每个模块仍被看作黑盒子。后续的详细设计目标是确定怎样具体地实现所要求的系统，经过详细设计，可以得出对目标系统的精确描述，从而在编码阶段可以将这个描述直接翻译成用某种程序设计语言书写的程序。因此，详细设计的结果基本上决定了最终的程序代码的质量。详细设计可以采用程序流程图、N-S图、PAD图和PDL语言等工具来表达。下面通过一个案例来说明如何应用结构化分析、总体设计与详细设计技术。

12.1.1　需求说明

图书管理系统的主要功能包括图书购入、借阅、归还以及注销。管理人员可以查询读者、图书的借阅情况，还可对当前图书借阅情况进行统计，给出统计数据。简单的图书管理系统针对图书进行4方面的管理：购入新书、读者借书、读者还书以及图书注销。

购入新书需要为该书编制图书卡片，包括分类目录号、流水号（保证每本书都有唯一的流水号，同类图书也是如此）、书名、作者、内容摘要、价格和购书日期等信息，并将这些信息写入图书目录文件中。

读者借书时需填写借书单，包括读者号、欲借图书分类目录号，系统首先检查该读者号是否有效，若无效，则拒绝借书；否则进一步检查该读者所借图书是否超过最大限制数。若已达到最大限制数，则拒绝借书；若未达到最大限制数，读者可以借出该书，登记图书分类目录号、读者号和借阅日期等，写回到借书文件中。

读者还书时，根据图书流水号，从借书文件中读出和该图书相关的借阅记录，标明还书日期，再写回借书文件中。如果图书逾期未还，则处以相应罚款。

在某些情况下，需要对图书馆的图书进行清理工作，对一些过时或无继续保留价值的图书要注销，这时从图书文件里删除相关记录。

咨询要求分为查询读者信息、图书信息和借阅统计信息三种。

12.1.2　结构化分析

结构化分析的最终结果需要得到系统的数据流图、数据字典和加工处理说明。根据需求说明，首先界定系统的边界。因为购入新书、读者借书、读者还书和图书注销将来都是由图书管理员来操作系统，因此图书管理员将是系统的外部实体之一。当读者借书超期时，系统会给读者一个罚款单信息，所以，这里可以将系统时钟和读者也作为系统的外部实体之一。当然，如果认为罚款单是由系统管理员转交给读者，则可以认为图书管理系统只和管理员与系统时钟进行交互，这里采用第二种观点。咨询要求同样都是由系统管理员来操作，由此可以得出系统不完整的第0层DFD数据流图如图12-1所示。

图12-1　不完整的第0层DFD图

在0层DFD图上分析外部实体与系统间的数据流，因为管理员的两大工作任务是分析管理任务和咨询任务，因此管理员会向系统输入管理请求信息与查询请求信息，根据输入的管理请求，系统将会对一些存储文件进行修改；对查询请求，系统则会给出读者、图书与借阅的统计信息。系统时钟主要为图书管理系统提供系统时间。为图12-1补充数据流所得完整的0层DFD数据流图如图12-2所示。

图12-2　第0层DFD图

随后对0层DFD数据流图中的图书管理系统进行进一步细化，根据需求可以得知，系统主要分为管理任务和查询任务，因此可以将其细化为两个大的处理，如图12-3所示。

图12-3　第1层DFD图

同样，对处理2进行进一步细化，管理处理分为购书、借书、还书和清理4项任务，因此可以将处理2分解为4个处理，另外需要一个单独的处理根据管理请求的类型进行请求分派。细化后的数据流图如图12-4所示。

图12-4　处理2的第2层DFD图

同样的方法，可以对处理1也进行细化。一旦对处理1的细化完成，即可对得到的数据流图进行转化，从而形成系统的总体设计。但在转换之前，应该对数据流图中的数据流采用数据字典进行详细的说明，例如：

管理请求＝清理请求｜购书请求｜借书请求｜还书请求

清理请求＝图书分类目录号

┆

对于底层处理，以处理2.3为例进行说明。

加工编号：2.3

加工名称：借书

输入流：读者信息，借书信息

输出流：借书信息

处理逻辑：根据读者信息和借书信息，首先判断读者是否合法，判断读者是否已经超出借阅图书数目的最大限制，若都合法，将借书信息重新写入借书文件中，同时更新图书目录文件。

12.1.3　总体设计

由于数据流呈现了事务型的特性，因此，可采用事务型的变换方式对数据流图进行变换，从而得到的系统的总体结构如图12-5所示。

总体设计给出了数据流图中的各个处理间转换为模块后，模块与模块之间的调用关系，后续就需要根据总体设计给出模块的详细设计。

图12-5　系统总体结构图

12.1.4　详细设计

以借书为例，采用程序流程图的形式描述借书模块的详细设计。分析借书的输入是读者信息和借书信息，需要读者信息的读者借书证号，借书信息给出了读者已经借阅了多少本书，如果读者借阅的书籍数目尚未超出系统的限制，则允许借书，并把新的借书信息写入借书文件；否则，拒绝借书。该模块的详细流程图如图12-6所示。

图12-6　借书模块流程图

完成对每一个模块的详细设计，即可将详细设计转换为程序代码，从而实现整个管理系统。

12.2　数据库分析与设计

数据库设计属于系统设计的范畴。通常把使用数据库的系统统称为数据库应用系统，把对数据库应用系统的设计简称为数据库设计。数据库设计的任务是针对一个给定的应用环境，在给定的（或选择的）硬件环境和操作系统及数据库管理系统等软件环境下，创建一个性能良好的数据库模式，建立数据库及其应用系统，使之能有效地存储和管理数据，满足各类用户的需求。

合理的数据库结构是数据库应用系统性能良好的基础和保证，但数据库的设计和开发却是一项庞大而复杂的工程。从事数据库设计的人员，不仅要具备数据库知识和数据库设计技术，还要有程序开发的实际经验，掌握软件工程的原理和方法。数据库设计人员必须深入应用环境，了解用户具体的专业业务。在数据库设计的前期和后期，与应用单位人员密切联系，共同开发，可大大提高数据库设计的成功率。

12.2.1　数据库设计的步骤

1．数据库应用系统的生命期

按照软件工程对系统生命周期的定义，软件生命周期分为6个阶段工作：制定计划、需求分析、设计、程序编制、测试以及运行维护。在数据库设计中也参照这种划分，把数据库应用系统的生命周期分为数据库规划、需求描述与分析、数据库设计与应用程序设计、实现、测试、运行维护6个阶段。

（1）数据库规划。数据库规划是创建数据库应用系统的起点，是数据库应用系统的任务陈述和任务目标。任务陈述定义了数据库应用系统的主要目标，而每个任务目标定义了系统必须支持的特定任务。数据库规划过程还必然包括对工作量的估计、使用的资源和需要的经费等。同时，还应当定义系统的范围和边界，以及它与公司信息系统的其他部分的接口。

（2）需求描述与分析。需求描述与分析是以用户的角度，从系统中的数据和业务规则入手，收集和整理用户的信息，以特定的方式加以描述，是下一步工作的基础。

（3）数据库与应用程序设计。数据库设计是对用户数据的组织和存储设计；应用程序设计是在数据库设计基础上对数据操作及业务实现的设计，包括事务设计和用户界面设计。

（4）数据库系统实现。数据库系统实现是依照设计，使用DBMS支持的数据定义语言（DDL）实现数据库的建立，用高级语言（Basic、Delphi、C、C++和Power builder等）编写应用程序。

（5）测试阶段。测试阶段是在数据系统投入使用之前，通过精心制定的测试计划和测试数据来测试系统的性能是否满足设计要求，发现问题。

（6）运行维护。数据库应用系统经过测试、试运行后即可正式投入运行。运行维护是系统投入使用后，必须不断地对其进行评价、调整与修改，直至系统消亡。

在任一设计阶段，一旦发现不能满足用户数据需求时，均需返回到前面的适当阶段，进行必要的修正。经过如此的迭代求精过程，直到能满足用户需求为止。在进行数据库结构设计时，应考虑满足数据库中数据处理的要求，将数据和功能两方面的需求分析、设计和实现在各个阶段同时进行，相互参照和补充。

在数据库设计中，对每一个阶段设计成果都应该通过评审。评审的目的是确认某一阶段的任务是否全部完成，从而避免出现重大的错误或疏漏，保证设计质量。评审后还需要根据评审意见修改所提交的设计成果，有时甚至要回溯到前面的某一阶段，进行部分重新设计乃至全部重新设计，然后再进行评审，直至达到系统的预期目标为止。

2．数据库设计的方法

在确定了数据库设计的策略以后，就需要相应设计方法和步骤。多年来，人们提出了多种数据库设计方法，多种设计准则和规范。

1978年10月召开的新奥尔良（New Orleans）会议提出的关于数据库设计的步骤（简称新奥尔良法）是目前得到公认的，较完整、较权威的数据库设计方法，它把数据库设计分为如下4个主要阶段。

（1）用户需求分析。数据库设计人员采用一定的辅助工具对应用对象的功能、性能和限制等要求所进行的科学分析。

（2）概念设计。概念结构设计是对信息分析和定义，如视图模型化、视图分析和汇总。该阶段对应用对象精确地进行抽象和概括，以形成独立于计算机系统的企业信息模型。描述概念模型的较理想工具是E-R图。

（3）逻辑设计。将抽象的概念模型转化为与选用的DBMS产品所支持的数据模型相符合的逻辑模型，它是物理设计的基础。包括模式初始设计、子模式设计、应用程序设计、模式评价以及模式求精。

（4）物理设计。逻辑模型在计算机中的具体实现方案。

当各阶段发现不能满足用户需求时，均需返回到前面适当的阶段，进行必要的修正。如此经过不断地迭代和求精，直到各种性能均能满足用户的需求为止。

12.2.2　需求分析

需求分析是在项目确定之后，用户和设计人员对数据库应用系统所要涉及的内容（数据）和功能（行为）的整理和描述，是以用户的角度来认识系统。这一过程是后续开发的基础，以后的逻辑设计和物理设计以及应用程序的设计都会以此为依据。

1．需求分析的任务、目标及方法

需求分析阶段的任务：综合各个用户的应用需求，对现实世界要处理的对象（组织、部门和企业等）进行详细调查，在了解现行系统的概况，确定新系统功能的过程中，收集支持系统目标的基础数据及处理方法。

参与需求分析的主要人员是分析人员和用户，由于数据库应用系统是面向企业和部门的具体业务，分析人员一般并不了解，而同样用户也不会具有系统分析的能力，这就需要双方进行有效的沟通，使得设计人员对用户的各项业务了解和熟悉，进行分析和加工，将用户眼中的业务转换成为设计人员所需要的信息组织。

分析和表达用户需求的方法主要包括自顶向下和自底向上两类方法。自顶向下的结构化分析（Structured Analysis, SA）方法从最上层的系统组织机构入手，采用逐层分解的方式分析系统，并把每一层用数据流图和数据字典描述。需求分析的重点是调查组织机构情况、调查各部门的业务活动情况、协助用户明确对新系统的各种要求、确定新系统的边界，以此获得用户对系统的如下要求。

（1）信息要求。用户需要在系统中保存哪些信息，由这些保存的信息要得到什么样的信息，这些信息以及信息间应当满足的完整性要求。

（2）处理要求。用户在系统中要实现什么样的操作功能，对保存信息的处理过程和方式，各种操作处理的频度、响应时间要求、处理方式等以及处理过程中的安全性要求和完整性要求。

（3）系统要求。包括安全性要求、使用方式要求和可扩充性要求。安全性要求：系统有几种用户使用，每一种用户的使用权限如何。使用方式要求：用户的使用环境是什么，平均有多少用户同时使用，最高峰时有多少用户同时使用，有无查询相应的时间要求等。可扩充性要求：对未来功能、性能和应用访问的可扩充性的要求。

需求分析阶段的工作以及形成的相关文档（作为概念结构设计阶段的依据）如图12-7所示。

图12-7　需求分析阶段的工作

2．需求分析阶段的文档

需求调查所得到的数据可能是零碎的、局部的，分析师和设计人员必须进一步分析和表达用户的需求，建立需求说明文档、数据字典和数据流程图。将需求调查文档化，文档既要被用户所理解，又要方便数据库的概念结构设计。

数据流分析是对事务处理所需的原始数据的收集及经处理后所得数据及其流向，一般用数据流程图（DFD）来表示。DFD不仅指出了数据的流向，而且还指出了需要进行的事务处理（但并不涉及如何处理，这是应用程序的设计范畴）。除了使用数据流程图、数据字典以外，需求分析还可使用判定表、判定树等工具。下面介绍数据流程图和数据字典，其他工具的使用可参见软件工程等方面的参考书。

数据字典（Data Dictionary, DD）是各类数据描述的集合，它是关于数据库中数据的描述，即元数据，而不是数据本身。如用户将向数据库中输入什么信息，从数据库中要得到什么信息，各类信息的内容和结构，信息之间的联系等。数据字典包括数据项、数据结构、数据流、数据存储和处理过程5个部分（至少应该包含每个字段的数据类型和在每个表内的主键、外键）。

数据项描述＝｛数据项名，数据项含义说明，别名，数据类型，长度，取值范围，取值含义，与其他数据项的逻辑关系｝

数据结构描述＝｛数据结构名，含义说明，组成：｛数据项或数据结构｝｝

数据流描述＝｛数据流名，说明，数据流来源，数据流去向，组成：｛数据结构｝，平均流量，高峰期流量｝

数据存储描述＝｛数据存储名，说明，编号，流入的数据流，流出的数据流，组成：｛数据结构｝，数据量，存取方式｝

处理过程描述＝｛处理过程名，说明，输入：｛数据流｝，输出：｛数据流｝，处理：｛简要说明｝｝

需求分析阶段的成果是系统需求说明书，主要包括数据流图、数据字典、各种说明性表格、统计输出表和系统功能结构图等。系统需求说明书是以后设计、开发、测试和验收等过程的重要依据。关于需求分析的详细过程请参见第4.2节。

12.2.3　概念结构设计

数据库概念结构设计阶段是在需求分析的基础上，依照需求分析中的信息要求，对用户信息加以分类、聚集和概括，建立信息模型，并依照选定的数据库管理系统软件，转换成为数据的逻辑结构，再依照软硬件环境，最终实现数据的合理存储。这一过程也称为数据建模。

概念结构设计的目标是产生反映系统信息需求的数据库概念结构，即概念模式。概念结构是独立于支持数据库的DBMS和使用的硬件环境的。此时，设计人员从用户的角度看待数据以及数据处理的要求和约束，产生一个反映用户观点的概念模式，然后再把概念模式转换为逻辑模式。

1．概念结构设计策略与方法

概念结构设计是设计人员以用户的观点，对用户信息的抽象和描述，从认识论的角度来讲，是从现实世界到信息世界的第一次抽象，并不考虑具体的数据库管理系统。

现实世界的事物纷繁复杂，即使是对某一具体的应用，由于存在大量不同的信息和对信息的各种处理，也必须加以分类整理，理清各类信息之间的关系，描述信息处理的流程，这一过程就是概念结构设计。

概念结构设计的策略通常有以下4种：自顶向下、自底向上、逐步扩张和混合策略。实际应用中这些策略并没有严格的限定，可以根据具体业务的特点选择，如对于组织机构管理，因其固有的层次结构，可采用自顶向下的策略；对于已实现计算机管理的业务，通常可以以此为核心，采取逐步扩张的策略。

概念结构设计最著名、最常用的方法是P.P.S Chen于1976年提出的实体—联系方法（Entity-Relationship Approach, E-R）方法。它采用E-R模型将现实世界的信息结构统一由实体、属性以及实体之间的联系来描述。

使用E-R方法，无论是哪种策略，都要对现实事物加以抽象认识，以E-R图的形式描述出来。

2．用E-R方法建立概念模型

E-R图的设计要依照上述的抽象机制，对需求分析阶段所得到的数据进行分类、聚集和概括，确定实体、属性和联系。概念结构的具体工作步骤包括选择局部应用、逐一设计分E-R图和E-R图合并，如图12-8所示。

图12-8　概念结构设计工作步骤

（1）选择局部应用。需求分析阶段会得到大量的数据，这些数据分散杂乱，许多数据会应用于不同的处理，数据与数据之间关联关系也较为复杂，要最终确定实体、属性和联系，就必须根据数据流图这一线索理清数据。

数据流图是对业务处理过程从高层到底层的一级抽象，高层抽象流图一般反映系统的概貌，对数据的引用较为笼统，而底层又可能过于细致，不能体现数据的关联关系，因此要选择适当层次的数据流图，让这一层的每一部分对应一个局部应用，实现某一项功能。从这一层入手，就能很好地设计分E-R图。

（2）逐一设计分E-R图。划分好各个局部应用之后，就要对每一个局部应用逐一设计分E-R图，又称为局部E-R图。对于每一局部应用，其所用到的数据都应该收集在数据字典中，依照该局部应用的数据流图，从数据字典中提取出数据，使用抽象机制确定局部应用中的实体、实体的属性、实体标识符及实体间的联系及其类型。

事实上，在形成数据字典的过程中，数据结构、数据流和数据存储都是根据现实事物来确定的，因此都已经基本上对应了实体及其属性，以此为基础，加以适当调整，增加联系及其类型，就可以设计分E-R图。

现实生活中，许多事物作为实体还是属性没有明确的界定，这需要根据具体情况而定，一般遵循以下两条准则。

①　属性不可再分，即属性不再具有需要描述的性质，不能有属性的属性。

②　属性不能与其他实体发生联系，联系是实体与实体间的联系。

3．E-R图合并

根据局部应用设计好各局部E-R图之后，就可以对各分E-R图进行合并。合并的目的在于在合并过程中解决分E-R图中相互间存在的冲突，消除分E-R图之间存在的信息冗余，使之成为能够被全系统所有用户共同理解和接受的统一的、精炼的全局概念模型。合并的方法是将具有相同实体的两个或多个E-R图合而为一，在合成后的E-R图中把相同实体用一个实体表示，合成后的实体的属性是所有分E-R图中该实体的属性的并集，并以此实体为中心，并入其他所有分E-R图。再把合成后的E-R图以分E-R图看待，合并剩余的分E-R图，直至所有的E-R图全部合并，就构成一张全局E-R图。

分E-R图之间的冲突主要有以下三类。

（1）属性冲突。同一属性可能会存在于不同的分E-R图，由于设计人员不同或是出发点不同，对属性的类型、取值范围和数据单位等可能会不一致，这些属性对旧的数据将来只能以一种形式在计算机中存储，这就需要在设计阶段进行统一。

（2）命名冲突。相同意义的属性，在不同的分E-R图上有着不同的命名，或是名称相同的属性在不同的分E-R图中代表着不同的意义，这些也要进行统一。

（3）结构冲突。同一实体在不同的分E-R图中有不同的属性，同一对象在某一分E-R图中被抽象为实体，而在另一分E-R图中又被抽象为属性，需要统一。

分E-R图的合并过程中要对其进行优化，具体可以从以下几个方面实现。

（1）实体类型的合并。两个具有1∶1联系或1∶n联系的实体可以予以合并，使实体个数减少，有利于减少将来数据库操作过程中的连接开销。

（2）冗余属性的消除。一般在各分E-R图中的属性是不存在冗余的，但合并后就可能出现冗余。因为合并后的E-R图中的实体继承了合并前该实体在分E-R图中的全部属性，属性间就可能存在冗余，即某一属性可以由其他属性确定。

（3）冗余联系的消除。在分E-R图合并过程中，可能会出现实体联系的环状结构，即某一实体A与另一实体B间有直接联系，同时A又通过其他实体与实体B发生间接联系。通常直接联系可以通过间接联系所表达，可消除直接联系。

12.2.4　逻辑结构设计

逻辑结构设计即是在概念结构设计的基础上进行数据模型设计，可以是层次模型、网状模型和关系模型，本节介绍如何在全局E-R图基础上进行关系模型的逻辑结构设计。逻辑结构设计阶段的主要工作步骤包括确定数据模型、将E-R图转换成为指定的数据模型、确定完整性约束和确定用户视图，如图12-9所示。

图12-9　逻辑结构设计阶段工作步骤

1．E-R图关系模式的转换

E-R方法所得到的全局概念模型是对信息世界的描述，并不适用于计算机处理，为适合关系数据库系统的处理，必须将E-R图转换成关系模式。E-R图是由实体、属性和联系三要素构成，而关系模型中只有唯一的结构——关系模式，通常采用以下方法加以转换。

1）实体向关系模式的转换

将E-R图中的实体逐一转换成为一个关系模式，实体名对应关系模式的名称，实体的属性转换成关系模式的属性，实体标识符就是关系的码。

2）联系向关系模式的转换

E-R图中的联系有三种：一对一联系（1∶1）、一对多联系（1∶n）和多对多联系（m∶n），针对这三种不同的联系，转换方法如下。

（1）一对一联系的转换。一对多联系有两种方式向关系模式进行转换。一种方式是将联系转换成一个独立的关系模式，关系模式的名称取联系的名称，关系模式的属性包括该联系所关联的两个实体的码及联系的属性，关系的码取自任一方实体的码；另一种方式是将联系归并到关联的两个实体的任一方，给待归并的一方实体属性集中增加另一方实体的码和该联系的属性即可，归并后的实体码保持不变。

（2）一对多联系的转换。一对多联系有两种方式向关系模式进行转换。一种方式是将联系转换成一个独立的关系模式，关系模式的名称取联系的名称，关系模式的属性取该联系所关联的两个实体的码及联系的属性，关系的码是多方实体的码；另一种方式是将联系归并到关联的两个实体的多方，给待归并的多方实体属性集中增加一方实体的码和该联系的属性即可，归并后的多方实体码保持不变。

（3）多对多联系的转换。多对多联系只能转换成一个独立的关系模式，关系模式的名称取联系的名称，关系模式的属性取该联系所关联的两个多方实体的码及联系的属性，关系的码是多方实体的码构成的属性组。

2．关系模式的规范化

由E-R图转换得来的初始关系模式并不能完全符合要求，还会有数据冗余、更新异常存在，这就需要经过进一步的规范化处理，具体步骤如下。

（1）根据语义确定各关系模式的数据依赖。在设计的前一阶段，只是从关系及其属性来描述关系模式，并没有考虑到关系模式中的数据依赖。关系模式包含着语义，要根据关系模式所描述的自然主义写出关系数据依赖。

（2）根据数据依赖确定关系模式的范式。由关系的码及数据依赖，根据规范化理论，就可以确定关系模式所属的范式，判定关系模式是否符合要求，即是否达到了3NF或4NF。

（3）如果关系模式不符合要求，要根据关系模式的分解算法对其进行分解，达到3NF、BCNF或4NF。

（4）关系模式的评价及修正。根据规范化理论，对关系模式分解之后，就可以在理论上消除冗余和更新异常。但根据处理要求，可能还需要增加部分冗余以满足处理要求，这就需要作部分关系模式的处理，分解、合并或增加冗余属性，提高存储效率和处理效率。

3．确定完整性约束

根据规范化理论确定了关系模式之后，还要对关系模式加以约束，包括数据项的约束、表级约束及表间约束，可以参照SQL标准来确定不同的约束，如检查约束、主码约束和参照完整性约束，以保证数据的正确性。

4．用户视图的确定

确定了整个系统的关系模式之后，还要根据数据流图及用户信息建立视图模式，提高数据的安全性和独立性。

（1）根据数据流图确定处理过程使用的视图。数据流图是某项业务的处理，使用了部分数据，这些数据可能要跨越不同的关系模式，建立该业务的视图，可以降低应用程序的复杂性，并提高数据的独立性。

（2）根据用户类别确定不同用户使用的视图。不同的用户可以处理的数据可能只是整个系统的部分数据，而确定关系模式时并没有考虑这一因素，如学校的学生管理，不同的院系只能访问和处理自己的学生信息，这就需要建立针对不同院系的视图达到这一要求，这样可以在一定程度上提高数据的安全性。

12.2.5　数据库的物理设计

数据库系统实现是离不开具体的计算机的，在实现数据库逻辑结构设计之后，就要确定数据库在计算机中的具体存储。数据库在物理设备上的存储结构与存取方法称为数据库的物理结构，它依赖于给定的计算机系统。为一个给定的逻辑数据模型设计一个最适合应用要求的物理结构的过程，就是数据库的物理设计。

在数据库的物理结构中，数据的基本单位是记录，记录是以文件的形式存储的，一条存储记录就对应着关系模式中的一条逻辑记录。在文件中还要存储记录的结构，如各字段长度、记录长度等，增加必要的指针及存储特征的描述。

数据库的物理设计是离不开具体的DBMS的，不同的DBMS对物理文件存取方式的支持是不同的，设计人员必须充分了解所用DBMS的内部特征，根据系统的处理要求和数据的特点来确定物理结构。数据库的物理设计工作过程如图12-10所示。

图12-10　数据库的物理设计工作过程

一般来说，物理设计包括确定数据分布、存储结构和访问方式的工作。

1．确定数据分布

从企业计算机应用环境出发，需要确定数据是集中管理还是分布式管理，但目前企业内部网及因特网的应用越来越广泛，大都采用分布式管理。对于数据如何分布需要从以下几个方面考虑。

（1）根据不同应用分布数据。企业的不同部门一般会使用不同数据，将与部门应用相关的数据存储在相应的场地，使得不同的场地上处理不同的业务，对于应用多个场地的业务，可以通过网络进行数据处理。

（2）根据处理要求确定数据的分布。对于不同的处理要求，也会有不同的使用频度和响应时间，对于使用频度高、响应时间短的数据，应存储在高速设备上。

（3）对数据的分布存储必然会导致数据的逻辑结构的变化，要对关系模式作新的调整，回到数据库逻辑设计阶段作必要的修改。

2．确定数据的存储结构

存储结构具体指数据文件中记录之间的物理结构。在文件中，数据是以记录为单位存储的，可以是顺序存储、哈希存储、堆存储和B+
树存储等，要根据数据的处理要求和变更频度选定合理的物理结构。

为提高数据的访问速度，通常会采用索引技术。在物理设计阶段，要根据数据处理和修改要求，确定数据库文件的索引字段和索引类型。

3．确定数据的访问方式

数据的访问方式是由其存储结构所决定的，采用什么样的存储结构，就使用什么样的访问方式。数据库物理结构主要由存储记录格式、记录在物理设备上的安排及访问路径（存取方法）等构成。

1）存储记录结构设计

存储记录结构包括记录的组成、数据项的类型、长度和数据项间的联系，以及逻辑记录到存储记录的映射。在设计记录的存储结构时，并不改变数据库的逻辑结构，但可以在物理上对记录进行分割。数据库中数据项的被访问频率是很不均匀的，基本上符合公认的“80/20规则”，即“从数据库中检索的80％的数据由其中的20％的数据项组成”。

当多个用户同时访问常用数据项时，会因访盘冲突而等待。如果将这些数据分布在不同的磁盘组上，当用户同时访问时，系统可并行地执行I/O，减少访盘冲突，提高数据库的性能。所以对于常用关系，最好将其水平分割成多个裂片，分布到多个磁盘组上，以均衡各个磁盘组的负荷，发挥多磁盘组并行操作的优势。

2）存储记录布局

存储记录的布局，就是确定数据的存放位置。存储记录作为一个整体，如何分布在物理区域上，是数据库物理结构设计的重要一环。聚簇功能可以大大提高按聚簇码进行查询的效率。聚簇功能不但可用于单个关系，也适用于多个关系。设有职工表和部门表，其中部门号是这两个表的公共属性。如果查询涉及到这两个表的连接操作，可以把部门号相同的职工元组和部门元组在物理上聚簇在一起，既可显著提高连接操作的速度，又可节省存储空间。建立聚簇索引的原则如下。

（1）聚簇码的值相对稳定，没有或很少需要进行修改。

（2）表主要用于查询，并且通过聚簇码进行访问或连接是该表的主要应用。

（3）对应每个聚簇码值的平均元组数既不太多，也不太少。

任何事物都有两面性，聚簇对于某些特定的应用可以明显地提高性能，但对于与聚簇码无关的查询却毫无益处。相反地，当表中数据有插入、删除、修改时，关系中有些元组就要被搬动后重新存储，所以建立聚簇的维护代价是很大的。

3）存取方法的设计

存取方法是为存储在物理设备（通常是外存储器）上的数据提供存储和检索的能力。存取方法包括存储结构和检索机制两部分。存储结构限定了可能访问的路径和存储记录；检索机制定义每个应用的访问路径。

存取方法是快速存取数据库中数据的技术。数据库系统是多用户共享系统，对同一个关系建立多条存取路径才能满足多用户的多种应用要求。为关系建立多种存取路径是数据库物理设计的另一个任务。在数据库中建立存取路径最普遍的方法是建立索引。确定索引的一般顺序如下。

（1）首先可确定关系的存储结构，即记录的存放是无序的，还是按某属性（或属性组）聚簇存放。这在前面已讨论过，这里不再重复。

（2）确定不宜建立索引的属性或表。对于太小的表、经常更新的属性或表、属性值很少的表、过长的属性、一些特殊数据类型的属性（大文本、多媒体数据）和不出现或很少出现在查询条件中的属性不宜建立索引。

（3）确定宜建立索引的属性。例如关系的主码或外部码、以查询为主或只读的表、范围查询、聚集函数（Min、Max、Avg、Sum、Count）或需要排序输出的属性可以考虑建立索引。

索引一般还需在数据库运行测试后，再加以调整。在RDBMS中，索引是改善存取路径的重要手段。使用索引的最大优点是可以减少检索的CPU服务时间和I/O服务时间，改善检索效率。但是，不能对进行频繁存储操作的关系建立过多的索引，因为过多的索引也会影响存储操作的性能。

12.2.6　数据库实施与维护

在数据库正式投入运行之前，还需要完成很多工作。例如，在模式和子模式中加入数据库安全性、完整性的描述，完成应用程序和加载程序的设计，数据库系统的试运行，并在试运行中对系统进行评价。如果评价结果不能满足要求，还需要对数据库进行修正设计，直到满意为止。数据库正式投入使用，也并不意味着数据库设计生命周期的结束，而是数据库维护阶段的开始。数据库实施阶段的工作过程如图12-11所示。

图12-11　数据库实施阶段的工作过程

1．数据库实施

根据逻辑和物理设计的结果，在计算机上建立起实际的数据库结构并装入数据，进行试运行和评价的过程，叫做数据库的实施（或实现）。

1）建立实际的数据库结构

用DBMS提供的数据定义语言（DDL）编写描述逻辑设计和物理设计结果的程序（一般称为数据库脚本程序），经计算机编译处理和执行后，就生成了实际的数据库结构。所用DBMS的产品不同，描述数据库结构的方式也不同。有的DBMS提供数据定义语言，有的提供数据库结构的图形化定义方式，有的两种方法都提供。在定义数据库结构时，应包含以下内容。

（1）数据库模式与子模式，以及数据库空间等的描述。例如，在Oracle系统中，数据库逻辑结果的描述包括表空间（Tablespace）、段（Segment）、范围（Extent）和数据块（Data block）。DBA或设计人员通过对数据库空间的管理和分配，可控制数据库中数据的磁盘分配，将确定的空间份额分配给数据库用户，控制数据的可用性，将数据存储在多个设备上，以提高数据库性能等。

（2）数据库完整性描述。所谓数据的完整性，是指数据的有效性、正确性和一致性。在数据库设计时，如果没有一定的措施确保数据库中数据的完整性，就无法从数据库中获得可信的数据。数据的完整性设计，应该贯穿在数据库设计的全过程中。例如，在数据需求分析阶段，收集数据信息时，应该向有关用户调查该数据的有效值范围。在模式与子模式中，可以用DBMS提供的DDL语句描述数据的完整性。

（3）数据库安全性描述。数据安全性设计同数据完整性设计一样，也应在数据库设计的各个阶段加以考虑。在进行需求分析时，分析人员除了收集信息及数据间联系的信息之外，还必须收集关于数据的安全性说明。在设计数据库逻辑结构时，对于保密级别高的数据，可以单独进行设计。子模式是实现安全性要求的一个重要手段，可以为不同的应用设计不同的子模式。在数据操纵上，系统可以对用户的数据操纵进行两方面的控制：一是给合法用户授权，目前主要有身份验证和口令识别；二是给合法用户不同的存取权限。

（4）数据库物理存储参数描述。物理存储参数因DBMS的不同而不同。一般可设置的参数包括块大小、页面大小（字节数或块数）、数据库的页面数、缓冲区个数、缓冲区大小和用户数等。详细内容请参考DBMS的用户手册。

2）数据加载

数据库应用程序的设计应该与数据库设计同时进行。一般地，应用程序的设计应该包括数据库加载程序的设计。在数据加载前，必须对数据进行整理。由于用户缺乏计算机应用背景的知识，常常不了解数据的准确性对数据库系统正常运行的重要性，因而未对提供的数据作严格的检查。所以，数据加载前要建立严格的数据登录、录入和校验规范，设计完善的数据校验与校正程序，排除不合格数据。

数据加载分为手工录入和使用数据转换工具两种。现有的DBMS都提供了DBMS之间数据转换的工具。如果用户原来就使用数据库系统，可以利用新系统的数据转换工具。先将原系统中的表转换成新系统中相同结构的临时表，然后对临时表中的数据进行处理后插入到相应表中。数据加载是一项费时费力的工作。另外，由于还需要对数据库系统进行联合调试，所以大部分的数据加载工作应在数据库的试运行和评价工作中分批进行。

3）数据库试运行和评价

当加载了部分必须的数据和应用程序后，就可以开始对数据库系统进行联合调试，称为数据库的试运行。一般将数据库的试运行和评价结合起来的目的是测试应用程序的功能；测试数据库的运行效率是否达到设计目标，是否为用户所容忍。测试的目的是为了发现问题，而不是为了说明能达到哪些功能。所以，测试中一定要有非设计人员的参与。

2．数据库维护

只有数据库顺利地进行了实施，才可将系统交付使用。数据库一旦投入运行，就标志着数据库维护工作的开始。数据库维护工作的内容主要包括对数据库的监测和性能改善、故障恢复、数据库的重组和重构。在数据库运行阶段，对数据库的维护主要由DBA完成。

（1）对数据库性能的监测和改善。性能可以用处理一个事务的I/O量、CPU时间和系统响应时间来度量。由于数据库应用环境、物理存储的变化，特别是用户数和数据量的不断增加，数据库系统的运行性能会发生变化。某些数据库结构（如数据页和索引）经过一段时间的使用以后，可能会被破坏。所以，DBA必须利用系统提供的性能监控和分析工具，经常对数据库的运行、存储空间及响应时间进行分析，结合用户的反映确定改进措施。目前的DBMS都提供一些系统监控或分析工具。例如，在SQL Server中使用SQL Server Profiler组件、Transaction—SQL工具和Query Analyzer组件等都可进行系统监测和分析。

（2）数据库的备份及故障恢复。数据库是企业的一种资源，所以在数据库设计阶段，DBA应根据应用要求制定不同的备份方案，保证一旦发生故障能很快将数据库恢复到某种一致性状态，尽量减少损失。数据库的备份及故障恢复方案，一般基于DBMS提供的恢复手段。

（3）数据库重组和重构。数据库运行一段时间后，由于记录的增、删、改，数据库物理存储碎片记录链过多，影响数据库的存取效率。这时，需要对数据库进行重组和部分重组。数据库的重组是指在不改变数据库逻辑和物理结构的情况下，去除数据库存储文件中的废弃空间以及碎片空间中的指针链，使数据库记录在物理上紧连。数据库的重构是指当数据库的逻辑结构不能满足当前数据处理的要求时，对数据库的模式和内模式修改。

注意：由于数据库重构的困难和复杂性，一般都在迫不得已的情况下才进行。例如，应用需求发生了变化，需要增加新的应用或实体，取消某些应用或实体。又如，表的增删、表中数据项的增删、数据项类型的变化等。重构数据库后，还需要修改相应的应用程序，并且重构也只能对部分数据库结构进行。一旦应用需求变化太大，需要对全部数据库结构进行重组，说明该数据库系统的生命周期已经结束，需要设计新的数据库应用系统。

12.2.7　案例分析

某单位图书馆需要建立一个图书管理系统，在图书管理过程中，由于要处理多种不同的业务流程，各个部门之间有相互联系。

1．图书管理需求分析

通过对图书管理日常工作的详细调查，该图书馆对某书目的信息如表12-1所示，与该书目对应的图书信息如表12-2所示。

表12-1　书目信息

表12-2　图书信息

1）初步的需求分析结果

（1）资料室有图书管理员若干名，他们负责已购入图书的编目和借还工作，每名图书管理员的信息包括工号和姓名。

（2）读者可在阅览室读书，也可通过图书流通室借还图书，读者信息包括姓名、年龄、工作单位、借书证号、电话和E-mail，系统为不同读者生成不同的借书证号。

（3）每部书在系统中对应唯一的一条图书在版编目数据（CIP，以下简称书目），书目的基本信息包括ISBN号、书名、作者、出版商、出版年月以及本资料室拥有该书的册数（以下简称册数），不同书目的ISBN号不相同。

（4）资料室对于同一书目的图书可拥有多册（本），图书信息包括图书ID、ISBN号、存放位置和当前状态，每一本书在系统中被赋予唯一的图书ID。

（5）一名读者最多只能借阅10本图书，且每本图书最多只能借两个月，读者借书时需由图书管理员登记读者ID、所借图书ID、借阅时间和应还时间，读者还书时图书管理员在对应的借书信息中记录归还时间。

（6）当某书目的可借出图书的数量为0时，读者可以对其进行预约登记，即记录读者ID、需要借阅的图书的ISBN号和预约时间。

2）业务流程

图书管理信息系统在应用中的业务流程图如图12-12所示。

图12-12　图书管理业务流程图

系统的主要业务处理如下。

（1）入库管理。图书购进入库时，管理员查询本资料室的书目信息，若该书的书目尚未建立，则由管理员编写该书的书目信息并录入系统，然后编写并录入图书信息；否则，修改该书目的册数，然后编写并录入图书信息。对于进入流通室的书，其初始状态为“未借出”，而送入阅览室的书的状态始终为“不外借”。

（2）图书证注册和注销。登记所有办理的新图书证和注销图书证，需要注明办理及注销日期及管理员号。

（3）挂失管理。包括挂失登记，登记挂失的图书证，使得该图书证不能借书，注明挂失日期及管理员号；解除挂失，解除已经挂失的图书证，使得该借书证可以开始借书，注明解除挂失日期及管理员号。

（4）借书管理。读者借书时，若有，则由管理员为该读者办理借书手续，并记录该读者的借书信息，同时将借出图书的状态修改为“已借出”。

（5）预约管理。若图书流通室没有读者要借的书，则可为该读者建立预约登记，需要记录读者ID、书的ISBN号、预约时间和预约期限（最长为10天）。一旦其他读者归还这种书，就自动通知该预约读者。系统将自动清除超出预约期限的预约记录并修改相关信息。

（6）还书管理。读者还书时，则记录相应借还信息中的“归还时间”，对于超期归还者，系统自动计算罚金（具体的计算过程此处省略）。系统同时自动查询预约登记表，若存在其他读者预约该书的记录，则将该图书的状态修改为“已预约”，并将该图书ID写入相应的预约记录中（系统在清除超出预约期限的记录时解除该图书的“已预约”状态）；否则，将该图书的状态修改为“未借出”。

（7）通知处理。对于已到期且未归还的图书，系统通过E-mail自动通知读者。若读者预约的书已到，系统则自动通过E-mail通知该读者来办理借书手续。

2．图书管理概念结构设计

根据需求分析的结果设计的实体联系图（不完整）如图12-13所示，请指出读者与图书、书目与读者、书目与图书之间的联系类型，并补充图12-13中实体间缺少的联系。

图12-13　图书管理系统的实体联系图

1）联系类型

读者与图书、书目与图书、书目与读者之间的联系类型分析如下。

（1）读者与图书之间的联系类型。读者与图书之间形成了借还关系，需求分析的结果已经说明“一名读者最多只能借阅10本图书”，显然一本图书可被多名读者借阅，而每名读者应该能够借阅多本图书，因此读者与图书之间的借还联系为多对多（n∶m），即空（1）和空（2）应分别填写n和m。

（2）书目与图书之间的联系类型。图书馆对于同一书目的图书可拥有多册（本），每一本书在系统中被赋予唯一的图书ID，所以书目与图书之间的联系类型为一对多（1∶n），即空（3）和空（4）应分别填写1和n。

（3）书目与读者之间的联系类型。当某书目的可借出图书的数量为0时，读者可以对其进行预约登记，由于一名读者可借阅多种图书，因此书目与读者之间的预约联系类型为多对多（n∶m），即空（5）和空（6）应分别填写n和m。

2）图书馆管理E-R模型

根据需求分析的结果，图书馆管理应该包括图书证注册、注销和挂失管理，因此在图12-13中，管理员和读者实体间缺少的联系有注册、注销和挂失借书证管理联系。补充缺少的联系的E-R模型如图12-14所示。

图12-14　补充缺少的联系的图书管理系统的实体联系图

3．图书管理逻辑结构设计

根据概念设计得到的E-R图转换成图书管理系统的主要关系模式如下，请补充“借还记录”和“预约登记”关系中的空缺。注：时间格式为“年．月．日　时∶分∶秒”，请指出读者、书目关系模式的主键，以及图书、借还记录和预约登记关系模式的主键和外键。

管理员（工号，姓名，权限）

读者（姓名，年龄，工作单位，借书证号，电话，E-mail）

书目（ISBN号，书名，作者，出版商，出版年月，册数，工号）

图书（图书ID，ISBN号，存放位置，状态，工号）

借还记录（　　（a）
，借出时间，应还时间，归还时间）

预约登记（　　（b）
，预约时间，预约期限，图书ID）

借书证管理（借书证号，使用状态，开始时间，结束时间，工号）

1）问题分析

由于读者借书时需由图书管理员登记借书证号、所借图书ID、借出时间和应还时间，还书时图书管理员在对应的借书信息中记录归还时间，因此借还记录关系中的空（a）处应填入“借书证号，图书ID”。

读者对某书目进行预约登记时，需记录借书证号、需要借阅的图书的ISBN号和预约时间等，目前的预约登记关系中已经有预约时间、预约期限、图书ID信息，显然还需要记录是哪位读者预约了书，以及书的ISBN号，因此，预约登记关系模式中的空（b）处应填入“借书证号，ISBN号”

2）主键与外键分析

主键也称为主码，是关系中的一个或一组属性，其值能唯一标识一个元组。根据图书管理的需求分析如下。

（1）管理员关系。主键显然是“工号”。

（2）读者关系。“系统为不同读者生成不同的借书证号”，因此读者关系的主键显然是“借书证号”。

（3）书目关系。不同书目的ISBN号不相同，书目关系的主键为书的“ISBN号”，外键是管理员关系的“工号”。

（4）图书关系。同一书目的多册（本）图书具有相同的ISBN号，因此所有的图书依据“图书ID”相互区分，图书关系的主键是“图书ID”，外键是书目关系的“ISBN号”和管理员关系的“工号”。

（5）借还记录关系。用于记录读者的借书和还书信息，为了区分读者在同一日期对同一本书多次借还，借还记录的主键为“借书证号，图书ID，借出时间”。借还记录是由联系借还对应的关系，它记录与图书和读者的联系。因此，借还记录具有外键借书证号和图书ID，分别与读者和图书相关联。

（6）预约登记关系。主键为“借书证号，ISBN号，预约时间”，外键为读者关系的“借书证号”、书目关系的“ISBN号”和图书关系的“图书ID”。

（7）借书证管理关系。主键为“借书证号，开始时间”，外键是管理员关系的“工号”。

在实现数据库逻辑结构设计之后，就要确定数据库在计算机中的具体存储。由于数据库在物理设备上的存储结构与存取方法依赖于给定的计算机系统和应用环境，故案例中不再介绍。

12.3　面向对象分析与设计

面向对象开发方法将问题和问题的解决方案组织为离散对象的集合，数据结构和行为都包含在对象的表示中。面向对象的特性包括表示、抽象、分类、封装、继承、多态和持久性。面向对象开发方法包括面向对象分析、面向对象设计和面向对象实现。面向对象分析强调在问题领域内发现和描述对象或概念。例如，在图书馆信息系统里包含了书、图书馆和顾客这样一些概念。面向对象设计采用协作的对象、对象的属性和方法说明软件解决方案的一种方式，强调的是定义软件对象和这些软件对象如何协作来满足需求，是面向对象分析的延续。例如，图书馆系统中，软件对象“书”可以有“标题”属性和“获取书”方法。在面向对象编程过程中会实现设计对象，如Java中的Book类。

面向对象方法中分析和设计有时会存在一部分重叠，不是完全独立的活动。在迭代开发中，不严格区分分析、设计和实现，而是每次迭代不同程度地进行精化。

面向对象分析和设计目前采用最多的可能是使用UML。它是面向对象的标准建模语言，通过统一的语义和符号表示，使各种方法的建模过程和表示统一起来，已成为面向对象建模的工业标准。UML通过事务、关系和图对现实世界进行建模。

12.3.1　面向对象分析与设计的步骤

面向对象分析包括三个活动：建模系统功能，发现并组织业务对象，组织对象并记录其关系。面向对象设计的目的是说明系统的对象和消息，包括精化用例模型以反映实现环境，建模支持用例情景的对象交互、行为和状态，修改对象模型以反映实现环境。

面向对象分析包含如下关键步骤。

（1）建模系统功能。需求分析可以包括对相关领域过程的描述，首先对系统需求进行建模，产生用例图。用例建模促进并鼓励了用户参与，为后期测试、跟踪和维护等方面提供支持，是项目成功的一个关键因素。建模需求用例模型的目的是提取和分析足够的需求信息，准备一个模型，该模型表示用户需要什么。产生用例的步骤如下。

①　确定参与者。

②　确定需求用例。

③　构造用例模型。

④　记录需求用例描述。

然后建模用例活动，即建模系统的过程和步骤，描述业务过程或用例的活动的顺序流程和并行活动。UML活动图用于构建系统的活动。建模用例执行过程中对象如何通过消息相互交互，将系统作为一个整体或者几个子系统进行考虑。

（2）定义领域模型。面向对象分析是从按对象分类的角度来创建对象领域的描述。领域的分解包括定义概念、属性和重要的关联。其结果可以被表示成领域模型，用一组显示领域概念或对象的图形来表示领域模型。

①　在用例建模中发现和确定业务对象。

②　组织对象并记录对象之间的主要概念关系。类图以图形化的用例描述对象及其关联关系。在该图中还包括多重性、关联关系、泛化／特化关系以及聚合关系。

（3）定义交互、行为和状态。面向对象设计定义软件对象及其协作。首先确定并分类用例设计类，然后确定类属性、行为和责任。顺序图可描述场景中涉及的所有对象类之间的交互，通过描述按照时间顺序对象之间的消息交互建模了用例（或用例的一部分）的逻辑。这些消息按照时间顺序自顶向下排列，展示出软件对象之间的消息流动以及因而引起的方法调用，描述对象之间的协作。还需基于对象的状态变化确定和建模具有复杂行为的对象，建模状态图。

（4）定义设计类图。除了在交互图中表示对象协作的动态视图外，还可以使用设计类图来创建类定义的静态视图，并说明类的属性和方法。

下面通过一个案例来说明如何应用面向对象分析和设计技术。

12.3.2　需求说明

某在线会议审稿系统（Online Reviewing System, ORS）主要处理会议前期的投稿和审稿事务。会议有名称、涵盖的主题（包括名称和描述）、提交期限、审稿期限、通知日期、状态和会议召开日期等信息。创建一个新的会议后审稿系统启动，在审稿结束时关闭审稿过程。

用户在初始使用系统时，必须在系统中注册成为作者或审稿人。需要提供名称、单位、电子邮件、登录名、密码、登录的状态。

作者登录后提交稿件和浏览稿件审阅结果。提交稿件必须在规定提交时间范围内，其过程为先输入标题和摘要、选择稿件所属主题类型、选择稿件所在位置（存储位置）。上述几步若未完成，则重复；若完成，则上传稿件至数据存储中，系统发送接收到稿件的通知。稿件的信息包括稿件ID、标题、摘要、URL、状态和录用标准。状态随着整个审稿过程的进行而进行更新。

审稿人登录后可设置兴趣领域、审阅稿件给出评价意见以及罗列录用和（或）拒绝的稿件。审阅意见从原创性、技术质量、相关性和总体评价多个方面进行录入。稿件要限制不能被作者本人审阅。

会议委员会主席是一个特殊审稿人，可以浏览提交的稿件、到了稿件提交的最终日期后给审稿人分配稿件、罗列录用和（或）拒绝的稿件以及关闭审稿过程。如果审稿完成，浏览提交的稿件时可以列出录用和（或）拒绝的稿件。分配稿件时需要满足一篇稿件陆续被分配给3个审稿人审阅，当审阅完成时，如果3位审稿人的总评价满足设定的录用标准（审稿人评价值大于等于某个阈值），则稿件被录用，否则退稿，并发送电子邮件通知作者被录用或退稿。如果稿件被录用，则准备可打印版并打印。关闭审稿过程包括罗列录用和（或）拒绝的稿件。

12.3.3　建模用例

用例图描述系统与外部系统和用户的交互。换句话说，它们以图形化的方式描述了谁将使用系统，以及用户期望以什么方式与系统交互。用例描述也用于以文本化的方式描述每个交互步骤的顺序。用例是一个行为上相关的步骤序列（一个场景），既可以是自动的也可以是手工的，其目的是完成一个单一的业务任务。用例从外部用户的观点并以他们可以理解的方式和词汇描述系统功能。参与者代表了需要同系统交互以交换信息的任何事物。发起或者触发用例的外部用户称为参与者，如人、组织、其他信息系统、外部设备、甚至是时间。用例之间还可能存在扩展关系、包含关系、依赖关系和继承关系，需要进行识别和建模。

初步的需求分析得出表12-3所示的参与者和表12-4所示用例。

表12-3　参与者列表

表12-4　用例名称列表

从需求说明中可知：

（1）用户需注册成为合法的作者和审稿人，因此，如果一个用户要登录时，可先注册，再登录。

（2）作者必须登录之后才能提交稿件。

（3）审稿完成后，浏览提交的稿件时可以列出录用和（或）拒绝的稿件。

（4）关闭审稿过程包括罗列录用和（或）拒绝的稿件，可识别出扩展关系和包含关系。

根据上述分析，建模出用例图如图12-15所示的审稿系统用例图。

图12-15　审稿系统用例图

用例建模技术用于描述系统使用的模型，其建模过程是以用户为中心的建模过程，先识别问题中的参与者、再根据参与者确定每个参与者的用例、定义用例之间的关系、确定模型的过程。用例模型作为后续面向对象分析和设计的基础。

注意：要小心选用用例之间的关系，一般来说这些关系都会增加用例和关系的个数，从而增加用例模型的复杂度。

12.3.4　建模活动

用例建模完成后，对每个用例进行细化，建模活动图。活动图描述一个业务过程或者一个用例的活动的顺序，也可以用于系统的建模逻辑。以用例提交稿件（submit paper）为例，建模其活动。

根据需求描述，提交稿件过程识别出表12-5所示的活动。

表12-5　提交稿件活动名称列表

因为从建模用例图时可知，参与者、作者和用户之间存在继承关系，提交稿件和登录之间存在扩展关系，提交稿件必须是在登录之后进行，所以，提交稿件的过程中首先判断用户是否登录，如果登录则继续；如果没有登录，则执行登录活动。根据活动说明，建模出图12-16所示的活动图。

图12-16　提交稿件过程

活动图并不同于流程图，它提供了描述并行活动的机制，特别适合用于建模当操作正在执行时的活动，以及那些活动的结果。建模活动图时应该遵循如下指导原则。

（1）从一个作为起点的初始节点开始。

（2）如果它们与你的分析有关就增加分割。

（3）为用例的每个主要步骤（或者一个角色发起的每个主要步骤）添加一个动作。

（4）从一个活动到另一个活动、决策点或者终点添加一条流。为了含义的最大精确度，每个动作应该只有一个输入流和一个输出流，所有的分支、联合、决策和合并例外。

（5）在流分解成不同的路线的地方添加决策。确保用一个合并将各个流重新合并。

（6）在并行执行活动的地方添加分支和联合。

（7）用一个单一的活动终止符号结束。

12.3.5　设计类图

类图用于描述系统的对象结构，它们显示了构成系统的对象类，以及那些对象类之间的关系。根据需求描述识别出对象类型有会议、用户、会议主题、稿件和审阅意见。

（1）会议。“会议有名称、涵盖的主题（包括名称和描述）、提交期限、审稿期限、通知日期、状态和会议召开日期等信息。创建一个新的会议后审稿系统启动，在审稿结束时关闭审稿过程。”所得到会议的属性有名称、提交期限、审稿期限、通知日期、状态和会议召开日期；行为有创建会议和关闭审稿过程。

（2）用户。“需要提供名称、单位、电子邮件、登录名、密码、登录的状态。”因此，得到用户的属性有名称、单位、电子邮件、登录名、密码、登录的状态；行为有登录和注册。

（3）会议主题。会议涵盖的主题包括名称和描述，所以属性为名称和描述。

（4）稿件。上传稿件的过程中需要输入标题和摘要、类型、URL、状态已经审阅后的结果，并在系统内有唯一标识，所以稿件的属性有稿件ID、标题、摘要、URL和录用标准；行为有上传、分配和更新状态。

（5）审阅意见。审稿人对稿件进行审阅时，从原创性、技术质量、相关性和总体评价多个方面录入审阅意见。所以，审阅意见的属性有原创性、技术质量、相关性和总体评价；行为有输入审阅意见。

对于每个类型之间，确定其对象／类关系和度数，例如会议和稿件之间的关系是聚合关系，一个会议有很多稿件，得到图12-17所示审稿系统的类图。

Lenat和Guha建议，如果一个概念满足以下几点就应该抽象为一个类。

（1）几个有趣的事务，它们可以作为一个整体被描述为这个概念。

（2）它具有不与任何其他的类共享的属性。

（3）这个概念的声明能将这个类及其所从属的某个较大的类相区别。

（4）这个概念的边界是不精确的。

（5）“兄弟”（如辅助类，这些辅助类的联合是该类的自然泛化）数目是少的。

图12-17　审稿系统的类图

12.3.6　建模对象状态

对象状态表示对象在其生命期中某一点所处的条件，通过修改对象属性的一个或多个值的事件触发对象状态变化。状态图用于建模在生命周期中事件如何改变对象可以经历的各种状态，以及引起对象从一个状态向另一个状态转换的事件。

基于需求描述中的信息，稿件上传成功之后，成为已上传状态。上传稿件的最后期限到了之后，就进入分配中，当分配给审稿人的数量达到规定的三个之后，进入审阅中，审阅结束后，评估结果如果大于等于设定的阈值，则变为录用状态，然后发送E-mail之后，变为已通知，当提交完准备好印制的电子版时，进入打印中，出版之后，状态结束；如果评估结果小于设定的阈值，变为解决状态，发送给作者E-mail通知状态结束。根据分析，得到图12-18所示的一份稿件的状态图。

建模状态图时应该注意遵循如下指导原则。

（1）状态名称要简单但具有描述性。

（2）避免“黑洞”状态，即只有变换进来而没有任何转换发出的状态，这种状态要么由于该状态是一个最终状态，要么就是已经错过了一个或多个转换。

（3）避免“奇迹”状态，即只有转换发出而没有任何转换进来的状态，这种状态要么是一个起点，要么就是已经错过了一个或多个转换。

（4）对符合状态，需要对子状态集进行建模。

（5）为复杂的实体创建分层的状态图。

图12-18　一份稿件的状态图

12.3.7　建模序列图

序列图是以图形化的方式描述了在一个用例或操作的执行过程中对象如何通过消息互相交互，说明了消息在对象之间被发送和接收以及发送的顺序。根据分析设计的迭代演化需求，最初可以绘制系统序列图。系统序列图是一副描述角色和系统在用例场景下交互的图形，有助于确定进入和退出系统的高层信息。

以审稿人获取已分配给自己的稿件的执行序列，浏览会议主页，看到主页后，从导航条浏览已分配给自己的稿件，可循环进行选择，创建已分配稿件并显示给审阅人。得出图12-19所示的审稿人获取分配给自己的稿件列表。

建模序列图应该遵循如下指导原则。

（1）确定顺序图的范围，描述这个用例场景或一个步骤。

（2）绘制参与者和接口类，如果范围包括这些内容的话。

（3）沿左手边列出用例步骤。

（4）对控制器类及必须在顺序中协作的每个实体类，基于它拥有的属性或已经分配给它的行为绘制框。

（5）为持续类和系统类绘制框。

（6）绘制所需消息，并把每条消息指到将实现响应消息的责任的类上。

（7）添加活动条指示每个对象实例的生命期。

（8）为清晰起见，添加所需的返回消息。

（9）如果需要，为循环、可选步骤和替代步骤等添加框架。

图12-19　审稿人获取分配的稿件列表

12.4　算法分析与设计

12.4.1　算法与软件系统

算法的概念在计算机科学领域几乎无处不在，在各种计算机软件系统的实现中，算法设计往往处于核心地位。例如，操作系统是现代计算机系统中不可缺少的系统软件，其中的各个任务都是一个单独的问题，每个问题由一个子程序根据特定的算法来实现。

尽管对有些应用来说，在应用这一层面上没有什么特别明显的算法方面的要求（例如，一些简单的Web应用），但是大多数问题对算法还是有一定要求的。例如，假设有一种基于Web的服务，用于确定如何从一个地方旅行到另一个地方。其实现依赖于快速的计算机硬件、图形用户界面（GUI）、广域网技术，甚至还可能要依赖于面向对象技术。除此之外，它还需要某些操作设计算法，如寻找路由（最短路径算法）、显示地图和插入地址等。

此外，即使那些在应用层面上对算法性内容没有什么要求的应用，其实也是相当依赖于算法的，该应用是否要依赖于快速的计算机硬件？硬件的设计就要用到算法。该应用是否要用到图形用户界面？任何GUI的设计也要依赖于算法。该应用是否要依赖于网络技术？网络路由对算法也有很大的依赖。该应用是否采用某种非机器代码的语言编写？那么，它就要由编译器、解释器或汇编器来处理，所有这些软件都要大量用到各种算法。因此，每个软件系统都会直接或者间接地涉及到算法理论，算法是当代计算机中用到的大部分技术的核心。

随着计算机性能的不断增长，有人可能认为，算法的研究已经不是那么重要了。但是想一想，计算机可以做得很快，但还不能是无限快。存储器可以做得很便宜，但不会是免费的。因此，计算时间是一种有限的资源，存储空间也是一种有限的资源，在开发软件系统时，这些有限的资源应该得到有效的利用。而且，正是由于计算机性能的不断增长，可以利用计算机来解决更复杂的问题，或者规模更大的问题。因此，算法的研究是必要的，它是推动计算机技术发展的关键。

正如第9章提到的，算法理论的研究主要包括算法设计技术和算法分析技术，这两者是不可分割的一个整体。算法设计要设计出针对问题的高质量的算法，算法分析的对象是被设计出的算法，并研究算法所耗费的计算资源与问题规模之间的函数关系，而每一个被设计出的算法只有经过算法分析，才能评价其质量的优劣。

因此，用什么方法来设计算法，如何判定一个算法的优劣，所设计的算法需要占用多少时间资源和空间资源，在实现一个软件系统时，都是必须予以解决的重要问题。

12.4.2　算法设计过程

算法是问题的解决方案，这个解决方案本身并不是问题的答案，而是能获得答案的指令序列。不言而喻，由于实际问题千奇百怪，问题求解的方法千变万化，所以，算法的设计过程是一个灵活的充满智慧的过程，它要求设计人员根据实际情况具体问题具体分析。可以肯定的是，设计算法是一个非常有创造性和值得付出精力的过程。

在设计算法时，有几个问题是要考虑到的：如何设计算法？如何表示算法？如何证明问题解决的正确性？如何评估算法的效率？如何进行算法的最优化？根据这些问题，可以归纳出算法设计的主要步骤：理解问题；确定相关因素，包括问题的输入与输出、用何种数据结构、用什么样的算法设计策略等；设计算法；证明所设计的算法的正确性；分析所设计的算法的效率；实现所设计的算法。

1）理解问题

在面对一个算法任务时，算法设计者往往不能准确地理解要求他做的是什么。对算法希望实现什么只有一个大致的想法就匆忙地落笔写算法，其后果往往是写出的算法漏洞百出。在设计算法时需要做的第一件事就是完全理解要解决的问题，仔细阅读问题的描述，手工处理一些小例子。

对设计算法来说，这是一项重要的技能。准确地理解算法的输入是什么？要求算法做的是什么？即明确算法的入口和出口，这是设计算法的切入点。

2）确定相关因素

（1）预测所有可能的输入。算法的输入确定了该算法所解问题的一个实例。一般而言，对于问题P，总有其相应的实例集I，因此算法A若是问题P的算法，则意味着把P的任一实例input∈I作为算法A的输入，都能得到问题P的正确输出。

预测算法所有可能的输入，包括合法的输入和非法的输入。事实上，无法保证一个算法（或程序）永远不会遇到一个错误的输入，一个对大部分输入都运行正确而只有一个输入不行的算法，就像一颗等待爆炸的炸弹。这绝不是危言耸听，有大量这种引起灾难性后果的案例。例如，许多年以前，整个AT&T的长途电话网崩溃，造成了几十亿美元的直接经济损失。原因只是一段程序的设计者认为他的代码能一直传送正确的参数值，可是有一天，一个不应该有的值作为参数传递了，导致了整个北美电话系统的崩溃。

如果养成习惯——首先考虑问题和它的数据，然后列举出算法必须处理的所有特殊情况，那么可以更快速地成功构造算法。

（2）在精确解和近似解间做选择。计算机科学的研究目标是用计算机来求解人类所面临的各种问题。但是，有些问题无法求得精确解，例如求平方根、解非线性方程和求定积分等。有些问题由于其固有的复杂性，求精确解需要花费太长的时间，其中最著名的要算旅行商问题（TSP问题），此时，只能求出近似解。

有时需要根据问题以及问题所受的资源限制，在精确解和近似解间做选择。

（3）确定适当的数据结构。在结构化程序设计时代，著名的计算机学者沃思（Wirth）提出了“算法＋数据结构＝程序”的观点，断言了算法和数据结构是构成计算机程序的重要基础。在面向对象的程序设计时代，数据结构对于算法设计和分析仍然是至关重要的。

确定数据结构通常包括对问题实例的数据进行组织和重构，以及为完成算法所设计的辅助数据结构。在很多情况下，数据结构的设计直接影响基于该结构之上设计的算法的时间性能。

（4）确定算法设计技术。第9章介绍的算法设计技术（或称为算法设计策略）是设计算法的一般性方法，可用于解决不同计算领域的多种问题。这些算法设计技术包括分治法、动态规划法、贪心算法、回溯法、分支限界法、概率算法和近似算法等，已经被证明是对算法设计非常有用的通用技术，它们构成了一组强有力的工具，在为新问题设计算法时，可以运用这些技术设计出新的算法。算法设计技术作为问题求解的一般性策略，在解决计算机领域以外的问题时，也能发挥相当大的作用。

3）设计算法

根据1）和2）的结果，就可以设计算法。在构思和设计了一个算法之后，必须清楚准确地将所设计的求解步骤记录下来，即描述算法。描述算法的常用方法有自然语言、流程图、程序设计语言和伪代码等，其中伪代码是比较合适的描述算法的方法。

4）证明算法的正确性

可以用循环不变式来证明算法的正确性，以插入排序为例介绍循环不变式，以下是插入排序的伪代码（伪代码的约定参考第9章）。

对要分析的算法，定义循环不变式，如对插入排序，定义其外层循环的循环不变式为：在每一轮迭代的开始，子数组A［1..j-1］中包含了最初位于A［1..j-1］，但目前已经排好序的各个元素。然后证明循环不变式的三个性质。

（1）初始化。在循环的第一轮迭代开始之前它是正确的。对于插入排序，第一轮迭代之前，j＝2，子数组为A［1..j-1］，即A［1］，也就是最初在A［1］中的那个元素，显然这个子数组是已排好序的，因此循环不变式成立。

（2）保持。如果在循环的每一次迭代之前它是正确的，那么在下一次迭代之前，它也应该保持正确。对于插入排序（上述伪代码包含两重循环，应该定义两个循环不变式，并证明它们的正确性。但为了简便，暂时不陷入过于形式化的细节，仅考虑外层循环），要将A［j-1］、A［j-2］和A［j-3］等元素向右移一个位置，直到找到A［j］的适当位置为止（第4～7行），这时将A［j］的值插入（第8行）。很显然，循环不变式是成立的。

（3）终止。当循环结束时，循环不变式给了我们一个有用的性质，它有助于表明算法是正确的。对于插入排序，当j大于n时，外层for循环结束。在循环不变式中，将j替换为n+1，就有子数组A［1..n］包含了原先在A［1..n］中的元素，但现在已经排好序了，这意味着算法是正确的。

除了循环不变式外，经验和研究表明，发现算法（或程序）中逻辑错误的重要方法就是系统地跟踪算法。跟踪必须要用“心和手”来进行，跟踪者要像计算机一样，用一组输入值来执行该算法，并且这组输入值要最大可能地暴露算法中的错误。即使有几十年经验的高级软件工程师，也经常利用此方法查找算法中的逻辑错误。

5）分析算法的效率

设计出的算法只有经过分析，才能评价其优劣，才能判断其是否能满足问题求解的需求或者在多个算法之间进行选择。算法分析主要分析两种效率：时间效率和空间效率，时间效率显示了算法运行得有多快，空间效率则显示了算法需要多少额外的存储空间，相比而言，一般更关注算法的时间效率。事实上，计算机的所有应用问题，包括计算机自身的发展，都是围绕着“时间—速度”这样一个中心进行的。一般来说，一个好的算法首先应该是比同类算法的时间效率高。

6）根据算法编写代码

现代计算机技术还不能将伪代码形式的算法直接“输入”进计算机中，而需要把算法转变为特定程序设计语言编写的程序，算法中的一条指令可能对应实际程序中的多条指令。在把算法转变为程序的过程中，虽然现代编译器提供了代码优化功能，但仍需要一些标准的技巧，例如，在循环体之外计算循环中的不变式、合并公共子表达式、用开销低的操作代替开销高的操作等。一般来说，这样的优化对算法速度的影响是一个常数因子，可能会使程序提高10％～50％的速度。

算法设计的一般过程如图12-20所示。需要强调的是，一个好算法是反复努力和重复修正的结果。那么，什么时候应该停止这种改进呢？设计算法是一种工程行为，需要在资源有限的情况下，在互斥的目标之间进行权衡。设计者的时间显然也是一种资源，在实际应用中，常常是项目进度表迫使我们停止改进算法。

图12-20　算法设计过程

12.4.3　算法问题类型

就像生物学家把自然界的所有生物作为自己的研究对象，计算机科学把问题作为自己的研究对象，研究如何用计算机来解决人类所面临的各种问题。在计算领域的无数问题中，或者由于问题本身具有一些重要特征，或者由于问题具有实用上的重要性，有一些领域的问题是算法研究人员特别关注的。经验证明，无论对于学习算法还是应用算法，对这些问题的研究都是极其重要的。下面列出几类重要的问题。

1．查找问题

查找是在一个数据集合中查找满足给定条件的记录。对于查找问题来说，没有一种算法对于任何情况都是合适的。有的算法查找速度比其他算法快，但却需要较多的存储空间（例如Hash查找）；有的算法查找速度非常快，但仅适用于有序数组（例如折半查找），等等。此外，如果在查找的过程中数据集合可能频繁地发生变化，除了考虑查找操作外，还需要考虑在数据集合中执行插入和删除等操作。这种情况下，就必须仔细地设计数据结构和算法，以便在各种操作的需求之间达到一个平衡。而且，组织用于高效查找的特大型数据集合对于实际应用具有非常重要的意义。

2．排序问题

简单地说，排序就是将一个记录的无序序列调整成为一个有序序列的过程。在对记录进行排序时，需要选定一个信息作为排序的依据，例如，可以按学生平均成绩对学生记录进行排序，这个特别选定的信息（平均成绩）称为关键码。

排序的一个主要目的是为了进行快速查找，这就是为什么字典、电话簿和班级名册都是排好序的。当然，在很多领域的重要算法中，排序也被作为一个辅助步骤，例如，搜索引擎将搜索到的结果按相关程度排序后显示给用户。

迄今为止，已经发现的排序算法不下于几十种，没有一种排序算法在任何情况下都是最好的解决方案，有些排序算法比较简单，但速度相对较慢；有些排序算法速度较快，但却很复杂；有些排序算法适合随机排列的输入；有些排序算法更适合基本有序的初始排列；有些排序算法仅适合存储在内存中的序列；有些排序算法可以用来对存储在磁盘上的大型文件排序等。

3．图问题

算法中最古老也是最令人感兴趣的领域是图问题，很多纷乱复杂的现实问题抽象出的模型都是图结构，如社会网络、Web、工作流、XML数据、电路、图像、化合物和生物网络等。作为一种通用的、复杂的数据结构，基于图的相关算法也具有十分重要的作用。

有些图问题在计算上是非常困难的，这意味着，在能够接受的时间内，即使用最快的计算机，也只能解决这种问题的一个很小规模的实例，例如TSP问题、最大团问题（即最大独立集问题）、图着色问题、哈密尔顿回路问题、顶点覆盖问题和最长路径问题等。图问题中还有一个奇怪的现象：许多形式上非常类似的问题，解决它们的难度却相差很大，例如，最短路径问题和欧拉回路问题存在多项式时间算法，而最长路径问题和哈密尔顿回路问题至今没有找到一个多项式时间算法。

4．组合问题

组合问题一般都是最优化问题，因此也称为组合优化问题，即寻找一个组合对象，例如一个排列、一个组合或一个子集，这个对象能够满足特定的约束条件并使得某个目标函数取得极值：价值最大或者成本最小。典型的组合优化问题包括0-1背包问题、TSP问题和整数线性规划等。

无论从理论的观点还是实践的观点，组合问题都是计算领域中最难的问题，其原因如下。

（1）随着问题规模的增大，组合对象的数量增长极快，即使是中等规模的实例，其组合对象的数量也会达到不可思议的数量级，产生组合爆炸。

（2）还没有一种已知算法能在可接受的时间内，精确地求解绝大多数问题。

5．几何问题

几何问题处理类似于点、线、面和体等几何对象。几何问题与其他问题的不同之处在于，哪怕是最简单、最初等的几何问题也难以用数字去处理。尽管人类对几何问题的研究从古代起便没有中断过，但是具体到借助计算机来解决几何问题的研究，还只是停留在一个初始阶段。随着计算机图像图形处理、机器人和断层X摄像技术等方面应用的深入，人们对几何算法产生了强烈的兴趣。经典的几何问题包括最近点对问题和凸包问题，前者指的是在给定平面上的n个点中，求距离最近的两个点；后者指的是要找出一个能把给定集合中的所有点都包含在里面的最小凸多边形。

12.4.4　现代优化计算方法

上述提到的组合优化问题很多都是NP-难问题，因此目前还不存在有效的算法——多项式时间算法来求解，而这些问题又是现实中人们常常遇到的、需要解决的问题。因此，计算机科学和算法研究者们致力于研究求解这些难解问题的一些可行的方法。自20世纪80年代以来得到快速发展和广泛应用的现代优化计算方法，借助现代计算机作为工具，对复杂的组合优化问题的求解具有普遍适用性。这些方法包括禁忌搜索算法（tabu search）、模拟退火算法（simulated annealing）、遗传算法（genetic algorithms）、蚁群优化算法（ant colony optimization）和人工神经网络（artificial neural networks）等。它们中的每一个算法都以人类、生物的行为方式或物质的运动形态为背景，经过数学抽象建立算法模型，通过计算机的计算来求解组合优化问题，因此这些算法也被称之为元启发式算法（meta-heuristics）。

1．禁忌搜索算法

禁忌搜索的思想最早由Glover于1986年提出，它是对局部邻域搜索的一种扩展，是一种全局逐步寻优算法，是对人类智力过程的一种模拟。简单的局部邻域搜索从任何一个初始解出发，达到一个局部最优解停止。因此，算法停止时得到的解依赖于算法初始解的选取、邻域的结构和邻域选解的规则。这个方法往往容易导致局部最优解，而难以得到全局最优解。而禁忌搜索算法引入禁忌策略尽量避免迂回搜索，并通过藐视准则来赦免一些被禁忌的优良状态，进而保证多样化的有效探索以最终实现全局优化。禁忌搜索的思想基于这样的日常生活现象：当要寻找某个东西或寻找某个地方时，对刚刚已经搜索的地方不会立即去搜索，而更大的概率是到近一段时间内没有去过的地方进行搜索，若没有找到，再搜索已经去过的地方。

禁忌搜索涉及到邻域函数、禁忌表、禁忌对象、禁忌长度、候选解和藐视准则等概念。通常邻域函数沿用局部邻域搜索的思想，以实现邻域搜索；禁忌表和禁忌对象的设置，体现了算法避免迂回搜索的特点；禁忌长度决定禁忌对象的任期；候选解从当前解的邻域函数产生的邻域解中确定；藐视准则是对优良状态的奖励，是对禁忌策略的一种松弛，从而实现全局优化。

禁忌算法的基本思想：给定一个初始解（当前解）和一种邻域函数，在当前解的邻域函数确定的邻域解中确定若干候选解。若最佳候选解对应的目标值优于best so far状态，则忽视其禁忌特性，用其代替当前解和best so far状态，并将相应的对象加入禁忌表，同时修改禁忌表中各对象的任期。若不存在上述候选解，则在候选解中选择非禁忌的最佳状态为新的当前解，而无视它与当前解的优劣，同时将相应的对象加入禁忌表，并修改禁忌表中各对象的任期。如此重复上述迭代搜索过程，直至满足停止准则。

禁忌搜索算法的新解不是在当前解的邻域中随机产生，而是优于best so far的解，或是非禁忌的最佳解，因此选取优良解的概率远远大于其他解。由于禁忌搜索算法具有灵活的记忆功能和藐视准则，并且在搜索过程中可以接受劣解，所以具有较强的“爬山”能力，搜索时能够跳出局部最优解，转向解空间的其他区域，从而增强获得更好的全局最优解的概率，所以禁忌搜索算法是一种局部搜索能力很强的全局迭代寻优算法。但是，禁忌搜索算法对于初始解具有较强的依赖性，一个较好的初始解可使禁忌搜索在解空间中搜索到更好的解，而一个较差的初始解则会降低禁忌搜索的收敛速度，搜索到的解也相对较差。迭代搜索过程是串行的，仅是单一状态的移动，而非并行搜索，这就使得算法的优化时间往往较长。

禁忌搜索算法在组合优化、生产调度、机器学习、路由选择和电路设计等领域取得了很大的成功。并且，为了进一步改善禁忌搜索算法的性能，可以对算法本身的操作和参数选择进行改进，也可以把禁忌搜索与其他启发式方法结合起来，如与模拟退火、遗传算法和人工神经网络等相结合。

2．模拟退火算法

模拟退火算法最早的思想由Metropolis在1953年提出，Kirkpatrick在1983年成功地应用在组合优化问题上。模拟退火算法是局部搜索算法的扩展，它不同于局部搜索之处是以一定的概率选择邻域中较差的解。从理论上说，它是一个全局最优算法。

模拟退火算法来源于物理退火过程，主要包括如下三个阶段。

（1）加温阶段。

（2）等温阶段。

（3）冷却阶段。

模拟退火算法的基本思想：首先将固体加温至充分高，再让其徐徐冷却，加温时，固体内部粒子随温升变为无序状态，内能增大，而徐徐冷却时粒子渐趋有序，在每个温度都达到平衡态，最后在常温时达到基态，内能减为最小。根据Metropolis准则，粒子在温度T时趋于平衡的概率为
其中E为温度T时的内能，∆E为其改变量，k为Boltzmann常数。用固体退火模拟组合优化问题，将内能E模拟为目标函数值f，温度T演化成控制参数t，即得到解组合优化问题的模拟退火算法：由初始解i和控制参数初值t开始，对当前解重复“产生新解→计算目标函数差→接受或舍弃”的迭代，并逐步衰减t值，算法终止时的当前解即为所得近似最优解，这是基于蒙特卡罗迭代求解法的一种启发式随机搜索过程。退火过程由冷却进度表控制，包括控制参数的初值t及其衰减因子∆t、每个t值时的迭代次数L和停止条件S。

模拟退火的步骤如下。

（1）初始化。初始温度T（充分大），初始解状态S（是算法迭代的起点），每个T值的迭代次数L。

（2）对k＝1,…，L进行（3）～（6）步操作。

（3）产生新解S′。

（4）计算增量∆t′＝C（S′）－C（S），其中C（S）为评价函数。

（5）若∆t′＜0，则接受S′作为新的当前解；否则以概率exp（-∆t′/T）接受S′作为新的当前解。

（6）如果满足终止条件则输出当前解作为最优解，结束程序（终止条件通常取为连续若干个新解都没有被接受时终止算法）。

（7）T逐渐减少，且T趋于0，然后转步骤（2）。

模拟退火算法实质上是局部搜索算法的推广，因此它克服了局部搜索算法局部最优的缺点，但它同样也面临解的表示和邻域函数的设计问题，好的设计方法能使算法在可行解域内进行搜索，否则会扩大搜索空间，增加搜索时间。同时，模拟退火算法是一种通用的、高效的、健壮的和灵活的随机近似算法，并且可以较容易地并行实现以进一步提高运行效率，适合求解大规模组合优化问题，特别是NP完全问题，因此具有很大的实用价值。此外，对模拟退火算法做一些局部或策略上的修改，还可以得到一些推广或变异形式。

3．遗传算法

遗传算法由J.Holland教授于1975年首先提出，并逐渐发展起来。“适者生存”揭示了大自然生物进化过程中的一个规律—最适合自然环境的群体往往产生了更大的后代群体。遗传算法就是基于生物界的进化规律演化而来的随机化搜索方法，其思想源于生物遗传学和适者生存的自然规律，是具有“生存＋检测”的迭代过程的搜索算法。其中，选择、交叉和变异构成了遗传算法的基本操作。

（1）选择（也称为再生）。从群体中选择出较适应环境的个体。这些选中的个体用于繁殖下一代。

（2）交叉。在选中用于繁殖下一代的个体中，对两个不同个体的相同位置的基因进行交换，从而产生新的个体。

（3）变异。在选中的个体中，对个体中的某些基因执行异向转化。

遗传算法的主要步骤如下。

（1）初始化。选择一个群体，这个初始的群体也就是问题假设解的集合。

（2）选择。根据适者生存原则选择下一代的个体。在选择时，以适应度为选择原则。适应度准则体现了适者生存，不适应者淘汰的自然法则。

（3）交叉。对于选中用于繁殖下一代的个体，随机地选择两个个体的相同位置，按交叉概率P，在选中的位置实行交换。

（4）变异。根据生物遗传中基因变异的原理，以变异概率Pm
对某些个体的某些位执行变异。

（5）全局最优收敛。

当最优个体的适应度达到给定的阈值，或者最优个体的适应度和群体适应度不再上升时，则算法的迭代过程收敛、算法结束。否则，用经过选择、交叉、变异所得到的新一代群体取代上一代群体，并返回到步骤（2）即选择操作处继续循环执行。

遗传算法是一种随机的优化与搜索方法，具有并行性、通用性、全局最优性、健壮性、可操作性和简单性等特点。遗传算法不论是在应用、算法设计上，还是基础理论上都得到了长足的发展，已经成为信息科学、计算机科学、运筹学和应用数学等诸多学科共同关注的热点研究领域。

4．蚁群优化算法

1991年，意大利学者M.Dorigo等基于自然界蚁群觅食原理，首先提出了第一个蚁群算法的最早形式——蚂蚁系统（AS），并应用到TSP问题中。AS算法被提出之后，其应用范围逐渐广泛，算法本身也不断被完善和改进，形成了一系列的蚁群优化算法。蚁群优化算法是一种受自然界生物行为启发而产生的“自然”算法，产生于对蚁群行为的研究，是一种分布式的智能模拟算法。蚁群中的蚂蚁以信息素为媒介，间接异步地相互联系。这是蚁群算法的最大特点。蚂蚁在行动（寻找食物或者寻找回巢的路径）中，会在它们经过的地方留下一些化学物质，称之为“信息素”。这些物质能被同一蚁群中后来的蚂蚁感受到，并作为一种信号影响后者的行为，具体表现在后到的蚂蚁选择有这些物质的路径的可能性比选择没有这些物质的路径的可能性大得多。后到者留下的信息素会对原有的信息素进行加强，并循环下去。这样，经过蚂蚁越多的路径，后到蚂蚁选择这条路径的可能性就越大。由于在一定的时间内，越短的路径会被越多的蚂蚁访问，因而积累的信息素也就越多，在下一个时间内被其他的蚂蚁选中的可能性也就越大。这个过程会一直持续到所有的蚂蚁都走最短的那一条路径为止。

通俗地说，蚁群算法可以看作是三个过程的相互作用，即蚂蚁构建解、更新信息素和后台执行。

蚂蚁构建解通过使蚂蚁移动到相应构建图Gc上的邻近点，而使一群蚂蚁并行异步地访问所考虑问题的邻近状态。蚂蚁根据信息素和启发式信息，采用一个随机局部决策方法选择移动的下一步。这样，蚂蚁就可以逐步建立起优化问题的解。一旦蚂蚁建立了一个解，或者是在构建解的期间，蚂蚁将对部分解进行评估，以便在更新信息素过程使用该（部分）解来决定应释放信息素的多少。

更新信息素就是修改信息素浓度的大小。信息素的浓度可能会因蚂蚁在点或连接的边上释放信息素而增加，也可能会由于信息素的蒸发而减少。从实际的角度看，释放新的信息素增加了蚂蚁访问某个点或者某条连接边的概率，这些点（边）有可能已经有很多蚂蚁访问过，或者至少有一只蚂蚁访问过，并产生了好的解从而会吸引以后的蚂蚁重新访问。不同的是，信息素的蒸发所起到的遗忘作用是很有用的：它可以避免算法朝着一个并非最佳的解区域过早收敛，从而使算法有更多的机会探索搜索空间中的新区域。

后台执行的过程就是执行单一蚂蚁不能完成的集中行动。后台执行的例子有局部优化过程的执行和全局信息的收集，在非局部的情况下，这个全局信息可以用于决定释放某些额外的信息素来调整搜索过程。

作为一种新的启发式优化算法，蚁群算法引起了相关领域的关注。蚁群算法具有较强的健壮性、通用性、快速性、全局优化和并行搜索等优点，蚁群算法从本质上讲是一种模拟进化算法，它的产生与进化算法的发展息息相关。如果与群体搜索策略结合使用，并保证群体中个体之间的信息交换，则蚁群算法可体现进化计算的优越性。但蚁群算法还存在如下一些问题。

（1）从数学上对其正确性与可靠性进行证明比较困难。

（2）蚁群算法是一个专用算法，一般只能解决一个问题，各种算法之间的相似性很差。

（3）系统的高层次行为是通过低层次的昆虫（蚂蚁）之间的简单行为交互而产生的。将高层的复杂行为映射到低层次简单个体的简单行为是一件极其困难的事情。

（4）蚁群算法的搜索时间较长，在算法模型、收敛性以及理论依据上还有许多工作有待进一步深入研究。

5．人工神经网络

人类对人工神经网络的研究可以追溯到1943年，心理学家W.S.McCuloch和数理逻辑学家W.Pitts最早提出的人工神经网络模型—M-P模型，是第一个用数理语言描述人脑的信息处理过程的模型，从此开创了神经科学理论研究的新纪元。1969年，人工智能的创始人中M.Minsky和S.Papert出版了有较大影响的《感知器》一书，指出感知器功能上的局限性，该论点极大地影响了人工神经网络的研究，至此进入了人工神经网络发展史上长达10年的低潮期。进入20世纪80年代后，随着计算机科学、生物技术和光电技术等领域学科的迅速发展，人工神经网络的研究进入到了一个新的大发展阶段。1982年，美国生物学家、物理学家J.Hopfield提出了Hopfield网络模型；1985年，D.E.Rumelhart和J.LMcclelland提出了误差反向传播（BP）算法，成为迄今为止影响较大的一种网络学习方法。

人工神经网络模型主要考虑网络连接的拓扑结构、神经元的特征和学习规则等。目前，已有近40种神经网络模型，其中有反传网络、感知器、自组织映射、Hopfield网络、波耳兹曼机和适应谐振理论等。根据连接的拓扑结构，神经网络模型可以分为如下几种。

（1）前向网络。网络中各个神经元接受前一级的输入，并输出到下一级，网络中没有反馈，可以用一个有向无环图表示。这种网络实现信号从输入空间到输出空间的变换，它的信息处理能力来自于简单非线性函数的多次组合。网络结构简单，易于实现。反传网络是一种典型的前向网络。

（2）反馈网络。网络内神经元间有反馈，可以用一个无向的完备图表示。这种神经网络的信息处理是状态的变换，可以用动力学系统理论处理。系统的稳定性与联想记忆功能有密切关系。Hopfield网络、波耳兹曼机均属于这种类型。

学习是神经网络研究的一个重要内容，它的适应性是通过学习实现的。根据环境的变化，对权值进行调整，改善系统的行为。由Hebb提出的Hebb学习规则为神经网络的学习算法奠定了基础。Hebb规则认为学习过程最终发生在神经元之间的突触部位，突触的联系强度随着突触前后神经元的活动而变化。在此基础上，人们提出了各种学习规则和算法，以适应不同网络模型的需要。有效的学习算法，使得神经网络能够通过连接权值的调整，构造客观世界的内在表示，形成具有特色的信息处理方法，信息存储和处理体现在网络的连接中。

人工神经网络具有以分布方式存储知识、并行方式处理、较强的容错能力，并且它具有可以逼近任意的非线性函数以及有很强的自学习、自适应和联想记忆功能等特征，引起了众多研究人员的兴趣，并在相关领域取得了显著的进展。例如，自动化领域的系统识别和神经控制器以及智能检测，经济领域中的市场预测和信贷分析，工程领域中的汽车工程、军事工程、水利工程和化学工程，信息领域中的信号处理、模式识别、数据压缩，医学领域中的生物活动分析、医学专家系统等。

12.5　面向过程的程序设计与实现

C语言是面向过程程序设计中的典型语言，它提供了丰富的数据类型、运算符号和灵活的控制语句，熟悉C语言提供的数据类型、运算符号和语句是进行C程序设计的最基本要求。合理使用数据类型和控制结构对软件的可维护性、可扩展性起重要作用。

指针是C语言的精华部分，它极大地丰富了C语言的功能。通过利用指针，可以描述复杂的数据结构，编程时能很好地利用内存资源，使其发挥最大的效率。运用指针编程是C语言最主要的风格之一。

12.5.1　指针类型

1．变量和指针

在程序中定义或说明的变量，编译系统为其分配相应的内存单元，也就是说，每个变量都有具体的地址。简单而言，程序语言中的变量就是内存单元的抽象。变量具有类型、值、地址、作用域和生存期等属性。

变量的本质是程序中用来存放数据的一段存储空间，一般情况下，变量所对应的存储空间在内存区域，C语言中程序员可以通过关键字register声明变量的存储单元是CPU中的寄存器。

变量的数据类型不同，它所占的内存单元数也不相同。访问变量时，首先应找到其在内存的地址。如果在程序中将变量的地址保存在另一个变量中，则形成指针变量，通过指针对所指向变量的访问，是一种对变量的“间接访问”。

例如，下面定义两个变量：整型变量a和指针变量ptr，在存取变量a中的数据时，可通过变量a或指向变量a的指针来进行，分别称为直接访问和间接访问。

（1）直接访问变量a中的数据。

（2）间接访问变量a中的数据。

经上述定义和处理后，变量a和指针变量ptr之间的关系如图12-21所示。

图12-21　指针变量ptr与指针变量指向的对象*ptr（a）

若指针变量指向的对象仍然是一个指针变量，则称为多级指针。

例如，对于下面的变量定义，mulptr是指向指针变量ptr的变量，这些变量间的关系如图12-22所示。

图12-22　二级指针变量mulptr

根据应用的需要，可以采用三级或多级指针。当然，采用多级指针在带来灵活性的同时降低了对数据的访问效率。

2．通过指针访问数组中的元素

C程序中常利用指针对数组和字符串进行处理。一个数组由连续的一块内存单元组成，数组名就是这块连续内存单元的首地址（起始地址）。一个数组是由各个数组元素（通过下标区分）组成的，每个数组元素按其类型不同占有若干个连续的内存单元。

通过指针访问数组元素是指针变量的一种常见应用方式。

1）指针变量与一维数组

指针变量指向一维数组元素的情形较为简单。C语言中定义一个指向数组元素的指针变量的方法如下所示。例如，定义一个整型数组st和指向数组元素的指针ptr。

若ptr指向数组的第一个元素（即下标为0的元素），则*（p+i）指向数组的第i＋1个元素（即下标为i的元素）。

例如，设数组a的空间足够大，函数InsertElem（int a[], int n, int newElem）的功能是将newElem中的数值插入到元素已经按照非递减方式排序的数组a中，并保持数组a中数据的排序特性。其中对数组元素的访问采用指针方式。

2）指针变量与二维数组

指针变量指向二维数组元素的情形比较复杂，下面举例简单说明。

设有定义int a[3][4]，其元素布局如图12-23所示。

图12-23　二维数组a[3][4]元素布局

数组a[3][4]可看作是由3个一维数组（a[0]、a[1]、a[2]）构成的一维数组，每个一维数组的元素是4个。a是二维数组名，它代表整个二维数组的首地址。

a[0]是第0个一维数组的数组名和首地址。*（a+0）、*a和a[0]是等效的，它们都表示一维数组a[0]的0号元素的首地址。&a[0][0]表示a的第0行第0列元素的首地址。因此，a、a[0]、*（a+0）、*a和&a[0][0]所表示的内容相同。

同理，a[1]是第1个一维数组的数组名和首地址。将二维数组a[3][4]看作元素是一维数组的一维数组时，a是数组首地址，a+1则是1号元素的地址，也就是a[1]的地址，因此，a+1、a[1]、*（a+1）和&a[1][0]的含义相同，它们都表示数组a第1行中第0个元素的地址。

所以，对于二维数组a[M][N]，a+i、a[i]、*（a+i）和&a[i][0]是等同的。

把二维数组a[3][4]分解为一维数组a[0]、a[1]、a[2]之后，设p为指向二维数组的指针变量。可定义为：

因此，可令p＝a（等价于p＝a[0]）或p＝a[1]或p＝a[2]。若令p＝a[1]，则*（p+2）就表示数组元素a[1][2]，二维数组其他元素通过指针p的表示方式依此类推。

3．指针与函数

C程序中将指针与函数结合使用的常见方式有函数参数为指针、函数返回值为指针以及通过函数指针变量调用函数（函数指针变量）。

1）函数参数为指针

函数的参数不仅可以是整型、实型、字符型等基本数据类型，还可以是指针类型。参数使用指针类型的作用是将一个变量的地址传送到另一个函数中。

C语言中实参向形参传递值，反之则不行。

例如，定义函数swap_1（a,b）的功能是交换a和b的值，代码如下：

如果发生函数调用swap_1（x,y），则系统将x的值传给a、y的值传给b，在函数swap_1中实现了a和b的值交换，但是x和y的值并不发生交换。为实现将函数中对形参的修改结果返回给调用函数之处，可使用指针参数。

对于上例，定义函数swap_2如下：

当形式参数为指针类型时，传递给形参的应该是地址信息，因此调用swap_2的形式为swap_2（&x，&y）。这样通过“间接访问”，实现了在被调用函数中修改实参所对应的变量的内容。

通过指针参数也可以实现将被调用函数中的多个处理结果传回给调用函数的地方。

当函数的参数为数组时，实参和形参即可以用指针形式，也可以用数组形式，实参向形参传递的是数组空间的首地址。

2）函数返回值为指针

函数类型是指函数返回值的类型。在C语言中允许一个函数的返回值是一个指针（即地址），这种返回指针值的函数称为指针型函数。

需要注意的是，不能返回局部数据的指针。

例如，下面的函数get_str虽然返回了局部数组str的首地址，但调用get_str的其他函数并不能通过此地址访问字符串″testing local pointer″。

将上述函数get_str中的char str[]={″testing local pointer″}；改为char *str={″testing local pointer″}即可，或者从内存的堆区申请字符串的空间，然后返回首地址。

函数返回值为指针时，也可以实现将被调用函数中的多个处理结果传回给调用函数的地方。

3）指针变量

程序中的一个函数总是占用一段连续的内存区，而函数名就是该函数所占内存区的首地址。可以把函数的首地址（或称入口地址）赋给一个指针变量，使指针变量指向该函数，然后通过指针变量就可以找到并调用这个函数。指向函数的指针变量称为“函数指针变量”。

函数指针变量定义的一般形式为：

类型说明符　（*指针变量名）（）；

其中，“类型说明符”表示被指函数的返回值的类型；“（*指针变量名）”表示“*”后面的变量是指针变量；最后的一对空括号表示指针变量所指向的对象是一个函数。

例如，下面定义了一个函数指针变量funptr。

下面的程序中先定义了一个函数max（a,b），其功能是比较用a和b表示的两个整数并返回它们中的较大者，然后通过函数指针变量调用max。

既然函数指针变量是一个变量，当然也可以作为某个函数的参数来使用。例如，下面的程序中设计了一个testFunCall函数，该函数根据其函数指针参数值的不同分别调用函数fun1和函数fun2（注：fun1和fun2的定义格式应相同）。

12.5.2　指针与数据结构

“程序＝数据结构＋算法”常用来说明程序、数据结构（数据的存储结构）与算法之间的关系，指针在数据结构的设计和实现中有重要的作用。随着软件开发环境不断完善和程序语言的抽象程度不断提高，数据结构的内部设计细节被封装和屏蔽起来，在很多应用软件的开发中没有必要也不再涉及底层细节，但是在系统级程序设计或嵌入式应用的软件设计中，指针及相关机制的应用仍然是十分必要的。

1．单链表的实现和应用

在数据结构的设计中，指针是常用的工具。对于无法预先确定数据规模的应用，可采用动态存储的方法解决数据的存储问题，也就是链表结构。链表中的每个节点之间可以是地址不连续的（节点内是连续的），节点之间的联系用指针实现，实际上是在节点结构中定义一个成员项来存放下一节点的首地址，称为指针域。

C语言标准库中提供的用于申请动态存储空间的函数是malloc（）、calloc（）和realloc（），函数free（）用于释放由上述函数申请的内存空间。

栈是一种常用的数据结构，下面设计并实现一个用链表结构的栈，如图12-24所示（为了简化，栈中的元素类型设为int型）。

图12-24　栈的链式存储结构示意图

2．二叉链表和多叉链表的设计和应用

1）二叉树的存储结构设计

二叉树是一种重要的数据结构，采用二叉链表存储时，其节点的类型常定义为：

对于某些特殊的二叉树，如哈夫曼树（或称为最优二叉树），由于该类型二叉树中的内部节点是由初始的m个叶子节点根据一定的规则构成的，因此其节点总数总是2m－1，所以采用静态的二叉链表来表示，其类型定义如下：

以具有4个叶子节点（分别用a，b，c，d表示，权值为2，7，4，5）为例，采用哈夫曼算法构造的最优二叉树及其存储结构如图12-25所示。

图12-25　树及其孩子—兄弟表示示意图

2）其他树结构的存储结构设计

一般的树结构常采用孩子—兄弟表示法表示，即采用二叉链表作为树的存储结构，链表中节点的两个链域分别指向该节点的第一个孩子节点和下一个兄弟节点。节点类型定义如下：

例如，图12-26（a）所示树的孩子一兄弟表示如图12-26（b）所示。

图12-26　树及其孩子—兄弟表示示意图

显然，在物理上，表示一般二叉树的二叉链表与树的孩子一兄弟表示法的结构是相同的，其差别在于对指针所指对象的解释，也就是在逻辑上进行区分。

B树是适用于外部查找的平衡查找树，一个4阶的B树如图12-27所示。

图12-27　4阶B树示例

其存储结构的类型定义如下。

B树的阶M、bool类型、关键字类型及B树节点的定义如下：

3．其他链表的设计和应用

图也可以采用链接表存储结构，称为邻接表，其数据类型定义如下：

例如，某有向图（网）如图12-28（a）所示，其邻接表存储结构如图12-28（b）所示。

图12-28　图的邻接表存储结构设计

在散列技术中，可用拉链法将冲突的元素存储起来，实际上就是链表结构的一个应用。例如，可以设计散列桶结构的类型如下：

设散列函数为Hash（Key）=Key mod7，对于关键字序列15，14，21，87，96，293，35，24，149，19，63，16，103，77，5，153，145，356，51，68，705，453的存储结构如图12-29所示。

图12-29　散列桶结构示例

从上述内容可知，利用指针实现的链表结构有灵活多变的形式，可根据应用的需要为数据存储设计不同的结构。显然，数据的存储方式不同，体现相同算法思路的处理过程可能会有较大的差异。因此，算法的实现过程与数据结构的设计是密切相关的，在有些情况下，算法的实际效率会因为数据存储结构的不同而不同。

12.5.3　C语言实现面向对象设计思路

C语言是典型的过程式和结构化程序设计语言，由于支持位运算及内存地址操作，因此该语言适用于系统级和嵌入式软件设计。面向对象程序语言C++是在保持C语言效率的基础上扩充面向对象特性得到的。另一个面向对象程序语言Java虽然具备许多独立的特性，但它是在摒弃了多种语言的不足之处，从根本上解决了C++的固有缺陷后而开发的面向对象语言。Java对象其实是从C++中的对象和指针共同继承而来的。因此，在操作系统软件的底层部分常用C语言实现，而高层部分则采用面向对象程序设计语言实现。

用程式语言也可以在部分程度上实现面向对象的程序设计思想。例如，在一个简化的绘图程序中，支持点（point）和圆（circle）两种图形，在设计过程中采用面向对象思想，认为所有的点和圆都是一种图形（shape），并定义类型shape_t、point_t和circle_t分别表示基本图形、点和圆，点和圆自然具有基本图形的所有特征，它们的关系如图12-30所示。

图12-30　point_t、circle_t和shape_t的关系

下面的程序代码利用指针、可变数目参数机制实现以上设计，point_t和circle_t两种结构通过其成员shape_t common表示了图12-30中的继承关系；circle_t与point_t之间的引用关系用circle_t的数据成员point_t *center来表示。

C语言是一种基础性很强的通用程序设计语言，而程序设计方法是跨语言的，指针类型为实现C语言的强大功能和灵活性提供了保障，软件设计师应该熟悉C语言提供的各种机制，掌握用该语言实现的常用数据结构、算法并灵活应用。

12.6　面向对象的程序设计与实现

12.6.1　设计与实现方法

面向对象程序设计主要是根据问题的详细描述，设计出能够被迅速转换为面向对象程序实现的代码，相比本章的第12.3节，其设计与实现更为底层，更接近代码。但是，对面向对象设计结果的衡量没有统一的标准，因此很难衡量针对一个问题所设计的解决方案是否最优，这也正是软件工程领域内分析与设计的难点，更多的情况下，分析与设计的结果是一种经验的总结。

尽管不存在一致的对分析与设计结果的衡量标准，但许多面向对象和软件工程领域内的“大师”都根据自己长期的实践经验，总结出了面向对象分析与设计的原则，而这些原则可成为我们在对实际问题进行分析与设计时的指导准则。

一般而言，当面临一个具体的问题时，可分为两大阶段：首先根据问题进行设计，其次根据设计进行实现。由于面向对象的实现和面向对象设计之间不存在较大的差异，所不同的是设计更多采用的是UML的标准表示，而实现则是采用面向对象语言表达，因此解决问题的重点应放在面向对象的设计上。

目前，被公认的好的面向对象设计是由前人所总结的设计模式，因此，熟练并正确地掌握面向对象设计技术，必须很好地体会并理解常用的23种设计模式。当对23种设计模型运用时，必须做到以下几点。

（1）能够根据设计模式的名称画出其对应的类图。

（2）理解类图中每一个类的作用与功能。

（3）能够将现实问题中所描述的各种职责映射到类图中具体的类。

（4）能够使用一种面向对象语言实现设计。

下面通过具体的应用来说明这个过程。

12.6.2　设计模式的应用

1．问题说明

已知某类库开发商提供了一套类库，类库中定义了Application类和Document类，它们之间的关系如图12-31所示，其中，Application类表示应用程序自身，而Document类则表示应用程序打开的文档。Application类负责打开一个已有的以外部形式存储的文档，如一个文件，一旦从该文件中读出信息后，它就由一个Document对象表示。

图12-31　Application与Document关系图

当开发一个具体的应用程序时，开发者需要分别创建自己的Application和Document子类，例如图12-31中的类MyApplication和类MyDocument，并分别实现Application和Document类中的某些方法。

已知Application类中的openDocument方法采用了模板方法（Template Method）设计模式，该方法定义了打开文档的每一个主要步骤，如下所示。

（1）首先检查文档是否能够被打开，若不能打开，则给出出错信息并返回。

（2）创建文档对象。

（3）通过文档对象打开文档。

（4）通过文档对象读取文档信息。

（5）将文档对象加入到Application的文档对象集合中。

2．根据设计模式的名称画出其对应的类图

问题描述中已经给出了该设计采用的设计模式为模板方法，因此，首先给出模板方法的类图，如图12-32所示。

3．理解类图中每一个类的作用与功能

模板方法类图中，AbstractClass类定义了基本的操作PrimitiveOperation1（）和PrimitiveOperation2（），并在TemplateMethod（）方法中调用了这两个操作，但这两个操作都并未实现，而是留待子类去实现，从而达到模板方法的目的：定义一个操作中的算法的骨架，而将一些步骤延迟到子类中，使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

图12-32　模板方法类图

4．能够将现实问题中所描述的各种职责映射到类图中具体的类

在提出的具体问题中，已经告知了Application类中的openDocument方法采用了模板方法，而openDocument方法的步骤都已经给出，因此，很容易将Application类对应为模板方法类图中的AbstractClass类，openDocument方法映射为模板方法类图中的TemplateMethod方法，而主要步骤则对应为PrimitiveOperation1（）等基本操作。由此可推出，各种主要的步骤中，应该存在某些步骤是由Application的子类实现的。

5．能够使用一种面向对象语言实现设计

一旦分析清楚实际问题的设计结果和原始的设计模式类图中类的对应关系，便可采用一种面向对象语言实现。下面分别采用C++和Java语言实现给出的设计。

【C++代码】

【Java代码】