赞
踩
现在,计算机软件工业界面临着巨大的挑战:对于日益复杂的需求和运行环境,如何生产一个灵活、高效的软件系统。
随着软件系统的规模和复杂性的增加,软件结构的设计和规范变得越来越重要。对于一个小的程序,人们可以集中考虑其算法选择和数据结构的设计,以及使用哪一种的代码设计语言,可是,对于一个大的系统,软件的结构就显得更重要了,比如,系统由哪些组件(component)构成、组件间的关系如何、每个组件应完成的功能、组件运行的物理位置、如何通讯、同步/异步、数据访问等,这些内容就体现了软件系统结构的思想。
系统概念是系统理论的最基本的概念,它浓缩和概括了系统理论的最基本内容,然而,由于研究领域的不同、应用对象和理解角度的不同,对系统概念的定义也有不同,尚未有一个统一的定义。
我国著名科学家钱学森认为“系统即由相互作用和相互依赖的若干部分(要素)结合成的具有特定功能的有机的整体,而且这个系统本身又是它所从属的一个更大系统的组成部分”
以前,软件开发人员注意力主要集中在程序语言的层次上,现在,软件的代码和执行层次的问题已经得到很好的解决,而对结构级的理解一直都还停留在直觉和经验上,尽管一个软件系统通常都有文字和图表的说明,但所使用的句法和所表达语义的解释从来没有得到统一。而软件体系结构将在软件的较高层研究软件系统的部件组成和部件间的关系。
服务于软件开发的不同阶段,体系结构可分为
体系结构的重要性在于它决定一个系统的主体结构、基本功能和宏观特性,是整个软件设计成功的基础,其重要性表现为在项目的:
模块内聚性是指一个模块内相互作用的程度。
如果一个模块执行多个完全不相关的动作,那么这个模块就有偶然内聚性。例如,一个模块在一个列表中增加一个新的项或删除一个指定的项。【许多不同类的功能都写在一个模块里面】
具有偶然内聚性的模块有两个严重的缺点:一是在改正性维护和完善性维护方面,这些模块降低了产品的可维护性;二是这些模块不能重用。
造成的原因:“每个模块由35-50个可执行语句组成。”,将两个或多个不相关的小模块不得不组合在一起,从而产生一个具有偶然内聚性的大模块。另外,从管理角度认为在太大的模块内被分割出来的部分将来要组合在一起。
当一个模块执行一系列相关的动作,且其中一个动作是作为其他动作选择模块,称其有逻辑内聚性。例如,一个执行对主文件记录进行插入编辑、删除编辑和修改编辑操作的模块;【一类功能写在一个模块里面】
一个模块有逻辑内聚性会带来两个问题:其一,接口部分难于理解,其二,多个动作的代码可能会缠绕在一起,从而导致严重的维护问题,甚至难于在其他产品中重用这样的模块。
当一个模块执行在时间上相关的一系列动作时,称其具有暂时内聚性。例如,在一个执行销售管理的初始化模块中,其动作有:打开一个输入文件,一个输出文件、一个处理文件,初始化销售地区表,并读入输入文件的第一条记录和处理文件的第一条记录。这个模块的各个动作之间的相互关系是弱的,但是却与其他模块中的动作有更强的联系,就销售地区表来说,它在这个模块中初始化,而更新销售地区表和打印销售地区表这样动作是在其他模块中,因此,如果销售地区表的结构改变了,则若干模块将被改变,这样不仅可能有回归的错误(由于对产品的明显不相关的部分的改变而引起的错误),而且如果被影响的模块的数量很大时,则很可能会忽略一二个模块。而较好的作法是将有关销售地区表的所有操作放入一个模块。【一段时间内的功能写一个模块】
暂时内聚性的模块的缺点:一是降低了产品的可维护性;二是这些模块难以在其他产品中重用。
一个模块有过程内聚性,是指其执行与产品的执行序列相关的一系列动作。比如,从数据库中读出部分数据,然后更新日志文件中的维护记录。过程中的动作有一定的联系,但这种关系也还是弱的。【一个过程写一个模块】
这种模块比暂时内聚性模块有好些,但仍难以重用。
一个模块有通信内聚性,是指其执行与产品的执行序列相关的一系列动作,并且所有动作在相同数据上执行。【一个类写一个模块】
这种模块比过程内聚性模块有好些因为其动作有紧密的联系,但仍难以重用。
如果一个模块执行一系列动作,每一动作有自己的入口点,每一个动作有自己的代码,所有的动作在相同的数据结构上执行,这样的模块称其为信息内聚性模块。【一个接口写一个模块】
对于面向对象的范型来说,信息内聚性是最理想的。信息内聚性的模块本质上是抽象数据类型的实现,而对象本质上就是抽象数据类型的一个实例。
拥有抽象数据结构的优点。
一个只执行一个动作或只完成单个目标的模块有功能内聚性。【一个功能一个模块】
一个有功能内聚性的模块可能经常被重用。因为它执行的那个动作经常需要在其他产品中执行。一个经过适当设计、彻底测试的并且具有良好文档的内聚性模块对一个软件组织来说是一个有价值的资产,应该尽可能多地重用。
具有功能内聚性的模块也比较容易维护。首先,功能内聚有利于错误隔离;另外因为比普通的模块更容易理解从而简化了维护;同时还便于扩充(简单也抛弃旧模块用一个新模块来代替)。
模块耦合是指模块间的相互作用程度。
如果一个模块p直接引用另一个模块q的内容,则模块p和q是内容耦合的。比如,模块p分支转向模块q的局部标号。【两个模块代码杂糅】
在产品中,内容耦合是危险的,因为它们是不可分割的,模块q的改变,都需要对模块p进行相应的改变,并且,在新产品中,如果不重用模块q,则不可能重用模块p。
如果两个模块都能访问同一个全局变量,则称它们为共用耦合。比如模块a和模块b都需要访问x这个全局变量。【两个模块访问同一个全局变量】
缺点
如果一个模块传递一个控制元素给另一个模块,则称这两个模块是控制耦合的。即一个模块明确地控制另一个模块的逻辑。【一个模块的代码运行顺序由另一个模块控制】
控制耦合所带来的问题是:两个模块不是相互独立的——被调用模块必须知道模块p的内部结构和逻辑,降低了重用的可能性;另外,控制耦合一般是与逻辑内聚性的模块的有关,逻辑内聚性的问题也在控制耦合中出现。
如果把一个数据结构当作参数传递,而被调用的模块只在数据结构的个别元素上操作,则称两个模块是特征耦合的。【一个模块只需要另一个模块的部分参数】
导致数据无法控制。
如果两个模块的所有参数是同一类数据项,则称它们是数据耦合的,也一就是每一个参数要么是简单变量,要么是数据结构,而当是后者时,被调用的模块要使用这个数据结构中的所有元素。【一个模块需要零一个模块完整的参数】
重用是指利用一个产品的构件以使开发另一个具有不同性能的产品更容易。可重用的构件不一定是一个模块或一个代码框架,它可能是一个设计、一本手册的一部分、一组测试数据或最后期限和费用的估计。
而可移植的是指,为了使一个产品在另一个编译器/硬件/操作系统上运行,如果修改这个产品比从头编写代码更容易的话,那么这个产品就是可移植的。
计划重用比偶然重用有一个潜在的优点,这就是,专门为在将来的产品中使用而构造的那个构件更容易于重用,而且重用也更加安全。因为这样的构件一般都有健全的文档,并做过全面的测试,另外,它们通常有统一的风格,从而易于维护。
但另一方面在一个公司内实现计划的代价可能是很高的。对一个软件构件进行规格说明、设计、实现、测试和编制文档要花很多的时间,然而,这样一个构件是不是能重用,所投资的成本能否回收不无保证。
在计算机刚问世时,没有什么东西是可以重用的。每当开发一个产品时,所有项目都是从头开始构造的。然而不久为后,人们意识到这是相当大的工作浪费,于是人们构造了子程序库。这样,程序员在需要时就可直接调用这些以前编写好的例程,这些子程序库越来越成熟,并开始出现运行时的支持程序。
重用可以节省时间,缩短产品的开发期限同,使软件开发公司更有竞争力。开发人员既可以重用自己的例程,也可以重用各种类库或API,从而节省了大量的时间。相反,如果一个软件产品要花经费4年的时间才能进入市场,而一个竞争产品只用2年就交付使用,那么,不管它的质量有多高,它的销路也不会太好。开发过程的时间期限在市场经济中是至关重要的,如果产品在时间方面没有竞争优势,那么谈论怎样才能生产一个好的产品是不切实际的。
软件重用是一项诱人的技术。毕竟,如果可以重用一个现有的构件,就不必再设计、实现、测试这个构件了。
据统计,对于任意软件产品来说,平均只有15%是完全服务于原始的产品目的的,产品的另外85%在理论上可以进行标准化,并在将来的产品中重用。在实际中,通常只能达到40%的重用率并且利用重用来缩短开发周期的组织也很少。因为重用有许多的障碍。
在短短的几十年中,计算机技术成为了现代社会的高科技的核心,其中硬件的发展是其他领域不可比拟的。中央处理器功能、存储器的容量、集成工艺的提高、新兴材料的研制、网络等变革,使得计算机很快从实验室走向应用,进入各行各业。计算机是社会信息化的基础。
然而,在软件技术方面,虽然也以巨大的速度发展,但比起计算机的硬件发展,就是微不足道的了。特别是在应用领域,许多企事业单位、机关 团体中的计算机,其性能远远没有得到充分的发挥。
随着计算机硬件的飞速发展,对计算机软件的功能、结构和复杂性提出了更高的需求,在软件的设计中,软件的局部和整体系统的结构方面,已经越来越显出其重要性,甚至超过了软件算法和数据结构这些常规软件设计的概念。软件体系结构概念的提出和应用,说明了软件设计技术在高层次上的发展并走向成熟。
开发大型软件过程中﹐难以汇集参与人员的设计理念然后提供给使用者一致的设计概念(conceptual integrity)﹐因而导致软件的高度复杂性,使得大型软件系统往往会进度落后、成本暴涨及错误百出,就是所谓的软件危机(software crisis)。
人月神话——二十多年前(1975)﹐IBM大型电脑之父──Frederick P. Brooks 出版的一本书。
人月(man-month)":熟悉软件项目管理的人员都清楚,人们常常根据人月来估计工作量(并相应收费),比如一个项目五人两月完成,那么总工作量就是10人月。
称之为"神话"(Mythical),其用意也并非完全否定作为计量方法的人月,而是要理清这个概念中隐含的种种错觉。
文中论点主要包括:
著名的Brooks法则
Adding manpower to a late software project makes it later(对于进度已落后的软件开发计划而言﹐若再增加人力﹐只会让其更加落后。)
"人月"概念可以线性化的神话:无论是开发人员的人数上,还是工作量本身上的变化,都可能导致最终完成时间的非线性变化。
1986年,Brooks发表了一篇著名的论文──"No Silver Bullet: Essence and Accidents of Software Engineering"。他断言﹕在10年内无法找到解决软件危机的根本方法(银弹)(There will be no silver bullet within ten years)。
Brooks认为软体专家所找到的各种方法皆舍本逐末。解决不了软体的根本困难──即概念性结构(conceptual structure)的复杂,无法达到概念完整性。
软件开发的困难来自两个方面:本质的和偶然的。本质的困难是软件开发本身所固有的,无法用任何方式取消的,而偶然的困难是其中的非本质因素,可以通过引入新工具、方法论或管理模式来消除。关键在于,只要本质的困难在软件开发中消耗百分之十以上的工作量,则即使全部消除偶然困难也不可能使生产率提高10倍。
软件的本质
1990年﹐曾首先提出"Software IC"名词的OO大师──Brad Cox针对Brooks的观点而发表了一篇重要文章──"There Is a Silver Bullet"。说明他找到了尚方宝剑──即有些经济上的有利诱因会促使人类社会中的文化改变(culture change)。人们会乐于去制造类似硬体晶片(IC)般的软件组件(software component),将组件内的复杂结构包装得完美,使得组件简单易用。由这些组件整合而成的大型软件,自然简单易用。软件危机于是化解了。
桥墩有时会坍塌,但出现的次数远远小于操作系统崩溃的次数。
两种故障的主要区别:土木工程领域和软件领域对崩溃事件的理解态度不同。当桥墩坍塌时人们几乎总是要对桥墩进行重新设计和重新建造,因为桥墩的坍塌说明该桥的最初设计有问题,这将威胁行人的安全,所以必须对设计作大幅度的改动,此外,桥墩坍塌后,几乎桥的所有结构被毁掉,所以唯一合理的做法是将桥墩残留的部分拆除再重新建造。更进一步地,其他所有相同设计的桥都必须仔细考虑,在最坏的情况下,要拆除重新建造。相比之下,操作系统的一次崩溃很少被认真考虑,人们很少立即对它的设计进行考察。当出现操作系统崩溃时,人们很可能只会重新启动系统,希望引起崩溃的环境设计不再重现。在多数情况下,没有去分析关于崩溃原因的证据,而且操作系统的崩溃引起的破坏通常中微不足道的。
也许当软件工程师们以土木工程师们对待桥墩坍塌那样认真的态度来对待操作系统故障时,这种区别就会缩小。当然,人类关于桥梁的设计毕竟经历了几千年的历史,而设计操作系统的经历只不过短短50多年。随着经验的积累,将一定会像理解桥一样充分地理解操作系统,构造出无故障的操作系统。
软件工程是以软件系统为对象,合理地、创造性地采用软件系统所需的思想、理论、技术、方法对软系统进行研究分析、设计开发和服务,使软件最大限度地满足需求。
软件工程的生命周期
一个好的结构设计是决定一个软件系统成功的主要因素。
体系统结构的研究现状其现状具体表现在:
目前,软件体系结构已经成为软件工程的从业者一个重要研究领域。
在许多工程学科,设计模式、风格的利用是非常普遍的。事实上,衡量一个工程领域是否成熟的一个重要指标,是看该工程领域是否就建就一套共享的通用设计形式。在软件上也是这样。在结构上,与之联系的有,客户/服务器系统(client-server system,C/S)、管道/过滤器设计(pipe-filer design,P/F)、层次结构(layered architecture)、在设计方法上的面向对象(object-oriented)、数据流(dataflow)等。
在软件设计的实践中,人们发现某些特殊组织结构(如C/S结构,B/S结构、P/F结构)在许多的软件系统中频繁出现,这些特殊结构有很高的应用价值。于是人们就设想能否将这些特殊结构进一步发展规范,使它们成为一种相对固定的设计结构,并应用于新软件产品。
关于一个软件系统的结构风格定义为组织该系统可用的各种结构模式。具体地说,就是系统的组件(component)、连接器(connector),以及它们的组合的约束条件(constraint)。
一个软件系统的结构包括构成系统的所有计算组件(computational component)、组件间相互作用(interacton)即连接器(connector)。用数学中的图的概念描述,一个系统结构图就是由节点(node)和边(edge)组成,这里节点代表组件,边表示组件间的连接器。
优点
缺点
抽象数据类型概念对软件系统有着重要作用,目前软件界已普遍转向使用面向对象系统。这种风格建立在数据抽象和面向对象的基础上,数据以及施加于它们之上的操作封装在一个抽象数据类型或对象中。这种风格的系统中组件(component)是对象(object),或者说是抽象数据类型的实例。对象是一种被称作管理者(manager)的组件,因为它负责资源的表示(representation)。对象间的相互作用是通过函数(founction)和过程(procedure)调用(invocation)实现。连接器(connector)则是激活对象方法的消息。
优点
缺点
在面向对象的系统中,组件是一组对象,组件间的相互关系是通过直接的过程调用实现,这种方法的不足是组件(对象)标识必须是公开的,众所周知(well-known)的。因此,人们考虑是否可以隐含激活(implicit invocation)或作用集成(reactive integration)。
在一个基于事件的隐含激活系统中,组件不直接调用一个过程,而是触发(announce)或广播(broadcast)一个或多个事件(event)。系统中的组件事先注册(register)它们感兴趣的事件及对应的过程,将来,当一个事件被触发(inovaation),系统自动调用在这个事件中注册的所有过程,这样,一个事件的触发就导致了另一模块中的过程的调用。
从体系结构上说,这种隐含激活风格的组件是一些模块,这些模块是一些过程和一些事件的集合。过程可以用通常的方式调用,也可以在系统事件中注册一些过程,当发生这些事件时,过程被隐含调用。
基于事件的隐含激活风格的主要特点是事件的触发者并不知道哪些组件会被这些事件影响。这样不能假定组件的处理顺序,甚至不知道哪些过程会被调用,因此,许多隐含调用的系统也包含显式调用作为组件交互的补充形式。
支持基于事件的隐含激活的应用系统很多。例如,在数据库管理系统中确保数据的一致性(consistency)约束,在用户界面系统中管理数据,以及在编辑器中支持语法检查。例如在编程环境中,编辑器和变量监视器可以登记相应Debugger的断点(breakpoint)事件。当Debugger在断点处停下时,它声明该事件,由系统自动调用处理程序,程序代码自动滚动(scroll)到到断点,变量监视器刷新变量数值。而Debugger本身只声明事件,并不关心哪些过程会启动,也不关心这些过程做什么处理。
优点
缺点
层次系统组织成一个层次结构,每一层为上层提供若干服务(service),并作为下层的客户(client)。在一些层次系统中,除了一些精心挑选的输出函数外,内部的层只对相邻的层可见。
这样的系统中组件在一些层实现了虚拟机(在另一些层次系统中层是部分不透明的)。连接器通过决定层间如何交互的协议来定义,拓扑约束限制了对相邻层间交互。
层次系统(Layers Systems)最广泛的应用是分层通信协议(如,OSI/RM)。在这一应用领域中,每一层提供一个抽象的功能,作为上层通信的基础。较低的层次定义低层的交互,最低层通常只定义硬件物理连接。
优点
缺点
黑板系统主要由三部分组成
黑板系统的传统应用是信号处理领域,如语音和模式识别(speech and pattern recognition)。另一应用是松耦合代理(loosely coupled agent)数据共享存取。一些管道/过滤器系统(如编译器)也可以设计为黑板系统。另个黑板系统也是人工智能广泛使用的系统结构。
解释器(interpreters)广泛用于建立虚拟机(virtual machine),以减少程序的语义所期望的计算机器与硬件所提供的有效计算机器的差距。【JVM-java语言虚拟机】
一个解释器系统由一个程序伪代码(pseudoprogram)和解释机(interpretation engine)。其中程序伪代码包含程序代码和执行的解释中间代码,解释机包含解释器的定义和执行的当前状态的定义。
解释器的由个部件组成
KWIC-Key Word Index in Contex(关键字索引),由Parnas在1972年提出的,问题描述如下:
The KWIC [Key Word in Context] index system accepts an ordered set of lines; each line is an ordered set of words, and each word is an ordered set of characters. Any line may be “circularly shifted” by repeatedly removing the first word and appending it at the end of the line. The KWIC index system outputs a list of all circular shifts of all lines in alphabetical order.
KWIC[上下文中的关键字]索引系统接受一组有序的行;每一行都是一组有序的单词,每一个单词都是一个有序的字符集。任何一行都可以通过重复删除第一个单词并将其附加在行的末尾来“循环移位”。KWIC索引系统按字母顺序输出所有行的所有循环移位列表。
系统由一个主程序(master control)和四个子过程:input、shift、alphabetize和output组成。
四个子过程在主程序的控制下依次执行,子过程间的数据传递是通过共享的存储区(磁盘)。
在这种方法中,子程序间共享存储区域,使得数据的表示效率高,节省了存储空间;但这也带来一些不足,数据结构的改变将影响到所有的四个子过程,不支持重用(reuse)。
系统同样由五个模块组成。
但模块间不再直接共享数据存储区,每个模块定义了一些方法,供其他模块通过激活方法调用。
在这种方法中,算法和数据表示封装在模块内,对它们的修改不影响其他模块;支持重用。但不适合于功能的扩充,当增加一个新功能时,必须修改现有的模块。
组件:系统由多个可以独立运行机制的部件组成,每个部件称为组件;同时系统由多台机器运行这些组件,这些机器共同协作完成系统的所有功能。
透明性:系统中的每个组件不需要关心其他组件的运行位置、实现方式及数量。
可靠性:当系统中的一个或少数几个组件出现故障(关闭或崩溃)时,系统仍然可以工作,所出现的故障造成的影响多只是系统运行速度。
注:3.1.2 小节来自CSDN文章《分布式优缺点》
层次结构:组件的一种组织方式。
技术基础:进程通信
RPC:进程通信的发展
组件技术:RPC的发展,如CORBA、J2EE等
云计算技术:分布式处理技术的最新发展。
区块链:【具体内容在第十讲】
一个常见的分布式系统的例子
某学校的网络中,每台工作站都可以独立工作,我们将网络中的各个工作站的CPU组成一个CPU池,每个CPU并不指定分配某个工作站的用户而是可以根据需要动态地将某个CPU分配给某个用户。在这个网络中,也只有一个文件系统,所有机器都可以用同一方式和同样的路径来访问系统中的所有文件。比如一个用户在自己的工作终端上输入一个命令,系统将找到一个最佳的位置为其运行命令,可能是在自己的工作站上,也可能是在某一台机器上暂时没有工作的CPU上执行,或者是系统中某个尚未分配出去的CPU上执行。如果这个系统看上去像一个整体,而又像单处理器系统那样工作,我们就可称之为分布式系统。
尽管有这些潜在的问题,人们还是觉得利大于弊,并预计分布式系统将越来重要,事实上,不久的将来,有更多的组织、单位要求连接自己的机器构成一个大的分布式系统,提供更好、更经济、更方便的服务。一个独立的计算机系统在中、大型企业、组织将不再存在。
在一个信息处理系统中,通常由若干台计算机组成。其中用于提供数据和服务的机器,称为服务器(Server),向服务器提出请求数据和服务的计算机称为客户(Client),这样的系统工作模型称为客户/服务器(Client/Server)模型,简称C/S模型。
在广义上说,客户、服务器也可以是进程。因此,C/S模型可以在单处理器的计算机系统中实现。
在多计算机或多任务的单处理器系统中,C/S模型有多种组织方式:单客户单服务器、单客户多服务器、多客户单服务器、多客户多服务器等。
#define MAX_PATH 255 #define BUF_ZISE 1024 #define FILE_SERVER 243 #define CREATE 1 #define READ 2 #define WRITE 3 #define DELETE 4 #define OK 0 #define E_BAD_OPCODE -1 #define E_BAD_PARAM -2 #define E_IO -3 struct message{ long source; long dest; long opcode; long count; long offset; long result; char name[MAX_PATH]; char data[BUF_SIZE]; };
#include <header.h> int main() { struct message m1,m2; int r; while (1) { receive (FILE_SERVER, &m1); switch (m1.opcode) { case CREATE: r=do_create(&m1,&m2); break; case READ: r=do_read(&m1,&m2); break; case WRITE: r=do_write(&m1,&m2); break; case DELETE: r=do_delete(&m1,&m2); break; default: } m2.result=r; send (m1.source, &m2); } }
long copy(char *src,char *dst) { struct message m1; long position; long client = 110; initialize(); position=0; do { m1.opcode=READ; m1.offset=position; m1.source=client; m1.count=BUF_SIZE; strcpy(m1.name,src); send(FILE_SRVER,&m1); receice(client,&m1); if(m1.result<=0) break; m1.opcode=WRITE; m1.offset=position; m1.count=m1.result; m1.source=client; strcpy(m1.name,dst); send(FILE_SERVER,&m1); receive(client,&m1); position+=m1.result; } while(m1.result>0); return(m1.result>=0?OK:m1.result); }
格式:send(dest;&mptr)
参数:dest——把消息发送具有dest标识符的进程,mptr——表示要发送的消息的地址。
作用:将指定的消息发送到接收进程
格式:receive(addr;&mptr)
参数:addr——等待消息到达的接收进程的地址,mptr——指针,表标消息到后存放何处。
作用:用地址addr侦听,所需的消息。
阻塞操作也称同步操作(synchronous primitive)
特点
非阻塞操作也称异步操作(asynchronous primitive)
非阻塞操作的特点
非阻塞操作的两种改进方法
系统提供一个消息缓冲区,用于存放到来的消息。接收进程调用receive(addr,&mptr)时,机器用地址addr在网上侦听,当消息到来后,内核将它拷贝到缓冲区,并将唤醒接收进程。
一个希望接收消息的进程要告诉内核为其创建一个信箱,并在网络消息包中指定一个可供查询的地址,然后,包含这个地址的所有到达的消息都被放进信箱。现在调用receive就可以从信箱中取出一个消息,如果没有消息就阻塞(假定用阻塞操作)。
特点
不可靠send操作:系统不保证消息能被对方正确接收,要想实现可靠的的通信,由用户自己完成。
(1984年,Birrell,Nelson)允许程序调用位于其他机器上的过程,当机器A上的一个进程调用机器B上的过程时,在A上调用进程被挂起,在B上执行被调用过程,过程所需的参数以消息的形式从调用进程传送到被调用过程,被调用过程处理的结果也以消息的形式返回给调用进程。而对程序员来说,根本没有看出消息传递过程和I/O处理过程,这种方式称为远程过程调用(remote procedure call——RPC)。
传统的过程调用
count = read(fd, buf, nbytes)
操作系统隐藏了具体的写数据,对程序员来说也看不到这一过程。
参数传递的方式:
客户——调用进程所在的机器
服务器——被调用过程所在的机器
RPC透明性的思想使得远程过程调用尽可能象本地调用(内部过程调用)一样,即调用进程不关心也不必知道被调用过程的位置(在本地或远程另一台计算机);反过来也是如此,被调用过程也不关心是哪台机器上的进程调用,只要参数符合要求即可。
RPC的工作步骤
客户代理的功能之一是取出参数,将它们封装成消息,然后发送给服务器代理。表面上看,好像很简单,但实现起来并不是如此。下面我们将讨论RPC系统中参数传递的有关问题。
解决方案
问题的提出:客户端如何定位到服务器
方法:动态联编
联编(binder)——一个程序,功能:
服务器(export):启动时 register,unregister
客户:lookup
动态性:
联编表(list)
name | version | handle | unique id |
---|---|---|---|
read | 3.1 | 1 | 1 |
write | 3.1 | 1 | 2 |
close | 3.1 | 1 | 3 |
动态联编的灵活性(flexible)
动态联编的缺点(disadvantages)
RPC的设计目标是隐藏通信过程,使得远程过程调用像本地调用一样,但也有一些另外,如不能处理全局变量,事实上,只要客户和服务器能正常工作,RPC就应该可以正常工作。下面的问题是当客户或服务器出错误时的处理方法。
RPC可能出现的错误及处理方法
在单一建筑物和校园内使用的分布式系统可以采用面向连接的协议。
要使用自定义的RPC协议就得完全自己设计一些分布式系统使用IP(or UDP)作为基本协议,原因是:IP/UFP协议已经设计,可节约相当可观的工作消息包可在几乎所有的Unix系统中传送和接收IP/UDP消息支持许多现存的网络总之,IP/UDP很容易使用,并且适合现存的Unix系统和网络关于性能,IP是无连接的协议,在它之上可建立起可靠的TCP协议(面向连接的),IP协议支持网关的报文分组,使报文从一介网络进入另一个网络(物理网络)。
通常网络接口芯片可以接收连续不断到来的包,但是由于芯片中缓冲区的限制,总是对连续到来的包的个数有个数量限制"超载"(overrun):一个消息到来而接收它的机器无法接收,以至于到来的消息丢失。超载是一个很严重的问题,它比因噪声等其他破坏引来的包丢失普遍得多。
上面两种确认机制对超载问题有所不同
图中"ACK"包丢失,造成的问题。
解决:对确认(ACK)进行确认(ACK),“超时确认”。
分布式系统是否成功依赖于它的性能的好坏,而系统性能的好坏又依赖于它的通信速度,通信速度与系统的具体实现有关,下面我们进一步讨论从客户到服务器执行一个RPC的过程。
临界路径(critical path):每个RPC的指令执行的顺序是从客户调用客户代理,自陷进入内核,消息传送,服务器中断,服务器代理,最后到达进行请求处理并返回应答的服务器。RPC的这一系列步骤称为(从客户到服务器的)临界路径。临界路径(critical path)图示
原则上,启动计时器不属于计算RPC时间的部分,启动计时机后,系统有两种方式:忙等待和重新调度。
在服务器端,当字节到达后,被存入板上缓冲区或主存,当消息包的所有字节都到达后,服务器将形成一个中断。
检查消息包的有效性,并决定将其送给一个代理,若没有等待的代理则放弃或保存至缓冲区。
假定有一个等待的代理,那么,消息被拷贝到代理并切换到服务器代理,恢复寄存器及主存映像,代理调用receive原语,分解参数,建立服务器的调用环境,进行请求处理。
临界路径(critical path)的开销-拷贝
在考虑临界路径的时间开销问题时,其中最重大的部分是拷贝
所有的协议都是以通过通信介质交换消息为目的的,而事实上,所有的系统中,消息都有可能丢失,或许是因为噪声或是超载。所以,多数协议都设置定时器,当消息发送出去后,期待应答(确认)的到来,若没有消息到来而定时器期满(expiration),则重发。这个过程可以重复几直到发送方放弃。
设置定时器:建立一个数据结构来指定定时器何时期满,以及期满时如何处理。
多个进程的定时器组织成列表方式:
当一个确认或应答在超时之前到来,列表中查出对应进程所在的项,将它删除,实际上,只有少数的进程可能超时,但每一个进程都要进入超时表后再删除,这些大量的工作多数是多余的。另外,定时器也不需要精确的时间,但定时太短引起过多的超时,定时太长则对包丢失的情况又过多的等待。
实现方法:
在PCB中增加一个字段,称为定时器,当发出一个RPC时,将允许延迟的时间加上当前的时钟的值并存于PCB中定时器字段,如果不需要超时重传的,其值规定为0,这样,内核定期扫描PCB链表,如果定时器值非0且小于当前时间,则该进程超时。
RPC结构性好,使用方便,消息传递通信更灵活,但结构性差。RPC只有一个返回,而消息传递通信可以向多个客户返回。RPC返回的结果或参数的值最好是少量的,消息传递通信可适合于大批量数据的传递。
时间在同步中起重要的作用,首先,我们来看分布式系统中时间的度量(measured)。
几乎所有的计算机是使用电路来记录时间,尽管普遍使用时钟这个概念来表示这些设备,但它们并不是真正意义是的时钟(clock),更确切地说,计算机中使用的只是计时器(timer),由有规律振荡的晶体来产生。因为加工工艺等原因,不可能有两个晶体时钟是一致,从而不同的计算机其时钟不可能完全一样。
假使两台或几台机器时钟可以非常精确,但大型分布式系统允许两台机器仅次于地球的两个时区上,这样它们的时钟仍是不一致的。
因此,在分布式系统中,需要考虑另一种时间的度量,即逻辑时钟。在这方面有重大贡献的是Lamport,他在1978年提出时钟同步是可能的,并给出一些算法,这些观点为分布式系统奠定了重要的理论基础。
Lamport指出:时钟同步不需要绝对的(精确时间),如果进程间没有相互作用,就不需要时钟同步。并且进程所关心的不是什么时间做什么工作(由于进程的运行过程由多种因素决定,有不确定性),它们所关心的是事件发生的顺序。
思想:
Lamport算法-逻辑时钟修改
个人理解:
【在分布式系统中最直接的方法是模仿单处理器系统中的作法实现互斥】
基本思想
指定一个进程为协调者(Coordinator),当一个进程要进入临界区时,它发送一个请求消息给协调者进程表示它的要进入临界区并要求给一个许可(permission),如果当前没有进程在临界区执行,协调者进程返回一介许可的应答,当许可的应答收到时,要求进程则可以进入执行,当它的临界区执行完成时,再发送一个要求释放临界区的消息。【向协调者申请许可,谁有许可谁进入临界区,退出临界区,归还许可】
优点
缺点
基本思想
当一个进程要进入临界区时,它建立一个包含它要进入的临界区名、进程号、和当前时间戳(timestamp),并把消息发送给所有的进程,理论上也包括它自己,消息传送是可靠的,每个消息要求有确认,如果网络支持可靠的组通信,则可用于代替单个的通信。
当一个进程收到另一个进程的一个请求时,它采取的工作依赖于消息中临界区名,这有三种情况:
进程在发送了请求进入临界区的请求消息后,就等待其他进程的许可(OK)消息,一旦它得到所有进程的许可(OK),就可以进入临界区执行。
进程退出临界区时,向在该进程的请求队列中的所有进程发送OK消息,并从队列中删除这些进程的请求消息。【告诉请求队列的进程我退出临界区】
优点:
缺点:
基本思想
【所有进程围成一个圆,令牌在圆中依次传递,在谁手上谁可以访问临界区,不需要访问临界区或者退出临界区,则把令牌给下一个进程】
将系统中所有进程建立一个逻辑环(logical ring),环中进程的顺序可以按进程地址排序或其他方法排序,只要环中一进程能知道它的下一个进程是谁即可。
环被初始化后,进程0得到一个令牌(Token),令牌沿环逐个下传,从k到中k+1(当k+1为环中进程时取0)。
当一个进程从它的上一个得到令牌时, 如果它要进入临界区,则就可以进入执行,临界区执行完成后,再把令牌传给下一个。
每获得一个令牌,至多只能执行一个临界区。
优点
缺点
进/出临界区花费的代价 | 进入临界区等待时间 | 问题 | |
---|---|---|---|
集中式算法 | 3 | 2 | 协调者奔溃 |
RA算法(分布式算法) | 2 (n-1) | 2 (n-1) | 任一进程奔溃 |
令牌环算法 | 1 — ∞ \infin ∞ | 0 — (n-1) | 令牌丢失、任一进程奔溃 |
次数说明:
【3次 —— 申请许可、给予许可、返回确认】【2次 —— 申请许可、收到许可】
【2(n-1) —— 向n-1个进程发送请求,接收n-1个进程的ok消息,一共2(n-1)】
【1 —
∞
\infin
∞ —— 1 表示令牌传递需要一次,无穷 表示 即使当前所有进程都没有申请进入临界区运行,令牌也要沿环不断地传递;因此,当一个进程要进入临界区执行是,令牌消息传递的个数是不确定的,可以有任意多次,即 无穷。】【0 — (n-1) —— 0表示令牌刚好在手上,n-1 表示令牌刚好传递给下一个进程】
在分布式系统的许多算法中都要有一个进程充当协调者、发起者、序列生成器、或其他特殊的角色,如集中式(互斥)算法中的协调者进程。通常,由哪个进程充当这个角色均可,但总得有一个进程来承担。这一节介绍选择这种协调者的算法即选择算法。
几个假定
Bull算法
当某个进程发现协调者(Coordinator)无法响应它的请求时,该进程就启动一个选择过程。进程P,执行的选择过程如下:
任何时刻进程都可能收到进程号(优先级)比它小的进程发送来的Election消息,该消息到达时,消息的接收者就向发送者发送一个OK消息,说明它仍是活动进程,而且它将接管选择过程。接收者接管选择过程,除非它已经接管一个选择;最后,其他进程都放弃只剩一个进程时,该进程成为新的协调者,然后它向所有的进程发送消息,通知这些进程从此刻起,它是新的协调者。
环算法(Ring Algorithm)
对所有进程进行物理或逻辑的排序,这样每个进程都知道它的下一个进程是谁。
任何一个进程发现协调者不起作用时,就创建一个包含该进程号(优先级)的Election消息,并将该消息发送给它的下一个(即后继)进程;
如果后继进程已经中止,就跳过它发送给下一个,依此类推,直到找到一个活动进程为止;
每个进程收到Election消息时,就将自己的进程号(和优先级)加入到Election消息的进程表中;
最后Election消息会回到创建它的进程(通过判断进程表中是否含的自己的进程号),它从进程表中选择最大进程号(或优先级)的进程作为新的协调者,并构造一个Coordinator消息通知所有其他进程。
如果系统允许有k个组件出错,而仍能正常工作,这种容错技术称为k-容错技术。那么,Fail-silent faults【非拜占庭失败】须提供k+1个备份。Byzantine Faults则须提供2k+1个备份。
“两支军队”问题(two-army problem)
【说明即使两个处理器是正常的,但它们要达成一致的意见是困难的。】
一支红色军队,5000人,驻在山谷。两支蓝色的军队,每支3000人,驻扎在山角监视着山谷。两支蓝色的军队只有同时向红色军队发动进攻,才能获胜,任何一支单独出击都将失败。现在,两支蓝色军队的目标就是对进攻的时间达成一致的意见,然而他们的协同只能通过互派信使来交换意见,但信使随时可能被俘虏。
假设一支蓝色军队的将军Alxander,决定在第二天凌晨2:00进攻,并派出一个信使将这个决定通知另一支蓝色军队的将军Bonaparte,之后就下达自己的军队,准备第二天凌晨2:00进攻;
然而,到了第二于凌晨,Alexander担心,Bonaparte不知道信使已经安全返回,可能不会轻易进攻,结果,Alexander又派一个信使把确认的消息传给Bonaparte;假如信使也安全到达Bonaparte,这时,轮到Bonparte担心,Alexander不知道确认是否安全到达,可能不会按时进攻。这样Bonaparte只好也派一个信使将确认通知Alexander。
这样,两个将军将来回确认,永远无法到成协议。因为最后一个发送消息的人不知道消息为对方接收,如果最后一个消息丢失,则所有协议都无效,无法进攻,并进入死循环。
问题是:通信是不可靠的,下列我们假设通信是完善可靠的,仅仅是处理器可能出错。
Byzantine generals problem 【拜占庭失败问题】
一支红色军队安营在山谷中,有n个将军率领n支蓝色军队扎营在山下周围,他们可以点对点完善地通信。假设其中有m(m<n)个将军可能是是背叛者(出错),并且阻碍军队达成一致的意见(修改消息),现在问题是正规军(非背叛的)能否达成协同?
不失一般性,我们把这里的协同的含义稍作修改,即假设每个将军都掌握自己的军队的力量,问题的目标是各个将军能交换他们的部队力量,这样,算法结束时,每个将军就有一个向量表示盟军的力量, 如果将军是正规军,用i表示他的军队的力量,否则没有定义。
现在来看另一个例子
1982年,Lamport证明在一个系统中,有m个出错的处理器时,协同形成需要2m+1个正常工作的处理器,即总数是3m+1。协同形成需要约三分之二的人通过。
表决技术(votings)
客户读或写一个备份文件的操作前,先向所有的服务器发送请求,并得到多数服务器的许可后才能进行。
设备份服务器有N个,并规定客户对备份文件的更新要经多数服务器的许可,即至少(N+1)/2个服务器的许可后,存取工作才可以进行,复制文件并形成新的版本(具有一版本号,对新修改的备份文件其版本号应该是相同的。)
“许可”,可以是一个含有版本号的应答消息。
这样,读一个备份文件的步骤是:
例如,有5个服务器,某客户得到3个服务器的版本号是8,则其余的两个服务器的版本号不可能是9,因为版本号从8到9要得到半数以上的服务器的许多,而不是2个。
表决算法(voting algorithm)
某文件系统有N个备份服务器,定义Nr和Nw分别表示读法定人数(read quorum)和写法定人数(write quorum):
例子
N=12,Nr=3,Nw=10
N=12,Nr=7,Nw=6
N=12,Nr=1,Nw=12
特别地,Nr=1,Nw=12,意味着写操作要求所有的服务器表决,则读操作可以从中任选一个。
说明
进程的有效细化,是进程内可独立执行(调度)的实体。
当一个客户发送的一个请求给服务器时,服务器执行请求,返回结果,然后删除该请求的有关所有信息,这样的一个服务器称为无状态信息的。
有状态信息服务器(Stateful information server):文件服务器拥有打开、关闭、读、写等操作,当一个文件被打开后,服务器将保存是哪一个客户打开的,打开的是哪一个文件,并生成一个文件描述符(file discroptor)。
一个客户如果已经打开了一个文件,那么它的后续操作,只要给出文件描述符及有关的参数即可,服务器收到请求后,根据文件描述符就知道哪一个文件。
状态信息(Stateful information)是一个映射表,文件描述符映射到它的文件。
对于无状态服务器
对于有状态信息服务器
【层次结构是解决复杂问题时最常用的软件结构】
【研究表明,任何软件的完整结构都具有层次关系】
N层结构——C/S或B/S结构称为2层结构,我们把层次结构中3层或3层以上统称为N层结构。
一个产品的软件体系结构风格如果采用N层结构。则可能按下面的步骤实施:
例如:
Window NT的层次结构
优点
缺点
【划分UIC和UIPC,进一步抽象,提高代码的重用性和降低模块的耦合度】
【UI与UI之间的调用的代码不要写在UIC中,由UIP处理,提高重用性】
优点
Helper的作用
数据源:关系数据库、文件系统、Exchange Server、Web Storage等
解决分布式系统的应用程序开发问题的两条规则
这两条规则不仅用于分布式系统,对于开发任何一个可移植的应用程序都是是适用的。使用合适的抽象和模型建立一个能提供异构的应用程序开发层(分布计算环境),系统异构的复杂性集中在这个层次,在这一层次上,低层的细节被隐藏起来,并且允许应用程序的开发人员只解决他们自己的开发问题,而不必面对需求所涉及到的、由于不同计算机平台所带来的低层的网络细节。
对象管理组(OMG)
对象管理组(Object Management Group),是一个非赢利的组织,建立于1989年4月,总部在美国,起由11个公司参与组建,现在拥有800多个成员,目前它是世界上最大的软件团体,其目标就是解决异构系统的可移植、分布式应用程序的开发问题、制定的技术对一些具体的问题作了合理高层抽象,并隐藏低层细节。
OMG使用两个相关的模型来描述如何与平台无关的分布式体系结构
CORBA(Common Object Request Broker Architecture):公共对象请求代理的体系结构,第一版于1991年问世,当时只规范了如何在C语言中使用它,1994年推出CORBA2.0规范,目前普遍使用的是CORBA2.X,现在CORBA3规范已经建立,它主要新增了Java和internet、服务质量控制以及CORBA组件包等方面。
CORBA简化了C/S模式
在传统的client/server应用中,开发者使用自己设计的标准或通用标准来的协议(如Socket)。协议定义与实现的语言、网络传输及其w他网络因素有关。而CORBA简化了这一过程,它使用IDL来定义客户与服务器之间的接口协议。
为了调用一个分布式对象的操作,客户程序必须了解由这个对象所提供的接口,一个对象的接口是由它所支持的操作和能够来回传输这些操作的数据类型所组成的。
在CORBA中,对象接口是按OMG接口定义语言(Interface Define Language,IDL)来定义的,与C++、JAVA等高级语言不同,IDL不是编程语言,所以对象和应用程序不能用IDL实现,IDL在客户和服务器程序之间建立一个契约,用它来描述在应用程序中需要用到的类型和对象接口,这些描述与实现的语言无关,可以不用考虑客户程序的编程语言是否与服务器程序的编程语言一致。正是IDL的语言独立性,使CORBA成为异构系统的集成技术。
IDL是一个纯说明性的语言,它使用程序员把焦点集中在对象接口、接口所支持的操作和操作时可能引发的异常上。它有一整套词法规则,供程序员定义接口。有关这些词法的使用,将在下一讲中作简要介绍。
IDL的一个重要特性是,一个接口可以继承一个或多个其他的接口。
因为IDL只是一种说明性语言,它不能用于编写实际的应用程序,它不是提供控制结构或变量,所以它不能被编译或多用解释成一个可执行的程序,它只适用于说明我对象的接口,定义用于对象通信的数据类型。
语言映射指定如何把IDL翻译成不同的编程语言,目前OMG IDL语言映射可适用于C、C++、Java、COBOL、Smalltalk、Ada等。
IDL语言映射是开发应用程序的关键,它们提供CORBA所支持的抽象概念和模型的具体实施。IDL经过语言映射后,被翻译成特定语言的存根和框架,用于客户端和服务器端的程序的组成部分。
CORBA应用程序是以接收CORBA对象的请求或调用CORBA对象的请求这种形式工作的。
调用请求的两种方法
调用请求的过程
对象引用(Object Reference)
客户程序通过发送消息来控制对象,每当客户调用一个操作时,ORB发送一个消息给服务器对象。为了能发送一个消息给一个对象,客户必须拥有该对象的对象引用(Object Reference),对象引用起着一个句柄的作用,句柄标识唯一的一个对象,并且封装了要将所有消息发送给正确的目标ORB所需要的信息。
对象引用与C++中的类的实例指针有相同有语义,Java中没有指针的概念而用引用。每个对象引用必须准确地标识一个对象;一个对象可以有多个引用;引用可以是空的;
引用的获取
对象引用是客户程序获得目标对象唯一的途径,引用由服务器以某种方式发布的,常见的有:
请求调用的特征
CORBA 2.0引入一个通用的ORB互操作性结构体系,称为GIOP(General Inter-ORB Protocol,通用ORB协议)。
GIOP是一类抽象的协议,它指定了转换语法和一个消息格式的标准集,以便允许独立开发的ORB可以在任何一个面向连接的传递中进行通信。IIOP是internet网上的ORB协议(Interent Inter-ORB Protocol, IIOP),它是GIOP在TCP/IP上的实现。
服务器应用程序的事件处理模型
下图表明了ORB、POA管理器、POA和伺服程序之间的关系。
基于CORBA的系统包括客房客户程序和服务器程序两部分。通常需要执行以下几个步骤:
命名服务是CORBA最基本的服务,它提供从名称到对象引用的映射:给定一个名称,该服务返回一个存储在此名称下的一个对象引用。很像internet上的DNS。
命名服务给客户程序带来的好处
命名图(naming graph)
名称解析(name resolve)
命名服务提供(resolve)操作,由命名服务器将客户程序中一个指定的名称转换为对应的对象引用并返回。
命名服务允许一个客户程序通过一个符号名来定位对象的引用,这种机制对于客户程序定位一个对象很有用,但这要求客户程序必须确切知道要使用什么对象。
往往客户程序需要多种机制来定位一个对象,例如,一个客户程序可能只知道所需要的对象类型,对要做出精确选择的其他必要信息并不清楚。这时CORBA的交易服务(Trading Service)提供了这种功能。允许客户程序借助交易来定位对象。
与命名服务类似,一个交易用来存储对象引用及其服务描述,客户程序执行动态查找服务,此服务是基于查询服务描述的。
基本的交易概念
交易服务的基本轮廓
一个交易就是一个用于存储属性描述对象引用的数据库,我们可以导出(增加)新对象引用和它的描述,或者收回它们。
前面我们介绍的CORBA请求调用是基于同步的请求调用,在同步请求上,一个主动的客户程序向被动的服务器调用请求,在发送一个请求后,客户程序阻塞并等待返回结果。
CORBA事件服务允许服务器向客户发送消息,即将C/S方式转化为对待方式(peer-peer)。
CORBA的事件服务模型
提供者(suppliers)生成事件而使用者(consumers)接收事件,提供者和使用者都连接一个事件通道(event channel)上,事件通道将事件从提供者传送到使用者,而且不需要提供者事先了解使用者的情况,反之亦然。事件通道在事件服务中起着关键作用,它同时负责提供者和使用者的注册,可靠地将事件发送给所有注册的使用者,并且负责处理那些与没有响应的使用者有关的错误。
事件服务发送事件的模型
推模型(push model)
拉模型(pull model)
把能用来描述、分类,并可供系统设计员选择的有效的结构集合称为设计空间。
一个指定的应用系统的设计空间(desigm space)是指在建立该系统设计时所有可供选择的功能维(functional dimensions)和结构维(structure dimensions)的集合。
设计空间中的维数,可能是相关的,也可能是独立的,设计规则(design rule,简称规则)是指维之间的所有关系,主要两种类型:有功能维与结构维的关系规则、结构维内部之间的关系规则。
规则(rule)可用正(positive)、负(negative)权值(weight)表示,这样设计规则体现了维之间关系的紧密程度或选定一种组合的好坏。
一旦从设计空间中选定一个设计,那么就可以通过权值来计算这个设计的得分(score),如果能将一个应用的设计空间的所有设计的得分计算出来,就可以比较各种设计的得分,以找出一个最合适的设计。
对于一个经验丰富的高级设计员,可以建立一个足够完备(complete)和可靠(reliable)的规则,作为自动系统设计(automated system design)的基础。但是并不是说有了规则,就可以设计出一个完善的或一个最有可能的系统。规则可以帮助一个初级设计员(journeyman designer)像高级设计员(master designer)一样去选择一个合适的、没有大错误的设计。一个自动系统设计(automated system design)的实现将是一个漫长的过程。
设计知识库(design vocabulary)
表示软件设计的知识(knowledge),一种好的方法就是建立一个设计知识库(design vocabulary),一组容易理解(well-understood)、可重用(reusable)的设计概念(concept)和模式(pattern)。
使用设计知识库的好处(major benefit)
比如,程序结构由控制流程的少数几个标准概念【条件(conditional)、循环(iteration)、子过程调用(subroutine call)等】来描述,程序避免了早期所使用的复杂的底层测试和分枝等操作,而直接使用这些大家普遍理解和接受的控制流程模型,并把精力集中于这些模型的属性(如循环语句中的条件变量、结束条件等),这样不仅容易编写、容易阅读而且更加可靠。
随着软件工程的成熟和研究关注转向大的系统,我们期待类似于程序结构的标准也会在大型软件系统中出现,现存中型(medium-size)软件的结构模型的正在不断走向成熟,人们已经注意到(anticipate)对于一个大型系统更高层的抽象是必要的。
上面,我们主要介绍由不同的系统结构构成的多维设计空间(multidimensional design space)的概念,设计空间中的每个维描述了系统的一个特征及其可选择的范围,维上的值(即规则)则表示了需求与选定设计(design choices)的关系程度。比如,在一个并发进程的同步机制(interprocess synchronization mechanism)中,响应时间(response time)作为一个维,需求为Fast,Med和Slow,同步机制作为另一个维,结构有Message,Semaphore,Monitor,Rendezvous,Other,None等。
一个设计空间中不同维之间不一定是独立的(independent),事实上,为了建立设计规则,发现维之间的关系是非常重要的。
构造设计空间的关键是选择维,一些维反映需求或评估标准(evaluation criteria),如功能、性能等,另一些维则反映结构及其可用的设计。在维之间找出关系可以提供直接的设计指导,因为这些关系表明了设计选择是否最符合新系统的功能需求。在图5.1的例子,假设message机制能提供Fast的响应时间,而Rendezvous机制只能提供Med的响应时间。
设计空间的组成
比如,在瀑布模型(watefall model)中功能设计空间表示需求分析的结果,而结构设计空间则是指设计阶段初始系统的分解(decomposition)
下面,我们介绍一个设计用户交互接口的软件的设计空间的建立。
为了描述可用的结构,我们需要定义几个术语(terminology)用于表示系统的组件,这些术语要求是通用的,主要有:
在一些简单的系统中,可以没有后两种组件,比如,系统可能没有设备驱动程序的代码,或者系统不要求提供支持多类型设备的应用。
在一些中等规模的设计空间中,接口分成两种设备接口(device interface)和应用接口(application interface),图5.2表示了用户接口的一种基本结构模型。
有研究表明一个完整的用户接口系统的设计空间包括25个功能维和19个结构维,这里我们只能选择一些有代表性的6个功能维和5个结构维。
用户接口系统的功能维
以上6个功能维可分为3组
1.External requirements(外部需求)
2.Basic interactive behavior(基本交互行为)
需要提出,上述中只有菜单选择和表单填写可以支持同一种系统结构,其他类型的需求是单一的。
3.Practical considerations(考虑实际应用)
用户接口的结构维
以上5种结构维可分为3组:
1.Division of functions and knowledge among modules(模块中功能和信息的划分)
2.Representation issues(表示问题)
3.Control flow、communication、synchronization issues(控制流程、通信、同步问题)
规则指示了维之间的关系程度,主要有两种类型
规则例子(sample rules)
如果外部事件处理需要抢占用户命令,那么抢占控制线程机制(标准进程、轻量级进程或中断服务例程)是非常受欢迎的。如果没有这样的机制,必须对所有用户界面和应用程序处理施加非常严格的约束,以保证足够的响应时间。
高用户可定制性要求有利于用户界面行为的外部表示法。隐式和内部符号通常比外部符号更难访问,并且更紧密地耦合到应用程序逻辑。
分布式系统组织支持基于事件的通信。基于状态的通信需要共享存储器或一些等效的存储器,在这样的环境中访问这些存储器通常是昂贵的。
人们对软件产品(artifact)的分析已经有多年的研究和实践。在理论和经验上,系统的性能(performance)、复杂性(complexity)、可靠性(reliable)、可测试性(testability)以及其他所期望的质量的度量已经成为软件工程的主要组成(integral part)。然而软件产品作为软件开发过程的最后一个阶段,对其分析的是重要的。现在已经达到共识:软件产品可以与开发过程所依赖的设计一样好。
在过去,软件分析工具的开发并没伴随(accompany)人们对软件设计的逐惭关注而发展。原型技术( prototyping techniques)能证明(demonstrate)设计阶段的可行性(feasibility)或研究(investigate)设计中的特殊问题。形式软件检查(inspection)可以在后期开发阶段展开之前尽早地发现设计上的错误。形式描述语言(formal specification language)可以用于描述一个设计。但现在不没有一个能从理论和质量上对设计进行分析的工具。这样的工具应能提供一些度量设计符合产品所拥有的需求的程度,而无需构造一个原型,并且能使一个设计者将自己的知识应用于一个应用领域或致力于软件设计和构造。
量化设计空间(Quantified Design Space,QDS)是美国卡内基.梅隆(Carnegic Mellon)大学(CMU)提出的。QDS的目标是分析和比较一个指定应用的各种软件设计,QDS的思想是基于设计空间(design space)和质量配置函数(quality function deployment,QFD),是一种将系统需求转换为可选择的功能和结构设计,量化(quantified)分析各种的可能选择及其组合。QDS作用是可以为一个期望系统(desired system)制作(produce)一个设计模型,也可以用来分析和对比已有的设计,或对现有的设计提出改进建议。
QFD是一个质量保证(assurance)技术,其作用是将客户的需求转换为产品从需求分析到设计开发各个阶段的技术需求,或转换为制造(manufacturing)和维护(support)。1997年QFD应用于日本的汽车制造并获得显著的效果,现在已经在日本和美国得到普遍应用。QFD的基本目标(fundamental)是“客户的声音”(voice of customer)出现在(manifested)产品开发过程的各个阶段。QFD的观点(philosophy)是让所有的投资者对产品开发达成一个共识(common understanding)。
QFD技术带来的好处
QFD过程
QFD的基本原理(philosophy)是通过一组详细说明的图形符号和用于开发过程各个阶段的良好定义的转换机制(将需求转换为实现)。QFD实现协作工程的多用户设计系统的产品分析(同时有多用户从从的单个设计侧面协同工作)。
QFD过程的步骤如下:
步骤1:建立产品或服务开发的范围,招集投资者代表参与QFD工作,收集各阶段的用户需求(这些需求用户可按它们自己的语言表示),由交叉功能小组分析这些需求并达成共识,将这些需求加入到QFD表(如图5.4)的左边列中。
步骤2:QFD小组选择满足用户需求的实现机制(realization mechanism),这些机制组织在QFD图表的上端,建立用户需求对实现机制的关系矩阵。
步骤3:为每个实现机制建立目标值(target value),并填入QFD图表的关系矩阵的下方,这些目标可以是量化数值或量化目标,但这些目标都是可以主观验证的(verifiable)。
步骤4:建立机制与需求的关系(relationship),以确定不实现用户需求哪一种机制是最重要的每一种关系可用图形符号表示其程度(high、medium、Weak),如果某个机制不满足一个需求,则对应的关系矩阵的值为空。例如,图中,database manager机制与需求Work with large designs有强(strong)的系统性能关系,但与需求Apply expert knogledge系统能力上关系较弱(Weak)。这些关系赋于一个权值(weight)以便计算每个实现机制的重要性。分析这些关系是QFD的主要能力(strength)之一,比如,图5.4中,起始可能认为database manager机制主要满足Support cooperative design,但更进一步分析表明,有效的数据库database manager机制对需求work with large designs也有很好的性能,这种“副效益”(side-benefit)在QFD框架中可以很清楚地表示出来。
步骤5:计算机制间的相关性(correlations)即各种实现机制间的正、负关系,正数关系表示两种机制彼此都很好,负数关系表示一种机制的实现会干扰(interfere)另一个机制的实现。这些关系在QFD图表的上方,用图形符号表示关系程度,空格表示两个机制没有联系。比如,图5.4中表明ASCII文件的使用与数据库管理有很严重的负关系。
步骤6:估计(assess)每一种机制的实现难度也根据组织的强弱,困难等级(rating)记录在QFD图表中target value的上方。
步骤7:计算每种机制的重要等级:一种机制的各关系值乘以QFD表右边列(customer importance)对应客户值的并相加得到这种机制的技术绝对值。为了明确起见,将绝对值从大到小排序,最大的为1,相同的并列,最终得到相对值,它们分别记录在QFD图表的底部。
步骤8::建立了关系(relationship)和相关性(correlation)后,FQD小组就可以分析该框架,选择在产品中使用的机制。
过程的结果可以作为下一阶段的产品开发。
QFD总结:
为完成这个QFD例子,每个实现机制 (realization mechanism)要指出其目标值(target value),实现机制的各列要按相对技术重要性重排。这个框架还可以作为下一个QFD的输入,比如选定database manager机制,另个指定通信使用(client-server),作为下一阶段QFD的输入。
QDS原理
应用QFD框架实现一个设计空间
QFD过程的共同目标是获取设计知识,即关于实现机制与客户需求间的关系和机制间相关性的知识。设计空间的规则(rule)能从QFD框架中的关系和相关性获得。比如,一个规则表明一个“distributed system”不能在一个“monolithic program”可实现。
1.通用功能设计空间(Generalized Functional Design Space)
客户需求列在表的左边,赋于这些客户需求的权置于表中另一个列,功能维及其可选择的设计空间位于表的上方,并与需求构成的关系矩阵,关系值越大表明功能越能满足需求,计算每个功能的“calculated weight”,及各需求的权,表的“design decision”中1表示可选用的功能。“effective weight”为“calculated weight”与“design decision”的积。
计算范围:大矩形圈中的
计算方法:
Design decision = 对应每个function最大值的列标记为1,其余空着,默认为0;
Effective weight = Design decision 乘 Calculated weight
2.通用结构设计空间(Generalized Structure Design Space)
计算范围:大矩形圈中的
计算方法:
Design decision = 对应每个Structural最大值的列标记为1,其余空着,默认为0;
3.结构设计可选择相关性(Corrlation of Structural Deisgn Alternative)
4.量化设计空间值(score)
设E1、E2、…、En 是每个选定结构设计备选方案的有效重量;设Cmn为备选方案m和n的相关系数。
对于每个选定的结构设计方案,将设计方案的有效权重与其他选定方案的有效权相加,并将总和的每个项乘以矩阵中的相关因子。然后将乘积总和相加,得出设计的最终得分: S r o c e = ∑ C i j ( E i + E j ) Sroce=\sum C_{ij}(E_i+E_j) Sroce=∑Cij(Ei+Ej)
计算方法:
根据通用结构设计空间表的Design decision 值,确定选择了Structural#1的A1#1方案a、Structural#2的A1#2方案b和Structural#3的A1#3方案c。在结构设计可选择相关性表中确定他们对应的
c
a
b
=
0.5
,
c
a
c
=
1
,
c
b
c
=
0
c_{ab}=0.5,c_{ac}=1,c_{bc}=0
cab=0.5,cac=1,cbc=0,即下图圈中的值
根据通用结构设计空间Design decision 值,确定了Requirement#1对应方案a,b,c的
E
a
=
540
,
E
b
=
1080
,
E
c
=
1620
E_a=540,E_b=1080,E_c=1620
Ea=540,Eb=1080,Ec=1620
R
e
q
u
i
r
e
m
e
n
t
#
1
=
C
a
b
∗
(
E
a
+
E
b
)
+
C
a
c
∗
(
E
a
+
E
c
)
+
C
b
c
∗
(
E
b
+
E
c
)
Requirement\#1= C_{ab} * (E_a+E_b) + C_{ac} * (E_a+E_c) + C_{bc} * (E_b+E_c)
Requirement#1=Cab∗(Ea+Eb)+Cac∗(Ea+Ec)+Cbc∗(Eb+Ec)
2970
=
0.5
∗
(
540
+
1080
)
+
1
∗
(
540
+
1620
)
+
0
∗
(
1080
+
1620
)
2970 = 0.5 * (540 + 1080)+1*(540+1620)+0*(1080+1620)
2970=0.5∗(540+1080)+1∗(540+1620)+0∗(1080+1620)
根据对应关系,依次算出Requirement#2,Requirement#3和Requirement#4的值;【对于每个Requirement来说,C的值是一样的,只是E的值不同】
最后Sroce就是每个Requirement相加
通常,好的结构设计是决定软件系统成功的主要因素,然而,目前许多有应用价值的结构,如管道/过滤器、层次结构、C/S结构等,他们都被认为是一种自然的习惯方法,并应用在特定的形式。因此,软件系统的设计师还没有完全开发出系统结构的共性,制定出设计间的选择原则,从特定领域中研究出一般化的范例,或将自己的设计技能传授他人。
要使软件体系结构的设计更具有科学性,一个重要的步骤是就是要有一个合适的形式化基础。这一章我们将介绍软件体系结构的形式化描述(formal specification)。
人们已经认识到,对于一个成熟的工程学科,形式模型(formal model)和形式化分析技术(techniques for formal analysis)是基础(cornerstone),只是不同工程学科他们使用的形式不同。形式化方法可以提供精确、抽象的模型,提供基于这些模型的分析技术,作为描述指定工程设计的表示法,也有助于模拟系统行为。因此在软件体系结构领域,它的形式化描述也有很多的用处:
软件体系结构描述的方法分为
常用的一般描述方法
常用的形式化描述方法
在结构化范型中,将系统结构映射为主程序和一系列具有调用关系的子过程(或子函数)的集合。主程序充当子过程的调用者,子过程之间又存在着复杂的过程调用关系。过程之间通过参数传入和传出数据。这是软件设计最直接、最基本的结构关系。更复杂的系统设计包含了模块、包、库、程序覆盖等概念。
优点
缺点
数据抽象和面向对象设计是在主程序和子过程设计方法的基础上建立和发展起来的重要的软件描述方法。数据抽象是面向对象设计的理论基础,类和对象是该描述方法的基础粒度。
类是数据抽象的载体,由数据成员和操作方法构成,成员的类型和取值范围定义了数据运算的域,方法定义了对数据的运算及其遵循的公理,这样,数据以及对它们的操作就被安然完整地封装在一起而形成了抽象数据类型。
对象是类的实例,是软件系统的可运行实体,它全面复制了类的数据组成和操作方法,对象的数据反映了对象的生存状态。对象之间是通过操作方法的调用建立相互作用关系,方法调用采用<对象名>.<方法名>(<参数>)的形式。这就是对象的信息隐藏性和封装性,它保证了行为正确的对象在外界环境不出现意外时永远是正确的。
类的继承性是一种重用机制。通过继承类的数据和操作,并加以扩充,可以快速建立(或导出)起新的类。另外,封装性保证了类或对象作为独立体的完整性。
多态性是同一行为名,作用在不同类的对象上时,对应的性质相同但操作细节不同的特性,保证了系统中部件要可替换兼容性。
动态链接是在可环境中实现多态性的机制,也是运行代码重用的机制,为运行代码的扩充、升级提供了可能。
类属理论是一种表达对象关系的数学语言,最初的研究是Samuel Eilenberg 和 Sanders Maclane提出的。广泛用于描述软件体系结构中的部件和连接器。
Z Notation是由牛津大学(University of Oxford )的Programming Research Group研究的一种数学语言,基于一阶谓词逻辑(firs-order logic)和集合论(set theory)。使用标准的逻辑操作符(∧、∨、→等)和集合操作符(∩、∪、∈等)以及它们的标准常规定义。
使用Z Notation可以描述数学对象的模型,这些对象与程序计算对象相似,这是Z可作为体系结构和软件工程描述的原因。
形式规范(formal specifications)是用数学表示法(notation)精确地描述信息系统的属性,而不过分注重实现这些属性的方法,也就是说,这些规范描述系统应该做什么而不关心系统如何实现。这种抽象使用得形式规范对开发计算机系统的过程很有意义,因为它们使得系统的问题能够可信赖地回答,没有必要解决大量的程序代码细节。
形式规范为客户需求分析、程序实现、系统测试或编写系统用户手册等人员提供一个单一、可靠的资料,因为一个系统的形式规范独立于程序代码,可以在系统开发之前完成。虽然在这些规范在系统设计阶段或客户需求变化时需要改变,但它们是促进对系统达成共识的一个有效方法。
Z notation的思想是把描述一个系统的规范分解成若干图表(schema,也称架构或模式), 图表(schema)可以再分解更小的图表。在Z中,图表(schema)用于描述系统的静态和动态方面:
例子:
声明满足属性的各种变量或变量的取值,在Z中有两种基本的声明方法。
声明变量时,还可以指定是输入或输出,在变量后加上“?”表示该变量从用户输入;在变量后加上“!”表示输出。
声明的变量的适用范围由它出现的上下文(context)决定,可以是适合整个规范的全局变量,作为schema的组件,也可以是适合于某个谓词或表达式的局部变量。
一个schema文本是由声明和或选择的谓词列表。
Schema-Text ::= Declaration [Predicate; : : : ; Predicate]
∀ d e c l ∣ p r e d 1 ∗ p r e d 2 \forall decl\; |\; pred1 * pred2 ∀decl∣pred1∗pred2:它被读作“对在 decl 中满足谓词 pred1 的所有变量,都使得谓词 pred2 为真”;
∃ d e c l ∣ p r e d 1 ∗ p r e d 2 \exists decl\; |\; pred1 * pred2 ∃decl∣pred1∗pred2:它被读作“存在 decl 中满足谓词 pred1 的一个或多个变量,使得谓词 pred2 为真”;
详情可以访问下面博客
JavaEE
【10.2和10.3任选一题即可,回答要写明题目,技术名称和意义】
德国Innogy共享充电桩
德国能源巨头Innogy和物联网平台企业Slock.it合作推出基于区块链的电动汽车点对点充电项目。用户无需与电力公司签订任何供电合同,只需在智能手机上安装Share&Charge APP,并完成用户验证,即可在Innogy广布欧洲的充电桩上进行充电,电价由后台程序自动根据当时与当地的电网负荷情况实时确定。由于采用了区块链技术,整个充电和电价优化过程是完全可追溯和可查询的,因此极大地降低了信任成本。需要充电时,从APP中找到附近可用的充电站,按照智能合约中的价格付款给充电站主人。
什么是软件体系结构?它有哪三种类型?
一个软件系统的体系结构是指构成这个系统的计算部件、部件间的相互作用关系。部件可以是客户(clients)、服务器(servers)、数据库(databases)、过滤器(filters) 、层等。部件间的相互作用关系(即连接器)可以是过程调用(procedure call)、客户/服务器、同步/异步、管道流(piped stream)等。
服务于软件开发的不同阶段,体系结构可分为概略型——是上层宏观结构的描述,反映系统最上层的部件和连接关系;需求型——对概略型体系结构的深入表达,以满足用户功能和非功能的需求。设计型——从设计实现的角度对需求结构的更深层的描述,设计系统的各个部件,描述各部件的连接关系。这是软件系统结构的主要类别。
软件产品在实现时通常分解成一些较小的模块,这种做法的意义是什么?分解时应遵循的原则是什么?
解决复杂问题的一种有效方法;集体分工协作的前提,将一个产品分解成几个相对独立的模块,这些模块分配由几个不同的小组开发;产品维护的保障,模块间的相对独立性使得修改其中一个模块的内部代码或数据结构不影响其他模块。不仅为运行时的维护提供了可行性,还减少维护的费用。
一个好的模块设计应该使模块具有高内聚性和低耦合性。
在C/S结构中,有状态信息服务器和无状态信息服务器相比,哪一种服务器对同时使用的用户数有限制?为什么?
有状态信息服务器对同时使用的用户数有限制。因为有状态信息服务器需要设计一个状态信息表,用于登记当前的用户操作请求,当状态信息表填满时,新用户的操作被推迟,意味着同时使用的用户数受限制。
总结P/F结构风格的缺点
(1)通常导致进程成为批处理的结构。这是因为虽然过滤器可增量式地处理数据,但它们是独立的,所以设计者必须将每个过滤器看成一个完整的从输入到输出的转换。
(2)不适合于需要共享大量数据的应用设计
(3)不适合处理交互的应用。当需要增量地显示改变时,这个问题尤为严重。
(4)过滤器之间通过特定格式的数据进行工作,数据格式的设计和转换是系统设计的主要方面,为了确保过滤器的正确性,必须对数据的句法和语义进行分析,这增加了过滤器设计的复杂性。
(5)并行运行获得高效率往往行不通。原因,第一,独立运行的过滤器之间的数据传送的效率会很低,在网络传送时尤其如此;第二,过滤器通常是在消耗了所有输入后才产生输出;第三,在单处理器的机器上进程的切换代价是很高的;第四,通过管道对过滤器的同步控制可导致频繁启动和停止过滤器工作。
在分布式令牌环互斥算法中,一个进程进入临界区执行需要令牌消息传递的个数是
1
−
∞
1-\infin
1−∞ ,解释这里
∞
\infin
∞ 的含义。
在分布式令牌环互斥算法中,算法保持一个令牌在环中传递,一个进程只有在得到令牌时才能进入临界区执行,以便实现互斥;即使当前所有进程都没有申请进入临界区运行,令牌也要沿环不断地传递;因此,当一个进程要进入临界区执行是,令牌消息传递的个数是不确定的,可以有任意多次,即
∞
\infin
∞ 。
解释RPC透明性的含义。
使得远程过程调用尽可能象本地调用(内部过程调用)一样,即调用进程不关心也不必知道被调用过程的位置(在本地或远程另一台计算机);反过来也是如此,被调用过程也不关心是哪台机器上的进程调用,只要参数符合要求即可。
PRC中客户代理(client stub)实现什么主要功能?
将参数封装成消息;请求内核将消息送给服务器;调用receive操作进入阻塞状态;当消息返回到客户时,内核找到客户进程,消息被复制到缓冲区,并且客户进程解除阻塞;客户代理检查消息,从中取出结果,并将结果复制给它的调用进程
简述QDS的思想、作用及目标。
QDS的思想是基于设计空间和质量配置函数,是一种将系统需求转换为可选择的功能和结构设计,量化分析各种的可能选择及其组合。
QDS作用是可以为一个期望系统制作一个设计模型,也可以用来分析和对比已有的设计,或对现有的设计提出改进建议。
QDS的目标是分析和比较一个指定应用的各种软件设计;
某简单账户管理系统主要维护用户姓名和当前账目余额,提供账户新增加、查询、收入和支出等操作,要求账目余额不小于0.请应用Z Notation形式化如下的系统设计。
1)定义一个状态模式(UserAccount),描述账户的基本属性。
2)定义一个操作模式(NewUser),用于注册一个新的用户的用户账号和初始余额,规定同一个账户不能重复注册。
3)定义一个操作模式(Import),向给定用户cur_user的账目中添加指定的金额cur_money。
4)定义一个操作模式(Export),从给定用户cur_user的账目中扣除指定的金额cur_money。
5)定义一个操作模式(Query),查询系统中账目余额小于指定金额cur_money的所有用户。
已知某产品的设计空间的功能维子空间、结构维子空间以及规则(如表1、表2、表3)。完成下面两个问题:
1)采用QFD量化算法,完成表1和表2的剩余单元格的数据,并给出量化过程的设计选择结果。
功能维选择 Functional#1.Alt#1、Functional#2.Alt#3
结构维选择 Structural#1.Alt#2、Structural#2.Alt#1
2)结合表3,计算该设计的得分(含总分和各需求的得分,要求写出计算过程)。
根据结构维选择Structural#1.Alt#2、Structural#2.Alt#1,可得:
C
12
=
0.5
C_{12}=0.5
C12=0.5
根据结构维选择Structural#1.Alt#2、Structural#2.Alt#1,可得:
Requirement#1:
E
1
=
234
,
E
2
=
279
E_1=234,\;E_2=279
E1=234,E2=279
Requirement#2:
E
1
=
80
,
E
2
=
104
E_1=80,\;E_2=104
E1=80,E2=104
Requirement#3:
E
1
=
38
,
E
2
=
48
E_1=38,\;E_2=48
E1=38,E2=48
所以,
Requirement#1:
C
12
∗
(
E
1
+
E
2
)
=
0.5
∗
(
234
+
279
)
=
256.5
C_{12}*(E_1+E_2)=0.5*(234+279)=256.5
C12∗(E1+E2)=0.5∗(234+279)=256.5
Requirement#2:
C
12
∗
(
E
1
+
E
2
)
=
0.5
∗
(
80
+
104
)
=
92
C_{12}*(E_1+E_2)=0.5*(80+104)=92
C12∗(E1+E2)=0.5∗(80+104)=92
Requirement#3:
C
12
∗
(
E
1
+
E
2
)
=
0.5
∗
(
38
+
48
)
=
43
C_{12}*(E_1+E_2)=0.5*(38+48)=43
C12∗(E1+E2)=0.5∗(38+48)=43
总分 :
256.5
+
92
+
43
=
391.5
256.5+92+43=391.5
256.5+92+43=391.5
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。