Managed 執行程序
Managed 執行程式包含將於本主題中稍後詳細討論的步驟:
選擇編譯器
若要取得 Common Language Runtime 提供的好處,您必須使用一個或多個以執行階段為目標的語言編譯器。
編譯您的程式碼為 MSIL。
編譯會將您的原始程式碼轉譯成 Microsoft intermediate language (MSIL),並產生必要的中繼資料。
將 MSIL 編譯成機器碼
在執行期間,Just-in-Time (JIT) 編譯器會將 MSIL 轉譯成機器碼。 在這個編譯 (Compilation) 期間,程式碼必須傳遞會檢查 MSIL 和中繼資料的驗證程序,以查明程式碼是否可以被確定為型別安全的。
執行程式碼
Common Language Runtime 提供基礎結構,讓執行以及可在執行期間使用的服務能夠運作。
選擇編譯器
若要取得 Common Language Runtime (CLR) 提供的好處,就必須使用一個或多個以執行階段為目標的語言編譯器,例如 Visual Basic、C#、Visual C++、F#,或眾多協力廠商編譯器的其中一個編譯器,例如 Eiffel、Perl 或 COBOL。
因為是多種程式語言的執行環境,執行階段支援各種資料型別和語言功能。 您使用的語言編譯器將決定有哪些執行階段功能可用,而您會使用那些功能來設計程式碼。 會建立程式碼必須使用的語法的是編譯器,而不是執行階段。 如果您的元件必須完全合用於其他語言撰寫的元件,元件的匯出型別必須只能公開包含在 Common Language Specification (CLS) 中的語言功能。 您可以使用 CLSCompliantAttribute 屬性 (Attribute) 來確保您的程式碼符合 CLS 標準。 如需詳細資訊,請參閱 撰寫符合 CLS 標準的程式碼。
回到頁首
編譯為 MSIL
編譯為 Managed 程式碼時,編譯器會將您的原始程式碼轉譯成 Microsoft Intermediate Language (MSIL),它是可以有效率地轉換為機器碼而與 CPU 無關的指令集。 MSIL 包括可用來載入、儲存、初始化和呼叫物件上方法的指令,以及用於數學和邏輯作業、控制流程、直接記憶體存取、例外處理和其他作業的指令。 在程式碼可以執行之前,必須將 MSIL 轉換為 CPU 特定程式碼,而此轉換通常是藉 Just-In-Time (JIT) 編譯器進行。 由於 Common Language Runtime 會為其支援的每一個電腦架構提供一個或多個 JIT 編譯器,因此相同的 MSIL 集可以在任何受支援的架構上進行 JIT 編譯並執行。
當編譯器產生 MSIL 時,它也會產生中繼資料。 中繼資料描述您程式碼中的型別,包括各個型別的定義、各個型別成員的簽章 (Signature)、您的程式碼參考的成員,和 Runtime 在執行期間使用的其他資料。 MSIL 和中繼資料是包含在可移植執行檔 (PE) 中,這個檔案會根據並擴充已發佈的 Microsoft PE,以及記錄上供可執行檔內容使用的通用物件檔案格式 (COFF)。 這個檔案格式 (適用於 MSIL 或機器碼以及中繼資料) 使作業系統能夠辨認 Common Language Runtime 影像。 檔案內中繼資料的存在連同 MSIL 讓您的程式碼能夠描述它自己,意即不需要型別程式庫或介面定義語言 (IDL)。 Runtime 會依需要在執行期間從檔案找出並擷取中繼資料。
回到頁首
將 MSIL 編譯成機器碼
Microsoft Intermediate Language (MSIL) 必須先根據 Common Language Runtime 編譯成機器碼 (該程式碼是根據目標電腦架構來編譯) 才能執行。 .NET Framework 提供兩種執行這項轉換的方式:
.NET Framework Just-In-Time (JIT) 編譯器。
.NET Framework Ngen.exe (原生映像產生器)。
使用 JIT 編譯器編譯
當載入和執行時組件內容時,JIT 編譯會視需要於應用程式執行階段將 MSIL 轉換成機器碼。 由於 Common Language Runtime 會為每個支援的 CPU 架構提供 JIT 編譯器,因此開發人員可以建置可在不同架構的電腦上進行 JIT 編譯和執行的 MSIL 組件集。 然而,如果您的 Managed 程式碼呼叫特定平台的原生 API 或特定平台的類別庫,便只能在特定的作業系統中執行。
JIT 編譯會考慮某些程式碼在執行期間可能從不會被呼叫的事實。 它並非使用時間和記憶體將 (PE) 中所有的 MSIL 轉換為記憶體中的機器碼,而是在執行期間依需要轉換 MSIL 並儲存產生的機器碼,以供該處理序內容中的後續呼叫存取。 載入和初始化型別時,載入器會建立和附加 Stub 至型別中的每一個方法。 初次呼叫方法時,Stub 會將控制項傳遞至 JIT 編譯器,該編譯器會將該方法的 MSIL 轉換成機器碼,並且將 Stub 修改為直接指向產生的機器碼。 因此,JIT 編譯方法的後續呼叫會直接進行至機器碼。
使用 NGen.exe 產生安裝階段程式碼
由於 JIT 編譯器會在呼叫該組件中所定義個別方法時,將組件的 MSIL 轉換成機器碼,因此這會對執行階段的效能產生不良影響。 在大部分情況下,效能稍減是可以接受的。 最重要的是,JIT 編譯器產生的程式碼會繫結至觸發編譯的處理序。 該程式碼無法跨多個處理序共用。 為了允許跨應用程式的多個引動過程,或共用組件集的多個處理序共用產生的程式碼,Common Language Runtime 支援事先編譯模式。 這個事先編譯模式會使用Ngen.exe (原生映像產生器) 將 MSIL 組件轉換成機器碼,與 JIT 編譯器相當類似。 不過,Ngen.exe 的作業與 JIT 編譯器的作業有三個不同的地方:
它會事先執行從 MSIL 至機器碼的轉換,而不是在執行應用程式時。
它會一次編譯整個組件,而非一次一個方法。
它會將原生映像快取中產生的程式碼保存為磁碟上的檔案。
程式碼驗證
在編譯成機器碼的過程中,MSIL 程式碼必須通過驗證程序,除非系統管理員建立的安全性原則允許程式碼略過驗證。 驗證會檢查 MSIL 和中繼資料 (Metadata),以找出該程式碼是否為型別安全,這表示它僅存取獲得授權可存取的記憶體位置。 型別安全有助於隔離出各物件,因此也就能夠免於無心或蓄意的毀損。 它也提供保證,程式碼的安全性限制將會可靠地強制執行。
對執行階段而言,一個可以被確認為型別安全的程式碼會滿足下列條件:
型別的參考與正被參考的型別完全相容
只有適當定義的作業在物件上被叫用
識別是它們所要求的
在驗證處理期間,MSIL 程式碼會受檢查以確認程式碼只會使用有正確定義的型別來存取記憶體位置和呼叫方法。 例如,對於一個會存取超出物件欄位記憶體範圍外位址的程式碼是不被允許的。 此外,驗證會檢視程式碼,以判斷 MSIL 是否已經正確產生,因為不正確的 MSIL 可能會違反型別安全規則。 驗證程序會讓妥善定義的型別安全程式碼集合通過驗證,它也只會讓型別安全的程式碼通過驗證。 然而,由於驗證程序的某些限制,某些型別安全程式碼可能無法通過驗證,而某些語言由於設計之故,不會產生可確認的型別安全程式碼。 如果安全性原則需要型別安全程式碼,但該程式碼沒有通過驗證,則在程式碼執行時會產生例外狀況。
回到頁首
執行程式碼
Common Language Runtime 提供基礎結構讓 Managed 程式碼執行,以及讓可在執行期間使用的服務能夠運作。 在可以執行方法之前,必須將其編譯為處理器特定程式碼。 已經產生 MSIL 的每個方法在初次呼叫時會進行 JIT 編譯,然後才執行。 下次執行方法時,就會執行現有以 JIT 編譯的機器碼。 JIT 編譯和接著執行程式碼的過程會不斷重複直到執行完成為止。
在執行期間,Managed 程式碼會接收服務,例如記憶體回收、安全性、與 Unmanaged 程式碼的互通性 (Interoperability)、跨語言偵錯支援,以及增強的部署和版本控制支援。
在 Microsoft Windows XP 和 Windows Vista 中,作業系統載入器藉由查看 COFF 前置資料中的某一位元,檢查 Managed 模組。 所設定的位元代表 Managed 模組。 如果載入器偵測到 Managed 模組,則會載入 mscoree.dll,而當載入和卸載 Managed 模組影像時,_CorValidateImage 和 _CorImageUnloading 會通知載入器。 _CorValidateImage 會執行下列動作:
確定程式碼是有效的 Managed 程式碼。
將影像中的進入點 (Entry Point) 變更為 Runtime 中的進入點。
在 64 位元的 Windows 中,_CorValidateImage 會將記憶體中的影像從 PE32 格式轉換為 PE32+ 格式,以便進行修改。
回到頁首