標籤: 暫無標籤

MSIL是將.Net代碼轉化為機器語言的一個中間過程。它是一種介於高級語言和基於Intel的彙編語言的偽彙編語言。當用戶編譯一個.Net程序時,編譯器將源代碼翻譯成一組可以有效地轉換為本機代碼且獨立於CPU 的指令。當執行這些指令時,實時(JIT)編譯器將它們轉化為CPU特定的代碼。由於公共語言運行庫支持多種實時編譯器,因此同一段MSIL代碼可以被不同的編譯器實時編譯並運行在不同的結構上。

1 MSIT -基本介紹

在.Net框架中,公共語言基礎結構使用公共語言規範來綁定不同的語言。通過要求不同的語言至少要實現公共類型系統(CTS)包含在公共語言規範中的部分,公共語言基礎結構允許不同的語言使用.Net框架。因此在.Net框架中,所有的語言(C#,VB.Net,Effil.Net等)最後都被轉換為了一種通用語言:微軟中間語言(MSIL)。

MSIL是將.Net代碼轉化為機器語言的一個中間過程。它是一種介於高級語言和基於Intel的彙編語言的偽彙編語言。當用戶編譯一個.Net程序時,編譯器將源代碼翻譯成一組可以有效地轉換為本機代碼且獨立於CPU 的指令。當執行這些指令時,實時(JIT)編譯器將它們轉化為CPU特定的代碼。由於公共語言運行庫支持多種實時編譯器,因此同一段MSIL代碼可以被不同的編譯器實時編譯並運行在不同的結構上。從理論上來說,MSIL將消除多年以來業界中不同語言之間的紛爭。在.Net的世界中可能出現下面的情況:一部分代碼可以用Effil實現,另一部分代碼使用C#或VB完成的,但是最後這些代碼都將被轉換為中間語言。這給程序員提供了極大的靈活性,程序員可以選擇自己熟悉的語言,並且再也不用為學習不斷推出的新語言而煩惱了。

解密微軟中間語言的系列文章將通過一些簡單易懂的方式來揭示中間語言的複雜原理。這些原理通過詳細的例子來闡述。在一些例子中同時給出了源代碼和中間代碼,通過比較源代碼和中間代碼,我們可以更好地理解編譯器的局限性,指導我們編寫出更好更快的代碼。

2 MSIT -微軟中間語言概述

1.用中間語言編寫的一個簡單程序
讓我們從經典的Hello World例子開始。首先在一個文本編輯器中輸入以下的代碼,並保存為HelloWorld.il:

.assembly HelloWorldIL {} .method static void HelloWorld() { .entrypoint ldstr "Hello World." call void [mscorlib]System.Console::WriteLine(class System.String) ret }

在一個中間語言程序中,如果某一行以「.」開始,則代表這是一個傳輸給彙編工具的指令,該指令要求彙編工具執行某些操作,例如生成一個函數或類。而沒有以「.」開始的行是中間語言的代碼。在中間語言中方法通過彙編命令method來定義,彙編命令後跟方法的返回值、名稱和參數。方法體被包含在{}中。例子中的ret代表該方法的結束。

一個中間語言文件可以包含很多函數,彙編工具沒有辦法分辨應該首先執行哪一個方法。在諸如C#或VB這一類高級語言中,程序的入口方法通常都有特定的名稱,例如在C#中的public static void Main()。這就是上面的彙編工具發出錯誤提示的原因。在中間語言中,第一個被執行的方法被稱為入口函數(EntryPoint Function)。為了告訴彙編工具HelloWorld是入口函數,我們需要在代碼中增加一條彙編命令entrypoint,該命令可以放在方法體中的任何位置。需要注意的是在一個程序集中只能有一個入口函數。

中間語言代碼通常被編譯成一個模塊,該模塊隸屬於一個程序集。在.Net中模塊和程序集的概念非常重要,因此開發人員需要很清楚地了解它們。在後面的文章中我們將詳細討論.Net程序的結構。通過在代碼中加入assembly命令,可以告訴彙編工具中間代碼隸屬於那個程序集。assembly命令的格式如下:

.assembly <程序集名稱> {}

需要注意在method命令后加入了static關鍵字,這是因為每個入口函數必須是靜態的,例如在C#中我們將Main方法定義為public static void Main()。

接下來我們需要調用WriteLine方法將HelloWorld字元串輸出到屏幕。通過使用call指令(Instruction)我們可以達到這個目的。指令的格式如下:

call <return type> <namespace>.<class name>::<method name>

這裡我們可以看到當調用一個方法時,中間語言和其他的編程語言有很大的區別。在中間語言中,如果需要調用一個方法,需要指定方法的全名,包括他的名稱域(namespace)、類名、返回值類型和參數的數據類型。這樣就保證了彙編工具能夠找到正確的方法。

在調用WriteLine方法時需要一個字元串參數。所有傳遞給方法或函數的參數都被保存在內存的堆棧中。在中間語言中有一個指令ldstr可以從堆棧中載入一個字元串。(堆棧是內存中的一塊區域,它被用於將參數傳輸給方法,在後面我們會詳細討論堆棧的問題)。所有的方法都從堆棧中獲取它們的參數,因此ldstr指令是必不可少的。ldstr指令的格式如下所示:

ldstr <parameter string>

我們可以用ILAsm.exe來編譯這個程序。在運行ILAsm.exe之前,首先需要確認一下該程序已經包含在了Windows操作系統的Path環境變數中。ILAsm.exe 可在下面的路徑中找到:

%windir%\Microsoft.NET\Framework\v1.0.xxxx

其中xxxx是正在使用的.NET框架的內部版本號。例如我使用的版本號是3705,則應該如下設置Path環境變數:

Set Path = %Path%;c:\Windows\Microsoft.NET\Framework\v1.0.3705

然後運行cmd.exe(開始->運行->輸入cmd->按下確認鍵)。在彈出的命令窗口中輸入:
J:\Testcode>ilasm HelloWorld.il

彙編代碼后運行程序就可以看到Hello World.的輸出。

通過上面的例子,我們了解了中間語言的程序結構,一些命令和指令。同時需要提醒大家的是中間語言是區分大小寫的。

2.改進的HelloWorld例子
在.Net中的所有語言都是面向對象的語言,但是上面的HelloWorld例子是一個結構化的例子。下面讓我們來看一下如何將它轉化為面向對象的代碼。在面向對象的編程中,我們將操作定義在類中。為了將上面的HelloWorld例子轉化為面向對象的代碼,可以使用class命令:

.class HelloWorld { }

class命令后緊跟的是類的名稱。類的名稱在中間語言中是可選的。同時我們還需要為該指令添加一些屬性,例如存取控制類在內存中的布局和互用性等。這樣代碼就變成了:

.assembly HelloWorldIL {} .class public auto ansi HelloWorld extends [mscorlib]System.Object { .method public hidebysig static void HelloWorld() cil managed { .entrypoint ldstr "Hello World." call void [mscorlib]System.Console::WriteLine(class System.String) ret } .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ldarg.0 call instance void [mscorlib]System.Object::.ctor() ret } }

