共用方式為


受控執行程序

受控執行程式包含下列步驟,本主題稍後會詳細討論:

  1. 選擇編譯程式。 若要取得 Common Language Runtime 所提供的優點,您必須使用以運行時間為目標的一或多個語言編譯程式。
  2. 將您的程式代碼編譯為中繼語言。 編譯會將原始碼轉譯為通用中繼語言 (CIL),併產生所需的元數據。
  3. 將 CIL(Common Intermediate Language)編譯為機器碼。 在執行時間,Just-In-Time (JIT) 編譯程式會將 CIL 編譯成原生碼。 在此編譯期間,程式代碼必須通過驗證程式來檢查 CIL 和元數據,以找出程式代碼是否可以判斷為類型安全。
  4. 執行代碼。 Common Language Runtime 提供基礎結構,讓執行能夠進行,以及可在執行期間使用的服務。

選擇編譯程式

若要取得 Common Language Runtime (CLR) 所提供的優點,您必須使用以運行時間為目標的一或多個語言編譯程式,例如 Visual Basic、C#、Visual C++、F# 或許多第三方編譯程式之一,例如 Eiffel、Perl 或 COBOL 編譯程式。

因為它是多語言執行環境,因此運行時間支援各種不同的數據類型和語言功能。 您使用的語言編譯程式會決定可用的運行時間功能,並使用這些功能來設計程序代碼。 您的編譯程式在執行時期而不是運行時設定您的程式碼需要使用的語法。 如果您的元件必須由以其他語言撰寫的元件完全可用,則元件的導出類型必須只公開 Common Language Specification (CLS) 中包含的語言功能。 您可以使用 CLSCompliantAttribute 屬性來確保程序代碼符合 CLS 規範。 如需詳細資訊,請參閱 語言獨立和語言獨立元件

編譯為 CIL

編譯至Managed程式代碼時,編譯程式會將原始碼轉譯為通用中繼語言 (CIL),這是一組與 CPU 無關的指令,可有效率地轉換成機器碼。 CIL 包含載入、儲存、初始化和呼叫物件方法的指示,以及算術和邏輯作業、控制流程、直接記憶體存取、例外狀況處理和其他作業的指示。 執行程序代碼之前,CIL 必須轉換成 CPU 特定的程式代碼,通常是由 Just-In-Time (JIT) 編譯程式。 由於 Common Language Runtime 會針對所支援的每部電腦架構提供一或多個 JIT 編譯程式,因此相同的 CIL 集合可以進行 JIT 編譯,並在任何支援的架構上執行。

當編譯程式產生 CIL 時,它也會產生元數據。 元數據描述程式代碼中的類型,包括每個類型的定義、每個類型成員的簽章、程式代碼參考的成員,以及運行時間使用的其他數據。 CIL 和元數據包含在可攜式可執行檔 (PE) 檔案中,該檔案是以 為基礎,而且會擴充過去用於可執行文件內容的已發布Microsoft PE 和通用物件檔格式 (COFF)。 此檔案格式可容納 CIL 或本機程式碼以及元數據,使作業系統能夠識別 Common Language Runtime 映像檔。 檔案中元數據的存在與 CIL 可讓程式代碼描述本身,這表示不需要類型庫或介面定義語言 (IDL)。 運行時間會視需要在執行期間,從檔案中尋找並擷取元數據。

將 CIL 編譯為機器碼

在您能執行通用中繼語言(CIL)之前,必須先將其針對通用語言執行平台編譯為原生碼,以符合目標機器的架構。 .NET 提供兩種方式來執行此轉換:

由 JIT 編譯器進行的編譯

JIT 編譯會在應用程式執行時根據需求將 CIL 轉換為本地程式碼,當組件的內容被載入並執行時進行。 由於 Common Language Runtime 會為每個支援的 CPU 架構提供 JIT 編譯程式,因此開發人員可以建置一組 CIL 元件,這些元件可以進行 JIT 編譯,並在具有不同計算機架構的不同計算機上執行。 不過,如果您的 Managed 程式代碼呼叫平臺特定的原生 API 或平臺特定類別庫,它只會在該作系統上執行。

