如何理解裝配件、元數據、PE文件和持久化

裝配件

在.NET框架平臺中,裝配件(Assembly)是一個嶄新的概念。它是.NET框架應用程序的基礎生成區塊。它是一個功能的集合,可以被當作一個獨立的實現單元被生成、版本化和發佈。從本質上講,裝配件是一個邏輯上的動態鏈接庫。
作爲一名開發人員,應當對裝配件有如下了解:
● 裝配件包含CLR可以執行的程序代碼
中間代碼包含在可執行模塊文件(如EXE、DLL)中,而這些模塊文件是組成裝配件的重要成員。
● 裝配件組成了一個安全邊界
在執行任何託管的代碼時都需要符合兩個條件:一是這些代碼必須和清單(Manifest)一起封裝在裝配件中,二是CLR在加載時確認裝配件內容正確而且未被修改過。關於清單的知識我們稍後會做詳細介紹。
● 裝配件組成了一個類型邊界
它本身具有的邊界可以被某個類型當作其身份標識的一部分。例如,一個名叫ABCType的類型是從AssemblyA中加載而來的,它和另一個從AssemblyB中加載而來的同名類型是完全不同的,這種關係如圖2-31所示。
 
 
 
圖2-31 ABCType != ABCType
● 裝配件組成了一個引用範圍邊界
在它內部含有一個清單,這個清單具有對類型和資源引用的分辨能力。清單指明瞭宿主裝配件中哪些類型和資源可以暴露給外界。另外,清單還可以記錄宿主裝配件對其他裝配件的依賴情況。
● 裝配件組成了一個版本邊界
它是CLR中最小的版本化單元—— 在同一個裝配件中所有的類型和資源都被版本化成一個單元。它的清單描述了對其他有裝配件的版本依賴。具體內容我們稍後會做介紹。
● 裝配件組成了一個發佈單元
當應用程序啓動時,僅僅需要那些在它初始化時必須要調用的裝配件存在即可,不必讓所有與之相關的裝配件都存在。例如本地化資源(Localization Resources),只要在被需要的時侯存在即可。這種機制可以更好地實現軟件分割,使下載的應用程序儘可能小些。有關託管的應用程序發佈方面的更詳細知識,稍後我們就要介紹。
● 裝配件組成了一個可以支持“肩並肩”運行的單元
“肩並肩”執行方式是解決“DLL災難”問題的有效辦法,詳細信息我們稍後會進行介紹。
單純從物理存儲的角度看,一個裝配件中包含四個組成部分—— 清單元數據、類型元數據、IL以及資源,如圖2-32所示。
從一個裝配件消費者(Consumer)的角度看,一個裝配件是一個被命名的和版本化了的輸出類型和資源的集合;從裝配件開發人員的角度,一個裝配件是一個或多個文件的集合—— 一個單獨的PE文件到多個PE文件、資源文件、HTML文件等等文件組成的集合體。
 
 
 
圖2-32 裝配件的組成(物理存儲)
既然裝配件是一羣可能由形形色色的文件組成的組合體,卻不能將系統中隨便幾個文件放在一起就稱其爲裝配件。裝配件不是“烏合之衆”的代名詞,在裝配件中存在着一個清單,這個清單包含組成裝配件所需要的所有條目(Items)的信息。清單明確地指出了哪些條目可以對外開放,哪些條目只可以在裝配件內部進行訪問,另外清單中也包含一個對其他裝配件進行引用的集合。爲了便於理解,可以用倉庫和倉庫保管員之間的關係作爲例子來說明裝配件和清單之間的關係—— 倉庫相當於裝配件,而保管員手中的倉儲明細登記表就相當於清單,因爲在這個登記表中,記錄了所有庫存商品的明細。
清單中存儲的信息在運行時被運行時用來進行引用分析,從而實現類型安全,以及進行安全檢查等。清單在裝配件消費者和裝配件具體實現之間構成了一個間接的層。清單的存在使裝配件可以實現自我描述(Self Description)。
如果一個裝配件僅僅由一個文件組成,則這個裝配件的清單必然存在於這個文件當中;如果裝配件是由多個文件組成的,那麼要麼選擇這些文件當中的一個文件(必須是PE文件)作爲清單保管員(Manifest Keeper),要麼就需要單獨建立一個文件,存放此清單。總而言之,只要是裝配件,就不能沒有清單。裝配件和清單的關係如圖2-33所示。
 
 
 
