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系統軟體體系架構有哪些
摘要 - 分層模式 -