在代碼中用到了三個屬性:

· public:public是訪問控制屬性,它表明了對於訪問該類的成員沒有限制。

· auto:auto屬性表明了當類被載入到內存中時,在內存中的布局是由公共運行庫而不是程序決定的。

· ansi:指定ansi屬性是為了在沒有被管理和被管理的代碼之間實現無縫的轉化。在.Net中,那些不可直接應用在公共語言基礎設施之上的代碼被稱為沒有被管理的代碼,例如C、C++和VB6的代碼。我們需要一個屬性來處理被管理的代碼和沒有被管理的代碼之間的互用性。在被管理的代碼中,字元串用雙位元組的Unicode字元表示,而在被管理的代碼中,字元串有可能用單位元組的ANSI字元表示。指定了ansi屬性就可以在不同的代碼間轉化字元串了。

我們知道在.Net框架中,所有的類都直接或間接地繼承了System.Object類。在代碼中我們明確指定了HelloWorld繼承了System.Object。

在HelloWorld方法中加入了public、hidebysig、cil managed屬性,下面是對這些屬性的解釋:

· public:在C#或VB.Net中,當我們定義一個方法時,需要指定方法的訪問修飾符。訪問修飾符可以是public、protected、internal或private 。

· hidebysig:一個類可以繼承其他的類,hidebysig屬性保證當前類中的方法在作為父類時不會被子類繼承。例如如果HelloWorldChild類繼承了HelloWorld類,在HelloWorldChild中不會看到HelloWorld方法。

· cil managed:該屬性將在後面討論。

在高級語言中(C#,VB.Net等),每個類必須有構造函數,而且構造函數的第一行需要調用基類的構造函數。如果類中沒有構造函數,基類的構造函數將被自動調用。通常這是由編譯器自動完成的,現在我們要在的代碼中加入構造函數,該構造函數通過.ctor命令調用基類的構造函數。

小結
本文我們從經典的Hello World例子開始,通過實例了解了微軟中間語言的基本語法規則以及中間語言與其他開發語言的關係。在下一篇文章中,我們將在此基礎上,運用實常式序講述.net應用程序的格式和結構等內容。
上一篇[光參]    下一篇 [日本雲杉]

相關評論

同義詞:暫無同義詞