圖2-33 清單
在裝配件清單中,主要包含如下信息:
● 裝配件的名稱
包含一個文本字符串。
● 版本信息
包含主版本號、次版本號、修正版本號以及生成版本號。這些版本號可以讓運行時實現嚴格的版本策略,關於版本的具體內容,在第10章中將進行詳盡的介紹。
● 共享名稱信息
包含由公開密鑰和數字簽名組合而成的信息,關於共享名的具體內容,在第10章中將進行詳盡的介紹。
● 國別、處理器和操作系統信息
包含裝配件支持的國別、處理器以及操作系統信息。
● 裝配件中所有文件的列表
包含在裝配件中的每個文件的散列(hash)和一個與清單所在文件相關的路徑。
注意:
組成裝配件的所有文件必須和清單所在的文件在同一個目錄下。
● 類型引用信息
包含爲運行時所使用的類型引用信息。運行時可以將運行時中的類型引用根據描述映射到包含這個類型定義和實現的文件當中。
● 被當前裝配件引用的其他裝配件的信息
包含當前裝配件靜態引用的其他裝配件的列表。每個引用包含所依賴的裝配件的名稱、元數據以及共享名稱(如果這個裝配件是共享的)。有關共享裝配件以及元數據的討論,稍後進行介紹。
接下來,讓我們瞭解另外一個核心概念—— 元數據(Metadata),首先我們說明一下什麼是元數據。
元數據是包含類型描述和本身對其他裝配件的引用的詳細信息的數據塊。爲了讓運行時能夠爲託管的代碼的執行提供服務,語言編譯器必須可以產生元數據。元數據和中間代碼被一起存儲到PE文件,即每一個可加載的CLR映像(Image)都要包含元數據。運行時使用元數據來定位並加載類,在內存中完成佈置對象實例、分析方法調用、產生本機代碼、實施安全,以及設置運行時上下文(Context)邊界等任務。
爲了能儘早讓開發人員對元數據有一個感性認識,下面我們動手生成一個簡單的.NET應用程序,並通過這個程序中來了解元數據的有關知識。
這是一個簡單的輸出“Hello,World!”的小程序。如果閱讀該程序時,對裏面出現的一些語法感到困惑,請先不要急於搞懂它,稍後的的章節中會使用足夠的篇幅來講解這些代碼的含義,現在我們只是借用這個程序來說明問題。這段程序的源代碼可以在配書光盤的/SourceList/_Chap02/Hello目錄下找到,其代碼如下所示:
Hello.cpp
#using <mscorlib.dll>
// 使用名稱空間,
// 便於程序對存在於該名稱空間中的類進行調用
using namespace System;
// 全局函數main是應用程序的入口點
void main()
{
// 向控制檯輸出一行文本
Console::WriteLine(L"Hello World!");
}
請確保已經在機器上妥善地安裝了.NET Framework SDK或者Visual Studio.NET,然後按照如下步驟生成這個應用程序:
(1) 進入控制檯(也就是MS-DOS窗口)。
(2) 在Hello.cpp的所在的目錄下,輸入編譯命令:
cl.exe /CLR Hello.cpp
(3) 按Enter鍵確認,屏幕上會出現如下編譯信息:
hello.cpp
Microsoft (R) Incremental Linker Version 7.00.9030
Copyright (C) 1992-2000 Microsoft Corporation. All rights reserved.
/out:hello.exe
hello.obj
(4) 到此編譯成功結束。此時可以得到一個名爲hello.exe的可執行文件。
現在,我們要用到微軟提供的一個很有用的工具ILDasm.exe。使用這個工具可以對託管的應用程序進行反彙編,並可以形象地顯示出被查看應用程序所包含的元數據。開發人員可以在.NET框架所在的目錄下找到這個工具。
下面我們就使用這個工具來對hello.exe進行查看。在桌面上選擇“開始”→“程序” →Microsoft .NET Framework SDK→“工具”→ILDasm命令後,ILDasm.exe就會運行,它的執行結果如圖2-34所示。
 
 
 
