1. 编译程序包括哪几个主要组成部分
编译过程分为分析和综合两个部分,并进一步划分为词法分析、语法分析、语义分析、代码优化、存储分配和代码生成等六个相继的逻辑步骤。这六个步骤只表示编译程序各部分之间的逻辑联系,而不是时间关系。
编译过程既可以按照这六个逻辑步骤顺序地执行,也可以按照平行互锁方式去执行。在确定编译程序的具体结构时,常常分若干遍实现。对于源程序或中间语言程序,从头到尾扫视一次并实现所规定的工作称作一遍。每一遍可以完成一个或相连几个逻辑步骤的工作。
(1)软件编译架构扩展阅读:
对于c编译程序来说,其语言的特点如下:
1、c语言是一种结构化语言。它层次清晰,便于按模块化方式组织程序,易于调试和维护,而且表现能力和处理能力极强。
2、c语言具有丰富的运算符和数据类型,便于实现各类复杂的数据结构。它还可以直接访问内存的物理地址,进行位(bit)一级的操作。
3、由于c语言实现了对硬件的编程操作,因此集高级语言和低级语言的功能于一体。它既可用于系统软件的开发,也适合于应用软件的开发。
4、此外,c语言还具有效率高、可移植性强等特点。因此它广泛地移植到了各类各型计算机上,从而形成了多种版本。
2. keil和vc++,单片机编程
用VC++编程,是针对80x86 CPU 的,只能生成 80x86 的机器码,最终只能在 PC 机的Windows环境下执行,适用范围就太小了。
用keil编程,可以选择不同的CPU,可以选择操作系统,可以使用C,也可以使用汇编语言。
用keil编程,可以充分发挥人的智能。
3. 软件架构和系统架构的区别是什么
不同的架构方法论,会将架构分为不同视图,每个视图侧重某一个方面、领域的问题。
比如希赛推的ADMEMS架构体系,分为以下几种视图:
1. 数据架构:描述数据的存储结构、格式等方面。
2. 物理架构:描述机器的物理部署、网络拓扑方面。
3. 运行架构:描述运行期线程、进程间的交互工作机制。
4. 逻辑架构:指如何将代码分成不同模块、组件,以及之间的职责分配、交互行为。
5. 开发架构:主要指开发工具的选择,程序单元的划分,开发管理规范流程等方面。
例如分为哪些工程、项目,源代码管理,自动化编译构建、测试、部署等。
目前国际上运用比较广泛的是TOGAF架构体系,他把架构分为业务架构、数据架构、应用架构、技术架构等几个方面。
想详细的了解这些架构视图,可以参考这些架构体系相关的书、资料。
另外有很多人无缘无故的抨击架构概念,不知道是出于调侃还是无知。
埃及的金字塔、神庙的建设,不是几个平常的泥瓦匠聚在一起就能够造出来的。
像SAP、Oracle ERP,国内的金蝶等大规模的系统,以及空间站、火箭的控制系统等,没有系统性的架构方法、规范、流程,结果只能是悲剧。
当规模、复杂度没有达到一定程度,比如在一些小的团队、产品中,架构过程可能融入到老板、经理、组长、资历较深的一些开发者中,融入在大家的日常工作中,以至于感觉不到架构的存在。
就算遇到一些问题,因规模不大、复杂度不高,也比较容易调整。
当这些前提条件发生变化时,架构的作用和必要性就逐步的体现出来。
总的来说,一说到架构,如果懂软件,那么会了解为一个软件系统,这个软件设计的组成结构,如哪些是基础支持组件,哪些是完成A业务,哪些完成B业务……但说道企业架构的时候,就会问,该企业架构的几个架构如业务架构、数据架构、业务架构、技术架构,以及如何链接在一起。
倒觉得,一个企业确实需要这样的架构,但不要神话它,最主要的是业务如何最终体现到软件中和流程中。
而采取分离式设计时,最容易的错误就是各自为政,集成困难。
那么以数据为中心的架构设计,会自然提供集成的基础。
提到过,企业最重要的资产是数据,甚至不是信息,是数据。
企业的业务流程会变,IT系统会变,所需要的信息与知识会变,唯有数据能够积淀下来。
这有点象自然演进,考古那种,啥都
4. 架构类型以及软件架构逻辑详解
架构类型:分布式、SOA架构、单体式。
分布式架构
分布式应用架构中,相互独立,代码独立开发,独立部署,通过API接口互相通信。通讯协议一般使用HTTP,数据格式是JSON(是一种轻量级的数据交换格式),应用集成方式比较简化。
优点: 应用内部高内聚,独立开发、测试和部署,应用之间松耦合,业务边界清晰,业务依赖明确,支持大项目并行开发。
缺点: API接口需求变化,应用就需要重新部署,通信可靠性和数据的封装性相对于进程内调用比较差。
SOA架构[现在也流程SAAS服务模式架构也称云架构]
SOA也是分布式应用架构一种。
SOA架构提供配套的服务治理,包括服务注册、服务路由、服务授权、服务降级、服务监控等等。
SOA架构既体现业务的拆分,又体现业务的整合,更多地从业务整体上考虑系统拆分。
优点:以服务层为主,聚焦核心业务,同时以提供整个系统共享,服务作为独立的应用,独立部署,接口清晰,很容易做自动化测试和部署,服务是无状态的,很容易做水平扩展;通过容器虚拟化技术,实现故障隔离和资源高效利用。
缺点:系统依赖复杂,给开发/测试/部署带来不便,分布式数据一致性和分布式事务支持困难,一般通过最终一致性简化解决。
单体式应用
系统只有一个应用、打包成一个应用;部署在一台机器;在一个DB里存储数据.
单体式应用采用分层架构,一般为表示层、业务层、数据访问层、DB层,表示层负责用户体验,业务层负责业务逻辑,数据访问层负责DB层的数据存取
优点:开发、编译、调试一站式、一个应用程序包含所有功能点,容易测试和部署
缺点:系统逐渐庞大时,代码复杂度高,难以维护,应用扩展水平低,业务和模块职责区分不清晰。
软件架构
一、 微服务架构
微服务架构(microservices architecture)是服务导向架构(service-oriented architecture,缩写 SOA)的升级。
每一个服务就是一个独立的部署单元(separately deployed unit)。这些单元都是分布式的,互相解耦,通过远程通信协议(比如REST、SOAP)联系。
微服务架构分成三种实现模式。
RESTful API 模式 :服务通过 API 提供,云服务就属于这一类
RESTful 应用模式 :服务通过传统的网络协议或者应用协议提供,背后通常是一个多功能的应用程序,常见于企业内部
集中消息模式 :采用消息代理(message broker),可以实现消息队列、负载均衡、统一日志和异常处理,缺点是会出现单点失败,消息代理可能要做成集群
优点
扩展性好,各个服务之间低耦合
容易部署,软件从单一可部署单元,被拆成了多个服务,每个服务都是可部署单元
容易开发,每个组件都可以进行持续集成式的开发,可以做到实时部署,不间断地升级
易于测试,可以单独测试每一个服务
缺点
由于强调互相独立和低耦合,服务可能会拆分得很细。这导致系统依赖大量的微服务,变得很凌乱和笨重,性能也会不佳。
一旦服务之间需要通信(即一个服务要用到另一个服务),整个架构就会变得复杂。典型的例子就是一些通用的 Utility 类,一种解决方案是把它们拷贝到每一个服务中去,用冗余换取架构的简单性。
分布式的本质使得这种架构很难实现原子性操作,交易回滚会比较困难。
二、 事件驱动架构
事件(event)是状态发生变化时,软件发出的通知。
事件驱动架构(event-driven architecture)就是通过事件进行通信的软件架构。它分成四个部分。
事件队列(event queue):接收事件的入口。
分发器(event mediator):将不同的事件分发到不同的业务逻辑单元。
事件通道(event channel):分发器与处理器之间的联系渠道。
事件处理器(event processor):实现业务逻辑,处理完成后会发出事件,触发下一步操作
对于简单的项目,事件队列、分发器和事件通道,可以合为一体,整个软件就分成事件代理和事件处理器两部分。
优点
分布式的异步架构,事件处理器之间高度解耦,软件的扩展性好;适用性广,各种类型的项目都可以用;性能较好,因为事件的异步本质,软件不易产生堵塞;事件处理器可以独立地加载和卸载,容易部署
缺点
涉及异步编程(要考虑远程通信、失去响应等情况),开发相对复杂难以支持原子性操作,因为事件通过会涉及多个处理器,很难回滚分布式和异步特性导致这个架构较难测试。
三、分层架构。
分层架构(layered architecture)是最常见的软件架构,也是事实上的标准架构。如果你不知道要用什么架构,那就用它。
这种架构将软件分成若干个水平层,每一层都有清晰的角色和分工,不需要知道其他层的细节。层与层之间通过接口通信。
虽然没有明确约定,软件一定要分成多少层,但是四层的结构最常见。
表现层(presentation):用户界面,负责视觉和用户互动。
业务层(business):实现业务逻辑。
持久层(persistence):提供数据,SQL 语句就放在这一层。
数据库(database) :保存数据。
有的软件在逻辑层和持久层之间,加了一个服务层(service),提供不同业务逻辑需要的一些通用接口。
用户的请求将依次通过这四层的处理,不能跳过其中任何一层。
优点
1、结构简单,容易理解和开发。
2、不同技能的程序员可以分工,负责不同的层,天然适合大多数软件公司的组织架构
3、每一层都可以独立测试,其他层的接口通过模拟解决
缺点
1、一旦环境变化,需要代码调整或增加功能时,通常比较麻烦和费时
2、部署比较麻烦,即使只修改一个小地方,往往需要整个软件重新部署,不容易做持续发布
软件升级时,可能需要整个服务暂停
3、扩展性差。用户请求大量增加时,必须依次扩展每一层,由于每一层内部是耦合的,扩展会很困难。
五、 微核架构。
微核架构(microkernel architecture)又称为"插件架构"(plug-in architecture),指的是软件的内核相对较小,主要功能和业务逻辑都通过插件实现。
内核(core)通常只包含系统运行的最小功能。插件则是互相独立的,插件之间的通信,应该减少到最低,避免出现互相依赖的问题。
优点
1、良好的功能延伸性(extensibility),需要什么功能,开发一个插件即可
2、功能之间是隔离的,插件可以独立的加载和卸载,使得它比较容易部署,
3、可定制性高,适应不同的开发需要
4、可以渐进式地开发,逐步增加功能
缺点
1、扩展性(scalability)差,内核通常是一个独立单元,不容易做成分布式
2、开发难度相对较高,因为涉及到插件与内核的通信,以及内部的插件登记机制。
五、 云架构。
云结构(cloud architecture)主要解决扩展性和并发的问题,是最容易扩展的架构。
它的高扩展性,主要原因是没使用中央数据库,而是把数据都复制到内存中,变成可复制的内存数据单元。然后,业务处理能力封装成一个个处理单元(prcessing unit)。访问量增加,就新建处理单元;访问量减少,就关闭处理单元。由于没有中央数据库,所以扩展性的最大瓶颈消失了。由于每个处理单元的数据都在内存里,最好要进行数据持久化。
这个模式主要分成两部分:处理单元(processing unit)和虚拟中间件(virtualized middleware)。
处理单元:实现业务逻辑
虚拟中间件:负责通信、保持sessions、数据复制、分布式处理、处理单元的部署。
5. gcc的结构
GCC的外部接口长得像一个标准的Unix编译器。使用者在命令列下键入gcc之程序名,以及一些命令参数,以便决定每个输入档案使用的个别语言编译器,并为输出程序码使用适合此硬件平台的组合语言编译器,并且选择性地执行连接器以制造可执行的程序。
每个语言编译器都是独立程序,此程序可处理输入的原始码,并输出组合语言码。全部的语言编译器都拥有共通的中介架构:一个前端解析符合此语言的原始码,并产生一抽象语法树,以及一翻译此语法树成为GCC的暂存器转换语言〈RTL〉的后端。编译器最佳化与静态程序码解析技术(例如FORTIFY_SOURCE,一个试图发现缓冲区溢位〈buffer overflow〉的编译器)在此阶段应用于程序码上。最后,适用于此硬件架构的组合语言程序码以Jack Davidson与Chris Fraser发明的算法产出。
几乎全部的GCC都由C写成,除了Ada前端大部分以Ada写成。 前端的功能在于产生一个可让后端处理之语法树。此语法解析器是手写之递归语法解析器。
直到2004年,程序的语法树结构尚无法与欲产出的处理器架构脱钩。而语法树的规则有时在不同的语言前端也不一样,有些前端会提供它们特别的语法树规则。
在2005年,两种与语言脱钩的新型态语法树纳入GCC中。它们称为GENERIC与GIMPLE。语法解析变成产生与语言相关的暂时语法树,再将它们转成GENERIC。之后再使用gimplifier技术降低GENERIC的复杂结构,成为一较简单的静态唯一形式(Static Single Assignment form,SSA)基础的GIMPLE形式。此形式是一个与语言和处理器架构脱钩的全域最佳化通用语言,适用于大多数的现代编程语言。 GCC后端的行为因不同的前处理器宏和特定架构的功能而不同,例如不同的字符尺寸、呼叫方式与大小尾序等。后端接口的前半部利用这些讯息决定其RTL的生成形式,因此虽然GCC的RTL理论上不受处理器影响,但在此阶段其抽象指令已被转换成目标架构的格式。
GCC的最佳化技巧依其释出版本而有很大不同,但都包含了标准的最佳化算法,例如循环最佳化、执行绪跳跃、共通程序子句消减、指令排程等等。而RTL的最佳化由于可用的情形较少,且缺乏较高阶的资讯,因此相比较起来,增加的GIMPLE语法树形式,便显得比较不重要。
后端经由一次重读取步骤后,利用描述目标处理器的指令集时所取得的信息,将抽象暂存器替换成处理器的真实暂存器。此阶段非常复杂,因为它必须关注所有GCC可移植平台的处理器指令集的规格与技术细节。
后端的最后步骤相当公式化,仅仅将前一阶段得到的汇编语言代码借由简单的子例程转换其暂存器与内存位置成相对应的机器码。
6. 如何组织应用程序的结构
适用于应用程序使用的软件设计和构架总结软件架构一般定义为应用程序的结构。在定义这些结构的时候,软件架构师的目标就是使用不同级别的抽象,通过根据关注点把功能进行分割来最小化复杂度。我们会从最高层的抽象以及不同的关注点开始研究。因为设计的过程中需要不断深入这些层次、扩展关注点直到定义了结构为止。内容目标概览概要步骤第一步——选择我们的分层策略第二步——定义层之间的接口第三步——选择我们的部署策略第四步——选择通讯协议其它资源目标找出并选择分层策略定义层之间的接口找出并选择部署策略选择合适的通讯协议概览在开始应用程序设计的时候,我们第一个任务就是关注最高层次的抽象并且开始把功能分组到不同的层中。然后,我们需要根据我们所设计的应用程序的类型为每一层定义公共的接口。在定义了层和接口之后我们就需要决定应用程序是如何部署的。最后,最后一个步骤包含选择会用于在应用程序不同逻辑层和物理层之间通讯的协议。概要步骤第一步——选择我们的分层策略第二步——定义层之间的接口第三步——选择我们的部署策略第四步——选择通讯协议第一步——选择我们的分层策略层表示组件按照不同关注点的逻辑分组。例如,业务逻辑表示应该分组到某层的一个关注点。这可能是应用程序最初设计中最重要的一个抉择。然而,有很多种方式可以把功能进行分层。在完成了本步骤之后你应该能理解如何来组织应用程序的分层结构。下图演示了用于大多数业务应用程序设计的主要层。在这个示例中,功能按照表现逻辑、服务逻辑、业务逻辑和数据访问逻辑进行分组。如果服务没有公开的话,就不需要服务层,那么我们应该只有表现、业务和数据访问层。 这只是一个示例,不是对应用程序进行分层的唯一方式。然而,一般来说分层在软件设计中很常见。底层的操作系统设计使用了RING的方式,RING0(内核)表示最低的层,它可以直接访问诸如CPU和内存等硬件资源。设备驱动作为外层RING通过RING0提供的接口和硬件进行交互。应用程序代码包含在最外部的RING中,它们通过设备驱动来和硬件交互。除了层,我们还有一些功能会跨越层,它们通常称作横切。此类功能包含诸如日志以及异常管理等。有不同的方式来处理这种功能,比如公共类库、使用元数据直接在编译输出中插入横切代码的面向方面编程(AOP)等。在为应用程序选择分层策略的时候,如下事项是我们需要考虑的关键点:决定我们是否需要层选择我们需要的层决定我们是否需要合并层决定层之间交互的规则决定我们是否需要层在大多数情况下总是需要把功能进行分层。然而,在有些特例下不需要。例如,如果我们构建一个非常小的应用程序,可能就会考虑在用户界面事件处理程序中实现表现、业务逻辑以及数据访问功能。此外,一些软件工程师会认为跨层边界或增加层带来的开销会对性能有负面影响。 是否使用分层的决定说白了就是性能对可维护性。虽然不使用分层可以提升性能,因为所有的东西都紧密耦合在了一起。但是,这种耦合会严重影响应用程序的扩展和维护能力。说实话,对可扩展性和可维护性的影响远比获得的那一点性能的提升重大。不管怎么始终应该考虑分层,我们的目标其实是决定应用程序需要怎么样合适的分层。选择我们需要的层有几种不同的方式来把功能进行分层。例如一种分层的方式就是表现、服务和数据访问。许多关注业务领域的面向对象设计使用又一种不同的分层策略,最底层用于基础结构代码,提供类似数据访问的支持。上面一层就是领域层,用于包含业务领域对象,最顶层包含应用程序代码。对于使用微软.NET Framework开发的大多数业务应用程序来说,按照表现、服务、业务以及数据访问对功能进行分组是不错的选择。下面描述了每一层中的一些组件,以及什么时候不需要这层。表现-这层包含用于处理用户接口请求以及呈现用户接口输出的组件。如果我们的应用程序没有用户接口的话就不需要表现层。服务-这层包含用于处理服务请求的组件。例如,对于使用Windows Communication Foundation (WCF)的应用程序,我们就会找到定义契约的组件、用于实现接口以及提供在实现类和外部契约类之间提供转换支持的组件。如果我们的应用程序不提供服务,则不需要在设计中包含服务层。Business业务-这层包含用于处理表现或服务层请求的组件。在这层中的组件包括业务处理、业务实体以及业务工作流。在一些情况下,我们不需要有任何的业务层并且只需要从数据源获取数据,也就是说这种情况下不需要业务层。数据访问-这层包含用于管理和数据源交互的组件。例如,使用ADO.NET的话在数据访问层中就会有连接和命令组件。如果我们的应用程序不使用外部数据的话那么我们就不需要实现数据访问层。决定我们是否需要合并层例如,应用程序具有很有限的业务规则并且主要关注验证的话可以在一层中实现业务和表现逻辑。在以下情况考虑合并层: 如果我们的业务规则很有限,在一层中实现业务和表现逻辑是合理的。如果我们的应用程序从服务获取数据并且显示数据,可以直接为表现层添加服务引用并且直接使用服务数据。这样的话就可以合并数据访问和表现。 如果数据访问服务直接访问数据库中的表或视图的话可以考虑在服务层中实现数据访问功能。还有一些适用于合并分层的示例。然而,基本准则是我们总是应该把功能分层。在一些情况下,层只不过是一层调用并且没有提供什么功能。然而,如果把功能进行分离的话我们就可以在影响很小或没有影响的情况下扩展其功能。决定层之间的交互规则分层策略带来的一个问题是我们需要定义层之间如何交互的规则。指定这样的一种交互规则的主要原因是最小化依赖以及消除循环引用。例如,如果两个层依赖另外那个层的组件就很可能带来循环依赖。最常见的准则是只允许层之间的单向交互。从上到下的交互–比较高的层可以和其下层进行交互,但是低层不可以和其上的层进行交互。在设计中总是应该强制这样的准则来避免层之间的循环依赖。紧密交互 –每一层只能直接和其下的层进行交互。这个准则可以强制严格的关注分离,应该每一层只知道其直接下属层。这个准则的好处是对某一层的修改只会影响其直接上层。松散交互–高层可以跳过一些层直接和底层进行交互。这可以增加性能,但是也会增加依赖。换句话说,对底层的修改可能会影响其上的多个层。考虑使用紧密交互规则:如果我们设计的应用程序会不断修改来增加新的功能,并且我们希望最小化改变的影响。如果我们设计的应用程序可能会跨物理层进行分布。考虑使用松散交互规则:如果我们设计的应用程序肯定不会跨物理层进行分布。例如是安装在客户机上的独立富客户端应用程序。如果我们设计一个很小的应用程序,影响多层的改动工作量不大。检查点在结束本步骤之前,你应该可以回答如下问题:你是否找到了应用程序需要的功能并且把功能分层?你是否定义了层之间如何交互的规则?第二步——定义层之间的接口对于为层定义接口,最主要的原则是在层之间使用松散耦合。意思就是层不应该公开内部组件让其它层来依赖。而是,层的接口应该设计为最小的依赖性,提供公共接口并且隐藏层中组件的细节。隐藏细节叫做抽象,有很多种方式实现抽象。如下设计方式用来为层定义接口:抽象接口 –可以通过定义抽象基类或类型定义来实现。基类或类型定义了所有层消费者用于和层进行交互的公共接口。通过使用实现抽象接口的测试对象,有的时候又叫做mock对象,可以增进可测试性。公共设计类型 –许多设计模式定义表示不同层中接口的对象类型。这些对象类型提供了一个抽象,它隐藏了层中的细节。例如,表数据入口模式定义了表示数据库中表的对象类型。这些对象负责实现和数据库交互的必要SQL代码。对象的消费者不需要知道使用的SQL甚至不需要知道连接数据库和执行命令的细节。依赖倒置 –这是一种编程模式,抽象接口定义在任何层的外部或不依赖于任何层。层不依赖于公共接口,而不是一个层依赖于另一个层。依赖注入模式是依赖导致的常见实现方式。通过依赖注入组件,一层可以通过使用公共抽象接口来注入到其它层的组件中。基于消息 –基于消息的接口可以用于提供层之间的交互,而不是直接和组件进行交互。有多种消息解决方案,比如web服务和支持跨机器和进程边界的微软消息队列。然而,你也可以组合抽象接口和用于定义要交互的数据结构的公共消息类型。 基于消息最主要的区别是层之间的交互使用公共结构,它封装了交互的细节。这个结构可以定义操作、数据架构、错误契约、安全信息以及其它和层之间通讯相关的细节。考虑使用抽象接口:如果你希望使用接口具体实例来实现不同行为的能力。如果你希望使用mock对象来增加应用程序的可测试性。考虑使用公共设计类型:如果你在为层中的接口实现设计模式。许多设计模式基于抽象接口,然而,一些基于具体类。如果你希望一种快捷而简单的方式实现层接口。比如表数据入口模式。考虑使用依赖导致:如果你希望提供最大的可测试性,这个方式允许你诸如具体的测试类到设计中的不用层中。考虑使用基于消息的:如果你要实现一个web应用程序并且在表现层和业务层中定义接口。如果你有一个应用程序层需要支持多个客户端类型。如果你希望提供跨物理和进程边界交互。如果你希望以公共接口进行交互。如果你要和一个无状态的接口进行交互,状态信息使用消息进行携带。 对于web应用程序表现层和业务逻辑层之间的交互推荐使用基于消息的接口。一般通过使用Windows Communication Foundation (WCF)或提供公共消息类型的抽象接口来实现。在表现层和业务层之间使用基于消息的接口的原因是因为web应用程序的业务层不维护调用间的状态。换句话说,每一次表现层和业务层中的调用都代表一个新的上下文。通过使用基于消息的接口我们可以随请求传递上下文信息并且为表现层中的异常和错误处理提供一种公共的模型。检查点在结束本步骤之前,你应该可以回答如下问题:你是否需要使用抽象接口提供测试性?你的接口是否需要提供跨进程和机器边界交互?你的业务层是否需要支持多个客户端类型?你是否会使用基于消息的接口在web应用程序的表现层和业务层之间进行交互?第三步——选择部署策略对于大多数解决方案中都可以找到几种常见的模式,它们表示了应用程序的部署结构。如果要为我们的应用程序决定最佳部署解决方案,首先需要了解常见的模式。在理解了不同的模式之后,然后你就可以考虑场景、需求以及安全约束来选择某一种或多种模式。客户端-服务器这个模式表示具有两个主要组件:客户端和服务器的基本结构。对于这种情况,客户端和服务器可以在相同机器上也可以跨越两个机器。如下示例演示了常见的web应用程序场景,客户端和web服务器进行交互。N层这个模式表示应用程序组件跨域一个或多个服务器的结构。下图表示了2层部署,所有应用程序代码位于客户端而数据库位于分离的服务器中。3层在3层设计中,客户端和部署在独立服务器的应用程序软件进行交互,和数据库进行交互的应用程序服务器也是单独的服务器。这是许多web应用程序和web服务常见的模式。 4层对于这种情况,web服务器从物理上和应用服务器分离。这么做通常处于安全的目的,web服务器部署在隔离区域(DMZ)中而应用程序服务器位于不同的子网。我们一般会在客户端和web层以及web层和应用层或业务逻辑层之间假设2道防火墙。考虑客户端服务器或2层: 如果我们在开发一个需要访问应用服务器的客户端。 如果我们在开发一个访问外部数据库的独立客户端。考虑3层: 如果我们在开发基于局域网的应用程序,所有服务器都在一个私有网络中。 如果我们在开发基于Internet的应用程序,并且安全需求不约束在公共的web/应用服务器中实现业务逻辑。考虑4层: 如果安全需求规定业务逻辑不能在DMZ中部署业务逻辑。 如果我们的应用程序使用服务器上的资源很厉害并且我们希望把功能分离到另一个服务器。在大多数情况下推荐让所有的应用程序代码放在相同服务器。任何需要跨物理边界的需求都会影响性能因为数据需要在这些边界进行序列化。然而,有一些情况下我们需要跨服务器分离功能。此外,根据服务器的位置我们可以选择性能最优化的通讯协议。检查点在结束本步骤之前,你应该可以回答如下问题: 安全需求是否规定业务逻辑不能部署在公开的DMZ中? 我们是否在开发独立的只需要访问数据库的客户端? 我们是否在开发web应用程序或web服务? 我们是否在开发有多个客户端的应用程序? 第四步——选择通讯协议说到设计中用于跨逻辑层或物理层进行通讯的物理协议,通讯协议的选择对我们应用程序的性能起巨大作用。选择通讯协议的一个主要的地方就是使用服务和数据库进行交互。 Windows Communication Foundation (WCF) –直接支持四种协议: HTTP –用于在Internet上公开的服务。 TCP –用于在网络内部署的服务。 NamedPipes –用于服务和其使用者部署在同一服务器的服务。 MessageQueue –用于通过微软消息队列实现进行访问的服务。 Database –支持和WCF一样的许多协议。 HTTP –用于在Internet上公开的数据库。 TCP –用于在网络内部署的数据库。
7. 交叉编译器 arm-linux-gnueabi 和 arm-linux-gnueabihf 的区别
自己之前一直没搞清楚这两个交叉编译器到底有什么问题,特意google一番,总结如下,希望能帮到道上和我有同样困惑的兄弟…..
一. 什么是ABI和EABI
1) ABI: 二进制应用程序接口(Application Binary Interface (ABI) for the ARM Architecture)
在计算机中,应用二进制接口描述了应用程序(或者其他类型)和操作系统之间或其他应用程序的低级接口.
ABI涵盖了各种细节,如:
数据类型的大小、布局和对齐;
调用约定(控制着函数的参数如何传送以及如何接受返回值),例如,是所有的参数都通过栈传递,还是部分参数通过寄存器传递;哪个寄存器用于哪个函数参数;通过栈传递的第一个函数参数是最先push到栈上还是最后;
系统调用的编码和一个应用如何向操作系统进行系统调用;
以及在一个完整的操作系统ABI中,目标文件的二进制格式、程序库等等。
一个完整的ABI,像Intel二进制兼容标准 (iBCS) ,允许支持它的操作系统上的程序不经修改在其他支持此ABI的操作体统上运行。
ABI不同于应用程序接口(API),API定义了源代码和库之间的接口,因此同样的代码可以在支持这个API的任何系统中编译,ABI允许编译好的目标代码在使用兼容ABI的系统中无需改动就能运行。
2) EABI: 嵌入式ABI
嵌入式应用二进制接口指定了文件格式、数据类型、寄存器使用、堆积组织优化和在一个嵌入式软件中的参数的标准约定。
开发者使用自己的汇编语言也可以使用EABI作为与兼容的编译器生成的汇编语言的接口。
支持EABI的编译器创建的目标文件可以和使用类似编译器产生的代码兼容,这样允许开发者链接一个由不同编译器产生的库。
EABI与关于通用计算机的ABI的主要区别是应用程序代码中允许使用特权指令,不需要动态链接(有时是禁止的),和更紧凑的堆栈帧组织用来节省内存。广泛使用EABI的有Power PC和ARM.
二. gnueabi相关的两个交叉编译器: gnueabi和gnueabihf
在debian源里这两个交叉编译器的定义如下:
gcc-arm-linux-gnueabi – The GNU C compiler for armel architecture
gcc-arm-linux-gnueabihf – The GNU C compiler for armhf architecture
可见这两个交叉编译器适用于armel和armhf两个不同的架构, armel和armhf这两种架构在对待浮点运算采取了不同的策略(有fpu的arm才能支持这两种浮点运算策略)
其实这两个交叉编译器只不过是gcc的选项-mfloat-abi的默认值不同. gcc的选项-mfloat-abi有三种值soft,softfp,hard(其中后两者都要求arm里有fpu浮点运算单元,soft与后两者是兼容的,但softfp和hard两种模式互不兼容):
soft : 不用fpu进行浮点计算,即使有fpu浮点运算单元也不用,而是使用软件模式。
softfp : armel架构(对应的编译器为gcc-arm-linux-gnueabi)采用的默认值,用fpu计算,但是传参数用普通寄存器传,这样中断的时候,只需要保存普通寄存器,中断负荷小,但是参数需要转换成浮点的再计算。
hard : armhf架构(对应的编译器gcc-arm-linux-gnueabihf)采用的默认值,用fpu计算,传参数也用fpu中的浮点寄存器传,省去了转换, 性能最好,但是中断负荷高。
把以下测试使用的c文件内容保存成mfloat.c:
#include <stdio.h>
int main(void)
{
double a,b,c;
a = 23.543;
b = 323.234;
c = b/a;
printf(“the 13/2 = %f\n”, c);
printf(“hello world !\n”);
return 0;
}
1)使用arm-linux-gnueabihf-gcc编译,使用“-v”选项以获取更详细的信息:
# arm-linux-gnueabihf-gcc -v mfloat.c
COLLECT_GCC_OPTIONS=’-v’ ‘-march=armv7-a’ ‘-mfloat-abi=hard’ ‘-mfpu=vfpv3-d16′ ‘-mthumb’
-mfloat-abi=hard,可看出使用hard硬件浮点模式。
2)使用arm-linux-gnueabi-gcc编译:
# arm-linux-gnueabi-gcc -v mfloat.c
COLLECT_GCC_OPTIONS=’-v’ ‘-march=armv7-a’ ‘-mfloat-abi=softfp’ ‘-mfpu=vfpv3-d16′ ‘-mthumb’
-mfloat-abi=softfp,可看出使用softfp模式。
三. 拓展阅读
下文阐述了ARM代码编译时的软浮点(soft-float)和硬浮点(hard-float)的编译以及链接实现时的不同。从VFP浮点单元的引入到软浮点(soft-float)和硬浮点(hard-float)的概念
VFP (vector floating-point)
从ARMv5开始,就有可选的 Vector Floating Point (VFP) 模块,当然最新的如 Cortex-A8, Cortex-A9 和 Cortex-A5 可以配置成不带VFP的模式供芯片厂商选择。
VFP经过若干年的发展,有VFPv2 (一些 ARM9 / ARM11)、 VFPv3-D16(只使用16个浮点寄存器,默认为32个)和VFPv3+NEON (如大多数的Cortex-A8芯片) 。对于包含NEON的ARM芯片,NEON一般和VFP公用寄存器。
硬浮点Hard-float
编译器将代码直接编译成发射给硬件浮点协处理器(浮点运算单元FPU)去执行。FPU通常有一套额外的寄存器来完成浮点参数传递和运算。
使用实际的硬件浮点运算单元FPU当然会带来性能的提升。因为往往一个浮点的函数调用需要几个或者几十个时钟周期。
软浮点 Soft-float
编译器把浮点运算转换成浮点运算的函数调用和库函数调用,没有FPU的指令调用,也没有浮点寄存器的参数传递。浮点参数的传递也是通过ARM寄存器或者堆栈完成。
现在的Linux系统默认编译选择使用hard-float,即使系统没有任何浮点处理器单元,这就会产生非法指令和异常。因而一般的系统镜像都采用软浮点以兼容没有VFP的处理器。
armel ABI和armhf ABI
在armel中,关于浮点数计算的约定有三种。以gcc为例,对应的-mfloat-abi参数值有三个:soft,softfp,hard。
soft是指所有浮点运算全部在软件层实现,效率当然不高,会存在不必要的浮点到整数、整数到浮点的转换,只适合于早期没有浮点计算单元的ARM处理器;
softfp是目前armel的默认设置,它将浮点计算交给FPU处理,但函数参数的传递使用通用的整型寄存器而不是FPU寄存器;
hard则使用FPU浮点寄存器将函数参数传递给FPU处理。
需要注意的是,在兼容性上,soft与后两者是兼容的,但softfp和hard两种模式不兼容。
默认情况下,armel使用softfp,因此将hard模式的armel单独作为一个abi,称之为armhf。
而使用hard模式,在每次浮点相关函数调用时,平均能节省20个CPU周期。对ARM这样每个周期都很重要的体系结构来说,这样的提升无疑是巨大的。
在完全不改变源码和配置的情况下,在一些应用程序上,使用armhf能得到20%——25%的性能提升。对一些严重依赖于浮点运算的程序,更是可以达到300%的性能提升。
Soft-float和hard-float的编译选项
在CodeSourcery gcc的编译参数上,使用-mfloat-abi=name来指定浮点运算处理方式。-mfpu=name来指定浮点协处理的类型。
可选类型如fpa,fpe2,fpe3,maverick,vfp,vfpv3,vfpv3-fp16,vfpv3-d16,vfpv3-d16-fp16,vfpv3xd,vfpv3xd-fp16,neon,neon-fp16,vfpv4,vfpv4-d16,fpv4-sp-d16,neon-vfpv4等。
使用-mfloat-abi=hard (等价于-mhard-float) -mfpu=vfp来选择编译成硬浮点。使用-mfloat-abi=softfp就能兼容带VFP的硬件以及soft-float的软件实现,运行时的连接器ld.so会在执行浮点运算时对于运算单元的选择,
是直接的硬件调用还是库函数调用,是执行/lib还是/lib/vfp下的libm。-mfloat-abi=soft (等价于-msoft-float)直接调用软浮点实现库。
在ARM RVCT工具链下,定义fpu模式:
–fpu softvfp
–fpu softvfp+vfpv2
–fpu softvfp+vfpv3
–fpu softvfp+vfpv_fp16
–fpu softvfp+vfpv_d16
–fpu softvfp+vfpv_d16_fp16.
定义浮点运算类型
–fpmode ieee_full : 所有单精度float和双精度double的精度都要和IEEE标准一致,具体的模式可以在运行时动态指定;
–fpmode ieee_fixed : 舍入到最接近的实现的IEEE标准,不带不精确的异常;
–fpmode ieee_no_fenv :舍入到最接近的实现的IEEE标准,不带异常;
–fpmode std :非规格数flush到0、舍入到最接近的实现的IEEE标准,不带异常;
–fpmode fast : 更积极的优化,可能会有一点精度损失。
8. 如何让编译器架构Android.mk动态
Android.mk文件用来告知NDK Build 系统关于Source的信息。 Android.mk将是GNU Makefile的一部分,且将被Build System解析一次或多次。
所以,请尽量少的在Android.mk中声明变量,也不要假定任何东西不会在解析过程中定义。
Android.mk文件语法允许我们将Source打包成一个"moles". moles可以是:
静态库
动态库。
只有动态库可以被 install/到应用程序包(APK). 静态库则可以被链接入动态库。
可以在一个Android.mk中定义一个或多个moles. 也可以将同一份source 加进多个moles.
Build System帮我们处理了很多细节而不需要我们再关心。例如:你不需要在Android.mk中列出头文件和外部依赖文件。
NDK Build System自动帮我们提供这些信息。这也意味着,当用户升级NDK后,你将可以受益于新的toolchain/platform而不必再去修改Android.mk.
1. Android.mk语法:
首先看一个最简单的Android.mk的例子:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
include $(BUILD_SHARED_LIBRARY)
讲解如下:
LOCAL_PATH := $(call my-dir)
每个Android.mk文件必须以定义LOCAL_PATH为开始。它用于在开发tree中查找源文件。
宏my-dir则由Build System提供。返回包含Android.mk的目录路径。
include $(CLEAR_VARS)
CLEAR_VARS 变量由Build System提供。并指向一个指定的GNU Makefile,由它负责清理很多LOCAL_xxx.
例如:LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES等等。但不清理LOCAL_PATH.
这个清理动作是必须的,因为所有的编译控制文件由同一个GNU Make解析和执行,其变量是全局的。所以清理后才能避免相互影响。
LOCAL_MODULE := hello-jni
LOCAL_MODULE模块必须定义,以表示Android.mk中的每一个模块。名字必须唯一且不包含空格。
Build System会自动添加适当的前缀和后缀。例如,foo,要产生动态库,则生成libfoo.so. 但请注意:如果模块名被定为:libfoo.则生成libfoo.so. 不再加前缀。
LOCAL_SRC_FILES := hello-jni.c
LOCAL_SRC_FILES变量必须包含将要打包如模块的C/C++ 源码。
不必列出头文件,build System 会自动帮我们找出依赖文件。
缺省的C++源码的扩展名为.cpp. 也可以修改,通过LOCAL_CPP_EXTENSION。
include $(BUILD_SHARED_LIBRARY)
BUILD_SHARED_LIBRARY:是Build System提供的一个变量,指向一个GNU Makefile Script。
它负责收集自从上次调用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息。并决定编译为什么。
BUILD_STATIC_LIBRARY:编译为静态库。
BUILD_SHARED_LIBRARY :编译为动态库
BUILD_EXECUTABLE:编译为Native C可执行程序
2. NDK Build System变量:
NDK Build System 保留以下变量名:
以LOCAL_ 为开头的
以PRIVATE_ ,NDK_ 或者APP_ 开头的名字。
小写字母名字:如my-dir
如果想要定义自己在Android.mk中使用的变量名,建议添加 MY_前缀。
2.1: NDK提供的变量:
此类GNU Make变量是NDK Build System在解析Android.mk之前就定义好了的。
2.1.1:CLEAR_VARS:
指向一个编译脚本。必须在新模块前包含之。
include $(CLEAR_VARS)
2.1.2:BUILD_SHARED_LIBRARY:
指向一个编译脚本,它收集自从上次调用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息。
并决定如何将你列出的Source编译成一个动态库。 注意,在包含此文件前,至少应该包含:LOCAL_MODULE and LOCAL_SRC_FILES 例如:
include $(BUILD_SHARED_LIBRARY)
2.1.3:BUILD_STATIC_LIBRARY:
与前面类似,它也指向一个编译脚本,
收集自从上次调用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息。
并决定如何将你列出的Source编译成一个静态库。 静态库不能够加入到Project 或者APK中。但它可以用来生成动态库。
LOCAL_STATIC_LIBRARIES and LOCAL_WHOLE_STATIC_LIBRARIES将描述之。
include $(BUILD_STATIC_LIBRARY)
2.1.4: BUILD_EXECUTABLE:
与前面类似,它也指向一个编译脚本,收集自从上次调用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息。
并决定如何将你列出的Source编译成一个可执行Native程序。 include $(BUILD_EXECUTABLE)
2.1.5:PREBUILT_SHARED_LIBRARY:
把这个共享库声明为 “一个” 独立的模块。
指向一个build 脚本,用来指定一个预先编译好多动态库。 与BUILD_SHARED_LIBRARY and BUILD_STATIC_LIBRARY不同,
此时模块的LOCAL_SRC_FILES应该被指定为一个预先编译好的动态库,而非source file. LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := foo-prebuilt # 模块名
LOCAL_SRC_FILES := libfoo.so # 模块的文件路径(相对于 LOCAL_PATH)
include $(PREBUILT_SHARED_LIBRARY) # 注意这里不是 BUILD_SHARED_LIBRARY
这个共享库将被拷贝到 $PROJECT/obj/local 和 $PROJECT/libs/<abi> (stripped) 主要是用在将已经编译好的第三方库
使用在本Android Project中。为什么不直接将其COPY到libs/armabi目录呢?因为这样做缺陷很多。下一节再详细说明。
2.1.6: PREBUILT_STATIC_LIBRARY:预先编译的静态库。 同上。
2.1.7: TARGET_ARCH: 目标CPU架构名。如果为“arm” 则声称ARM兼容的指令。与CPU架构版本无关。
2.1.8: TARGET_PLATFORM: 目标的名字。
2.1.9:TARGET_ARCH_ABI
Name of the target CPU+ABI
armeabi For ARMv5TE armeabi-v7a
2.1.10:TARGET_ABI
2.2: NDK提供的功能宏:
GNUMake 提供的功能宏,只有通过类似: $(call function) 的方式来得到其值,它将返回文本化的信息。
2.2.1: my-dir: $(call my-dir):
返回最近一次include的Makefile的路径。通常返回Android.mk所在的路径。它用来作为Android.mk的开头来定义LOCAL_PATH. LOCAL_PATH := $(call my-dir)
请注意:返回的是最近一次include的Makefile的路径。所以在Include其它Makefile后,再调用$(call my-dir)会返回其它Android.mk 所在路径。 例如:
LOCAL_PATH := $(call my-dir) declare one mole include $(LOCAL_PATH)/foo/Android.mk LOCAL_PATH := $(call my-dir) declare another mole
则第二次返回的LOCAL_PATH为:$PATH/foo。 而非$PATH.
2.2.2: all-subdir-makefiles:
返回一个列表,包含'my-dir'中所有子目录中的Android.mk。
例如: 结构如下: sources/foo/Android.mk sources/foo/lib1/Android.mk sources/foo/lib2/Android.mk
在If sources/foo/Android.mk 中, include $(call all-subdir-makefiles) 那则自动include 了sources/foo/lib1/Android.mk and sources/foo/lib2/Android.mk。
2.2.3:this-makefile:
当前Makefile的路径。
2.2.4:parent-makefile:
返回include tree中父Makefile 路径。 也就是include 当前Makefile的Makefile Path。
2.2.5:import-mole:
允许寻找并inport其它moles到本Android.mk中来。 它会从NDK_MODULE_PATH寻找指定的模块名。 $(call import-mole,<name>)
2.3: 模块描述变量:
此类变量用来给Build System描述模块信息。在'include $(CLEAR_VARS)' 和 'include $(BUILD_XXXXX)'之间。必须定义此类变量。 include $(CLEAR_VARS) script用来清空这些变量。
include $(BUILD_XXXXX)收集和使用这些变量。
2.3.1: LOCAL_PATH:
这个值用来给定当前目录。必须在Android.mk的开是位置定义之。
例如: LOCAL_PATH := $(call my-dir) LOCAL_PATH不会被include $(CLEAR_VARS) 清理。
2.3.2: LOCAL_MODULE:
moles名。在include $(BUILD_XXXXX)之前,必须定义这个变量。此变量必须唯一且不能有空格。
通常,由此变量名决定最终生成的目标文件名。
2.3.3: LOCAL_MODULE_FILENAME:
可选。用来override LOCAL_MODULE. 即允许用户重新定义最终生成的目标文件名。 LOCAL_MODULE := foo-version-1 LOCAL_MODULE_FILENAME := libfoo
2.3.4:LOCAL_SRC_FILES:
为Build Moles而提供的Source 文件列表。不需要列出依赖文件。 注意:文件相对于LOCAL_PATH存放,
且可以提供相对路径。 例如: LOCAL_SRC_FILES := foo.c \ toto/bar.c
2.3.5: LOCAL_CPP_EXTENSION:
指出C++ 扩展名。(可选) LOCAL_CPP_EXTENSION := .cxx 从NDK R7后,可以写多个:
LOCAL_CPP_EXTENSION := .cxx .cpp .cc
2.3.6:LOCAL_CPP_FEATURES:
可选。用来指定C++ features。 LOCAL_CPP_FEATURES := rtti
LOCAL_CPP_FEATURES := exceptions
2.3.7:LOCAL_C_INCLUDES:
一个可选的path列表。相对于NDK ROOT 目录。编译时,将会把这些目录附上。 LOCAL_C_INCLUDES := sources/foo LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo
2.3.8: LOCAL_CFLAGS:
一个可选的设置,在编译C/C++ source 时添加如Flags。
用来附加编译选项。 注意:不要尝试在此处修改编译的优化选项和Debug等级。它会通过您Application.mk中的信息自动指定。
也可以指定include 目录通过:LOCAL_CFLAGS += -I<path>。 这个方法比使用LOCAL_C_INCLUDES要好。因为这样也可以被ndk-debug使用。
2.3.9: LOCAL_CXXFLAGS: LOCAL_CPPFLAGS的别名。
2.3.10: LOCAL_CPPFLAGS:
C++ Source 编译时添加的C Flags。这些Flags将出现在LOCAL_CFLAGS flags 的后面。
2.3.11: LOCAL_STATIC_LIBRARIES:
要链接到本模块的静态库list。(built with BUILD_STATIC_LIBRARY)
2.3.12: LOCAL_SHARED_LIBRARIES:
要链接到本模块的动态库。
2.3.13:LOCAL_WHOLE_STATIC_LIBRARIES:
静态库全链接。 不同于LOCAL_STATIC_LIBRARIES,类似于使用--whole-archive
2.3.14:LOCAL_LDLIBS:
linker flags。 可以用它来添加系统库。 如 -lz: LOCAL_LDLIBS := -lz
2.3.15: LOCAL_ALLOW_UNDEFINED_SYMBOLS:
2.3.16: LOCAL_ARM_MODE:
缺省模式下,ARM目标代码被编译为thumb模式。每个指令16位。如果指定此变量为:arm。 则指令为32位。 LOCAL_ARM_MODE := arm 其实也可以指定某一个或者某几个文件的ARM指令模式。
2.3.17: LOCAL_ARM_NEON:
设置为true时,会讲浮点编译成neon指令。这会极大地加快浮点运算(前提是硬件支持)
只有targeting 为 'armeabi-v7a'时才可以。
2.3.18:LOCAL_DISABLE_NO_EXECUTE:
2.3.19: LOCAL_EXPORT_CFLAGS:
定义这个变量用来记录C/C++编译器标志集合,
并且会被添加到其他任何以LOCAL_STATIC_LIBRARIES和LOCAL_SHARED_LIBRARIES的模块的LOCAL_CFLAGS定义中 LOCAL_SRC_FILES := foo.c bar.c.arm
注意:此处NDK版本为NDK R7C.(不同NDK版本,ndk-build所产生的Makefile并不完全相同)
9. 软件行业里常说的“架构”,究竟是什么东西
一直以来,在软件行业,对于什么是架构,都有很多的争论,每个人都有自己的理解。甚至于很多架构师一说架构,就开始谈论什么应用架构、硬件架构、数据架构等等。我曾经也到处寻找过架构的定义,请教过很多人,结果发现,没有大家都认可的定义。套用一句关于 big data 流行的笑话,放在架构上也适用:
Architecture is like teenage sex,everybody talks about it,nobody really knows what is it。
事实上,架构在软件发明时的 N 多年以前,就已经存在了,这个词最早是跟随着建筑出现的。所以,我觉得有必要从源头开始,把架构这个概念先讨论清楚,只有这样,软件行业架构的讨论才有意义。
什么是架构?
架构的英文是 Architecture,在 Wikipedia 上,架构是这样定义的:
Architecture (Latin architectura, from the Greek ἀρχιτέκτων arkhitekton” architect”, from ἀρχι- “chief” and τέκτων “builder”) is both the process and the proct of planning, designing, and constructing buildings and other physical structures。
从这个定义上看,架构好像是一个过程,也不是很清晰。为了讲清楚这个问题,我们先来看看为什么会产生架构。
为什么会产生架构?
想象一下,在最早期,每个人都完全独立生活,衣、食、住、行等等全部都自己搞定,整个人类都是独立的个体,不相往来。为了解决人类的延续的问题,自然而然就有男女群居出现,这个时候就出现了分工了,男性和女性所做的事情就会有一定的分工,可是人每天生活的基本需求没有发生变化,还是衣食住行等生活必须品。
但是一旦多人分工配合作为生存的整体,力量就显得强大多了,所以也自然的形成了族群:有些人种田厉害,有些人制作工具厉害,有些地方适合产出粮食,有些地方适合产出棉花等,就自然形成了人的分群,地域的分群。当分工发生后,实际上每个人的生产力都得到了提高,因为做的都是每个人擅长的事情。
整个人群的生产力和抵抗环境的能力都得到了增强。为什么呢?因为每个人的能力和时间都是有限的,并且因为人的结构的限制,人同时只能专心做好一件事情,这样不得已就导致了分工的产生。既然分工发生了,原来由一个人干生存所必需的所有的事情,就变成了很多不同分工的角色合作完成这些事情,这些人必须要通过某些机制合在一起,让每个人完成生存所必需的事情,这实际上也导致了交易的发生(交易这部分就不在这里展开了,有机会再讨论)。
在每个人都必须自己完成所有生活必须品的生产的时候,是没有架构的(当然在个人来讲,同一时刻只能做有限的事情,在时间上还是可能会产生架构的)。一旦产生的分工,就把所有的事情,切分成由不同角色的人来完成,最后再通过交易,使得每个个体都拥有生活必须品,而不需要每个个体做所有的事情,只需要每个个体做好自己擅长的事情,并具备一定的交易能力即可。
这实际上就形成了社会的架构。那么怎么定义架构呢?以上面这个例子为例,把一个整体(完成人类生存的所有工作)切分成不同的部分(分工),由不同角色来完成这些分工,并通过建立不同部分相互沟通的机制,使得这些部分能够有机的结合为一个整体,并完成这个整体所需要的所有活动,这就是架构。由以上的例子,也可以归纳出架构产生的动力:
必须由人执行的工作(不需要人介入,就意味着不需要改造,也就不需要架构了)
每个人的能力有限(每个人都有自己的强项,个人的产出受限于最短板,并且由于人的结构限制,同时只能专注于做好一件事情,比如虽然有两只眼睛,但是只能同时专注于一件事物,有两只手,无法同时做不同的事情。ps. 虽然有少部分人可以左手画圆右手画框,但是不是普遍现象)
每个人的时间有限(为了减少时间的投入,必然会导致把工作分解出去,给擅长于这些工作的角色来完成,见 2,从而缩短时间)
人对目标系统有更高的要求(如果满足于现状,也就不需要进行架构了)
目标系统的复杂性使得单个人完成这个系统,满足条件 2,3(如果个人就可以完成系统的提高,也不需要别的人参与,也就不需要架构的涉及,只是工匠,并且一般这个工作对时间的要求也不迫切。当足够熟练之后,也会有一定的架构思考,但考虑更多的是如何提高质量,提高个人的时间效率)
有人可能会挑战说,如果一个人对目标系统进行分解,比如某人建一栋房子,自己采购材料,自己搭建,难道也不算架构嘛?如果对于时间不敏感的话,是会出现这个情况的,但是在这种情况下,并不必然导致架构的发生。如果有足够的自觉,以及足够的熟练的话,也会产生架构的思考,因为这样对于提高生产力是有帮助的,可以缩短建造的时间,并会提高房子的质量。事实上建筑的架构就是在长期进行这些活动后,积累下来的实践。
当这 5 个条件同时成立,一定会产生架构。从这个层面上来说,架构是人类发展过程中,由懵懵懂懂的,被动的去认识这个世界,变成主动的去认识,并以更高的效率去改造这个世界的方法。以下我们再拿建筑来举例加强一下理解。
最开始人类是住在山洞里,住在树上的,主要是为了躲避其他猛兽的攻击,以及减少自然环境的变化,对人类生存的挑战。为了完成这些目标,人类开始学会在平地上用树木和树叶来建立隔离空间的设施,这就是建筑的开始。但是完全隔离也有很多坏处,慢慢就产生了门窗等设施。
建筑的本质就是从自然环境中,划出一块独占的空间,但是仍然能够通过门窗等和自然环境保持沟通。这个时候架构就已经开始了。对地球上的空间进行切分,并通过门窗,地基等,保持和地球以及空间的有机的沟通。当人类开始学会用火之后,茅棚里面自然而然慢慢就会被切分为两部分,一部分用来烧饭,一部分用来生活。当人的排泄慢慢移入到室内后,洗手间也就慢慢的出现了。这就是建筑内部的空间切分。
这个时候人们对建筑的需求也就慢慢的越来越多,空间的切分也会变成很多种,组合的方式也会有很多种,比如每个人住的房子,群居所产生的宗教性质的房子,集体活动的房子等等。这个时候人们就开始有意识的去设计房子,架构师就慢慢的出现了。一切都是为了满足人的越来越高的需求,提升质量,减少时间,更有效率的切分空间,并且让空间之间更加有机的进行沟通。这就是建筑的架构以及建筑的架构的演变
总结一下,什么是架构,就是:
根据要解决的问题,对目标系统的边界进行界定。
并对目标系统按某个原则的进行切分。切分的原则,要便于不同的角色,对切分出来的部分,并行或串行开展工作,一般并行才能减少时间。
并对这些切分出来的部分,设立沟通机制。
根据 3,使得这些部分之间能够进行有机的联系,合并组装成为一个整体,完成目标系统的所有工作。
同样这个思考可以展开到其他的行业,比如企业的架构,国家的架构,组织架构,音乐架构,色彩架构,软件架构等等。套用三国演义的一句话,合久必分,分久必合。架构实际上就是指人们根据自己对世界的认识,为解决某个问题,主动地、有目的地去识别问题,并进行分解、合并,解决这个问题的实践活动。架构的产出物,自然就是对问题的分析,以及解决问题的方案:包括拆分的原则以及理由,沟通合并的原则以及理由,以及拆分,拆分出来的各个部分和合并所对应的角色和所需要的核心能力等。
望采纳!
10. 企业MPR系统软件体系架构有哪些
摘要 - 分层模式 -