JIT 編譯會考慮某些程式代碼在執行期間可能永遠不會呼叫的可能性。 與其使用時間和記憶體將PE檔案中的所有 CIL 轉換成原生程式代碼,而是在執行期間視需要轉換 CIL,並將產生的機器碼儲存在記憶體中,以便在該程式的內容中供後續呼叫存取。 載入器會在載入並初始化類型時,建立存根並附加至類型中的每個方法。 第一次呼叫方法時,存根會將控件傳遞給 JIT 編譯程式,將該方法的 CIL 轉換成機器碼,並修改存根直接指向產生的原生程式代碼。 因此,後續對 JIT 編譯方法的呼叫會直接移至機器碼。

使用 NGen.exe 產生安裝時程式碼

由於 JIT 編譯器在呼叫組合語言中定義的個別方法時,會將組合語言的 CIL 轉換為原生程式碼,這會在執行時對效能造成負面影響。 在大部分情況下,效能降低是可以接受的。 更重要的是,JIT 編譯程式所產生的程式代碼會系結至觸發編譯的進程。 它無法跨多個進程共用。 為了允許產生的程式代碼跨應用程式的多個調用或跨共用一組元件的多個進程共用,Common Language Runtime 支援預先編譯模式。 這種預先編譯模式會使用 Ngen.exe (原生映射產生器) 將 CIL 元件轉換成原生程式代碼,就像 JIT 編譯程式所做的一樣。 不過,Ngen.exe 的作業與 JIT 編譯程式的作業有三種不同:

  • 它會在執行應用程式之前,將 CIL 轉換為機器碼,而不是在應用程式運行時進行轉換。
  • 它會一次編譯整個組件,而不是一次編譯一個方法。
  • 它會將原生映像快取中產生的程式代碼保存為磁碟上的檔案。

程式代碼驗證

在編譯為機器碼的過程中,CIL 程式代碼必須通過驗證程式,除非系統管理員已建立允許程式代碼略過驗證的安全策略。 驗證會檢查 CIL 和元數據,以找出程式代碼的類型是否安全,這表示只會存取其授權存取的記憶體位置。 類型安全性有助於隔離物件彼此,並協助保護物件免於意外或惡意損毀。 它也保證可以可靠地強制執行程式碼的安全性限制。

執行環境依賴於以下條件,即對於可驗證類型安全的程式碼,下列語句是正確的:

  • 型別的參考與所參考的類型嚴格相容。
  • 僅會在物件上叫用已適當定義的作業。
  • 身份就是他們所聲稱的。

在驗證程式期間,會檢查 CIL 程式代碼,以嘗試確認程式代碼可以存取記憶體位置,並只透過正確定義的類型呼叫方法。 例如,程式碼不應使物件的欄位以可能造成記憶體位置溢出的方式被存取。 此外,驗證會檢查程序代碼,以判斷是否已正確產生 CIL,因為不正確的 CIL 可能會導致違反類型安全規則。 驗證程式會傳遞一組定義完善的型別安全程序代碼,而且只會傳遞類型安全的程序代碼。 不過,某些型別安全程式碼可能因為驗證程式的某些限制而無法通過驗證,而某些語言的設計不會產生可驗證的類型安全代碼。 如果安全策略需要型別安全碼,但程式代碼未通過驗證,則執行程式代碼時會擲回例外狀況。

執行程序代碼

共通語言執行階段提供基礎結構,可讓受控執行進行,並提供可在執行期間使用的服務。 執行方法之前,必須先編譯為處理器特定的程序代碼。 第一次呼叫 CIL 時,產生 CIL 的每個方法都會進行 JIT 編譯,然後執行。 下次執行 方法時,會執行現有的 JIT 編譯原生程序代碼。 JIT 編譯和執行程式碼的過程會重複進行,直到執行完成為止。

在執行期間,Managed 程式代碼會接收垃圾收集、安全性、與 Unmanaged 程式代碼的互作性、跨語言偵錯支援,以及增強的部署和版本控制支援等服務。

在 Microsoft Windows Vista 中,作業系統載入器會透過檢查 COFF 標頭中的一個位元來確認受控模組。 所設定的位元表示受控模組。 如果載入器偵測到受控模組,它會載入 mscoree.dll,並在_CorValidateImage_CorImageUnloading載入和卸除受管理的模組映射時通知載入器。 _CorValidateImage 會執行下列動作:

  1. 確保程式碼是有效的受控程式碼。
  2. 將映像中的進入點變更為運行時間中的進入點。

在 64 位 Windows 上, _CorValidateImage 將映像從 PE32 轉換為 PE32+ 格式來修改記憶體中的映像。

另請參閱