圖2-34 ILDasm工具界面
可以清楚地看到,Hello.exe中的元數據被該工具以樹型結構顯示出來。使用鼠標雙擊MANIFEST節點,開發人員還可以看到該應用程序包含的清單內容,如圖2-35所示。
 
 
 
圖2-35 MANIFEST信息窗口
請特別注意被着重標記的地方,前兩個是對引用其他裝配件進行的描述,而第三個是對本身的描述。顯然,這是一個單獨文件構成的裝配件。
現在回到圖2-34,將注意力集中在其他節點上,通過查閱工具的聯機幫助,我們可以瞭解到那些色彩鮮豔的圖標的含義,因爲在後面的討論中還要經常用到它們,所以在這裏將它們整理出來,如表2-3所示。
表2-3 ILDasm工具圖例含義
 
 
圖    例
含    義
名稱空間(Namespace)
(Class)
接口(Interface)
值類(Value Class)
枚舉(Enum)
方法(Method)
靜態方法(Static Method)
字段(Field)
靜態字段(Static field)
事件(Event)
屬性(Property)
清單或類信息條目(Manifest or a Class info item)
 
 
 
對照表2-3,會發現在Hello.exe的元數據中包含了一個清單、一個枚舉類型(帶有兩個類信息條目)、一個靜態字段以及一個靜態方法。雙擊靜態方法的所在的節點,可以得到關於靜態方法Main的反彙編信息,如圖2-36所示。
 
 
 
圖2-36 靜態方法Main的反彙編代碼
一下子說了這麼多,可能讀者一時難以理解。造成這種理解上的障礙主要原因是因爲元數據、清單和裝配件之間層層嵌套、糾纏不清的關係。這個時候,我想應當好好整理一下思路,將三個概念之間的關係搞清楚。
爲了說明問題,我們舉個例子,假設AssemblyABC是一個多文件的裝配件,它由兩個文件組成,第1個是可執行文件A.EXE,第2個是動態鏈接庫文件B.DLL,則元數據、清單和裝配件的關係可以用圖2-37來表示。
在圖2-37中,我們可以看到幾個明顯的關係,這些關係分別是:
● 裝配件包含了一個或多個PE文件,在本例中是兩個PE文件。
● 注意元數據的範圍。在每個PE文件都存在一個元數據,並且和IL代碼沒有包含關係,它們是構成新型PE文件的重要組成部份中的兩個代表。
● 注意清單的位置。非常明顯,它是元數據的一部分。
● 注意清單扮演的角色。清單中闡述了組成裝配件的文件列表以及所在裝配件的輸出類型(還有其他)。
 
 
 
圖2-37 元數據、清單和裝配件的關係
元數據和清單的不同之處在於元數據在每個PE文件中都存在一個,而清單在一個裝配件中只能存在一個;清單是元數據中概念上比較特殊的一部分,但是在物理存儲上沒有什麼兩樣。另外,還記得持久化(Persistence)的概念嗎?由於CLR要在運行時從PE文件的元數據中讀取並創建對象,所以元數據就是實現應用程序持久化的基礎。
     *持久化:就是將在內存中的實體對象永久保存.    
     *持久層:就是把對象從內存中持久化到數據庫,文件等的數據庫操作層,爲業務邏輯層提供了面向對象的API.
     *PE文件指的是可移植可執行(Portable Executable)文件。這些文件通常是DLL和EXE文件。一個 PE 文件中可以有多個命名空間,也可以包括嵌套的命名空間。而一個命名空間可以拆分到多個 PE 文件。一個或多個 PE 文件(可能還有其它非PE文件,如資源文件)可以組合在一起創建程序集。程序集是可部署、可進行版本編號和可複用的物理單元。
發佈了23 篇原創文章 · 獲贊 2 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章