官术网_书友最值得收藏!

1.2 初探.NET程序運(yùn)行原理

1.2.1 手工開發(fā).NET程序請讀者事先安裝好Visual Studio 2010。

幾乎所有的程序設(shè)計(jì)書,第一個(gè)程序都是一個(gè)“Hello,World!”程序,以之向世界莊嚴(yán)宣告讀者要開始學(xué)習(xí)程序設(shè)計(jì),即將進(jìn)入到無比神奇的軟件世界中。

本書也未能免俗,第一個(gè)程序就是C#版的“Hello,World!”。

請打開Windows附件中的記事本,手動(dòng)輸入以下程序:

class MyFirstProgram
{
    static void Main(string[] args)
    {
        System.Console.WriteLine("Hello,world!");
    }
}

給文件取名“MyFirstProgram.cs”,并以純文本格式將其保存到硬盤上。

從Windows“開始”菜單中按以下順序選取菜單命令:

開始→所有程序→Micrsoft Visual Studio 2010→Visual Studio Tools→Visual studio命令提示(2010)

提示

其實(shí)并不需要安裝Visual Studio,只需安裝有“.NET Framework SDK(Software Development Kit,軟件開發(fā)包)”就可以手工編譯一個(gè).NET程序,但須設(shè)置好相應(yīng)的環(huán)境變量。為簡單起見,本書直接使用“Visual Studio命令提示(2010)”所啟動(dòng)的控制臺窗口,以省去建立各種環(huán)境變量的麻煩。

這將打開一個(gè)黑底白字的命令提示窗口。

使用DOS命令“cd”轉(zhuǎn)入到保存“MyFirstProgram.cs”的文件夾下,輸入以下命令:

csc/target:exe MyFirstProgram.cs

csc.exe是C#語言編譯器,如果MyFirstProgram.cs中沒有語法錯(cuò)誤,將會產(chǎn)生一個(gè)可執(zhí)行的MyFirstProgram.exe文件,此程序運(yùn)行后將在控制臺窗口輸出字符串“Hello,World!”(見圖1-5)。

圖1-5 手工編譯并運(yùn)行C#程序

.NET下運(yùn)行的程序它的可執(zhí)行程序文件被稱為“程序集(assembly)”。與傳統(tǒng)的Windows應(yīng)用程序指直接運(yùn)行于Win32平臺上的應(yīng)用程序。不同,后者在可執(zhí)行程序文件中包含的是可以在本地CPU上直接執(zhí)行的機(jī)器指令,而.NET程序集中包含的是獨(dú)立于特定CPU的IL指令。

.NET 4.0 SDK提供了一個(gè)查看程序集IL指令的反匯編工具ildasm.exe(默認(rèn)情況下安裝于“\Program Files\Microsoft SDKs\Windows\v7.0A\bin\”文件夾下),可以使用它來查看生成的MyFirstProgram.exe程序集中所包容的IL指令代碼(見圖1-6)。

目前讀者可以不去理會這些指令代碼的含義,只需知道有這么一個(gè)工具就行了。關(guān)鍵是要明白:.NET應(yīng)用程序編譯之后會生成“程序集”,其中包含了IL指令。

圖1-6 使用ildasm查看.NET程序集中的IL指令代碼

提示

如果想深入地掌握.NET Framework的技術(shù)內(nèi)幕,經(jīng)常使用ildasm工具是一個(gè)好方法。美國著名的.NET技術(shù)專家Jeffrey RichterJeffrey Richter有多本著作,他的“CLR via C#”是.NET技術(shù)領(lǐng)域的名著。就有這么一句名言:“如果你對IL反匯編工具顯示的內(nèi)容了解得越多,那么你對.NET框架的理解也會越深”。

除了可以使用ildasm工具“反匯編”.NET程序集而得到應(yīng)用程序的IL代碼之外,我們還可以直接使用另一個(gè)名為Reflector在前言中給出了Reflector的下載網(wǎng)址。的工具,直接看到轉(zhuǎn)換為C#代碼的程序集反匯編結(jié)果。

請運(yùn)行Reflector工具,從File菜單中選擇“Open”命令,打開編譯好的示例程序集“MyFirstProgram.exe”(見圖1-7)。

如圖1-7所示,選中“MyFirstProgram”節(jié)點(diǎn),右擊,從彈出菜單中選擇“Disassemble”命令,就可以看到反匯編得到的C#代碼(見圖1-8)。

圖1-7 使用Reflector反匯編示例程序

圖1-8 Reflector反匯編程序集得到的C#代碼

在探索.NET技術(shù)內(nèi)幕的過程中,Reflector是一個(gè)強(qiáng)有力的工具。

擴(kuò)充閱讀

如何保護(hù)自己的代碼不被惡意利用?

Reflector是一個(gè)免費(fèi)的共享軟件,任何一個(gè)人都可以輕易地使用它反匯編一個(gè).NET程序集,而它的功能是如此強(qiáng)大,反匯編出來的代碼非常接近于真實(shí)的源代碼。一個(gè)問題出現(xiàn)了:如何保護(hù)開發(fā)者的勞動(dòng)成果不被不懷好意的人利用?

解決方案之一是使用“Dotfuscator Software Services”工具來保護(hù)程序集。此工具可以加密程序集,從而保護(hù)程序集不會被Reflector等工具輕易地反匯編出可以閱讀的源代碼。

Visual Studio 2010附帶了有“Dotfuscator Software Services”,其用法請讀者自行閱讀其隨機(jī)手冊,或者通過互聯(lián)網(wǎng)搜索引擎獲取相關(guān)的資料,本書不再贅述。

下面對比一下ildasm和Reflector這兩個(gè)工具:

ildasm可以將程序集反匯編為IL代碼,偏重“底層”一些,多用于輔助分析編譯器和CLR的行為特性,探索CLR的內(nèi)部機(jī)理。它可以直接查看程序集所包容的元數(shù)據(jù)。開發(fā)者可以在ildasm反匯編出來的IL指令代碼基礎(chǔ)上進(jìn)行修改,然后使用.NET Framework SDK所提供的另一個(gè)配套工具——IL編譯器(ilasm.exe)來將修改后的IL代碼編譯為新的程序集。

Reflector可以將程序集反匯編為多種語言形式(比如C#、IL、Visual Basic等,甚至還有Delphi),比較“高層”一些,人們多將其用于分析.NET Framework類庫,以了解特定技術(shù)領(lǐng)域的內(nèi)部技術(shù)細(xì)節(jié)(比如通過反匯編Page類來理解ASP.NET頁面的生命周期),Reflector對于.NET程序員深入把握特定技術(shù)領(lǐng)域的內(nèi)幕非常有幫助。

ildasm和Reflector堪稱絕配,共同成為探索.NET技術(shù)內(nèi)幕的兩把鋒利的手術(shù)刀,本書在許多地方同時(shí)使用這兩個(gè)工具分析.NET技術(shù)內(nèi)幕。

技術(shù)春秋

Lutz Roeder和他的Reflector

Reflector的開發(fā)者是Lutz Roeder。

1999年微軟宣布啟動(dòng).NET戰(zhàn)略不久,Lutz Roeder就開始了免費(fèi)工具軟件Reflector的開發(fā),這個(gè)工具一經(jīng)推出,就好評如潮。

Lutz Roeder花了8年的時(shí)間持續(xù)完善Reflector,使之成為了最受歡迎的.NET輔助開發(fā)工具之一。

2004年,.NET技術(shù)顧問James Avery在MSDN Magazine上寫了一篇文章——《每個(gè)開發(fā)者都需要立即下載的10個(gè).NET開發(fā)工具》http://msdn.microsoft.com/en-us/magazine/cc300497.aspx.,Reflector名列其中;Scott Hanselman在其《2007開發(fā)者和高級用戶的終極工具列表http://www.hanselman.com/blog/ScottHanselmans2007UltimateDeveloperAndPowerUsersToolListForWindows.aspx.》一文中稱“Reflector改變了世界”。

其實(shí)無需別人的文章“吹捧”,只需看一看安裝了Reflector的.NET開發(fā)者有多少,再問一問任何一名使用過Reflector的.NET軟件工程師的看法,就明白Reflector得享大名實(shí)在是理所應(yīng)當(dāng)。

商業(yè)軟件公司Red Gate看中了Reflector的巨大影響力,在2008年與Lutz Roeder達(dá)成協(xié)議,今后將由Red Gate公司負(fù)責(zé)Reflector的后續(xù)開發(fā)工作,Red Gate公司承諾未來的Reflector仍然免費(fèi)向社區(qū)提供,但他們會基于Reflector免費(fèi)版本開發(fā)功能更為強(qiáng)大的商業(yè)版本,使用商業(yè)版本需要付費(fèi)。

Lutz Roeder于2002年加盟微軟公司,參與開發(fā)Microsoft Expression和Silverlight。

1.2.2 托管和非托管的軟件運(yùn)行環(huán)境

要想開發(fā).NET應(yīng)用程序,不可不知其開發(fā)與運(yùn)行背后的故事。

非托管應(yīng)用程序的執(zhí)行過程

首先看一下Windows操作系統(tǒng)執(zhí)行一個(gè)普通程序(即非托管程序)的基本過程。

軟件工程師寫的程序,經(jīng)過編譯器轉(zhuǎn)為機(jī)器指令后,一般以文件的方式保存在外部存儲器中,當(dāng)CPU執(zhí)行程序時(shí),要先把外部存儲器中的程序指令代碼讀到內(nèi)存。

內(nèi)存被分成很多塊(稱為“內(nèi)存單元”),每個(gè)內(nèi)存單元都有一個(gè)唯一的地址,指令就存放在以某個(gè)特定的地址開始的內(nèi)存區(qū)域(即“若干個(gè)內(nèi)存單元的集合”)中。保存要執(zhí)行的第一條機(jī)器指令的那個(gè)內(nèi)存單元就是程序的“入口點(diǎn)(Entry Point)”。

當(dāng)程序執(zhí)行時(shí),CPU從入口點(diǎn)取出第一條指令,開始執(zhí)行,然后再取第二條,依次類推……

把一個(gè)程序從外部存儲器上裝入內(nèi)存執(zhí)行是一個(gè)復(fù)雜的過程,這個(gè)功能由操作系統(tǒng)實(shí)現(xiàn),開發(fā)具體應(yīng)用程序的軟件工程師通常不需要手動(dòng)去寫這部分代碼。

由此可知,程序的運(yùn)行必須依賴于操作系統(tǒng)(如Windows),而且編譯器生成的程序文件包含的是僅適用于特定CPU架構(gòu)的機(jī)器指令,由于不同CPU架構(gòu)的機(jī)器指令集不同,所以,這個(gè)可執(zhí)行程序無法不加修改地在擁有不同CPU架構(gòu)的計(jì)算機(jī)上運(yùn)行。

以這種方式生成的機(jī)器指令代碼稱為“非托管代碼(Unmanaged Code)”。非托管代碼不僅不能在不同CPU架構(gòu)的計(jì)算機(jī)上執(zhí)行,而且通常在不同的操作系統(tǒng)下也不能執(zhí)行,比如一個(gè)Windows應(yīng)用程序就無法直接在Linux下運(yùn)行,反之亦然,這說明非托管代碼的可移植性是受到較大限制的。

如果需要在擁有不同CPU架構(gòu)的計(jì)算機(jī)和多種多樣的操作系統(tǒng)上實(shí)現(xiàn)同一功能,必須針對每種操作系統(tǒng)和CPU架構(gòu)編寫特定的代碼,這明顯是一種重復(fù)且低效的勞動(dòng)。

程序能不能只寫一次,到處運(yùn)行?

完全可以的,這就是“跨平臺”的設(shè)計(jì)思想(Java就是一個(gè)典范)。.NET也采用了這種設(shè)計(jì)思想,而且走得更遠(yuǎn),.NET在架構(gòu)設(shè)計(jì)上不僅允許.NET應(yīng)用程序在各種操作系統(tǒng)和CPU架構(gòu)上運(yùn)行,而且允許在同一個(gè)程序中混合使用由不同的編程語言開發(fā)出來的軟件組件,.NET的這一特性被稱為“跨語言”。

要支持跨平臺這一特性,軟件工程師編寫的程序經(jīng)過編譯器生成的結(jié)果就不能是依賴于操作系統(tǒng)和特定CPU架構(gòu)的機(jī)器指令了,而必須是一種“中立”的、在各種操作系統(tǒng)和CPU架構(gòu)上都能執(zhí)行的代碼,這種代碼Java稱為“Byte Code(字節(jié)碼)”,.NET稱之為“IL(中間語言)”。

但程序最終還是要靠CPU執(zhí)行的,所以,Java的字節(jié)碼和.NET的IL代碼仍然需要最終被翻譯成本地CPU能執(zhí)行的機(jī)器指令,這部分功能由一個(gè)運(yùn)行在特定操作系統(tǒng)之上的軟件系統(tǒng)來完成,這個(gè)軟件系統(tǒng)被稱之為“虛擬機(jī)(Virtual Machine,VM)”。

只需為每種操作系統(tǒng)和CPU架構(gòu)提供一個(gè)虛擬機(jī),就可以讓同樣一個(gè)應(yīng)用程序不加修改地“跑”在運(yùn)行不同操作系統(tǒng)、擁有不同CPU架構(gòu)的計(jì)算機(jī)上。

這種運(yùn)行在虛擬機(jī)之上的代碼,被稱為“托管代碼(Managed Code)”,其原理如圖1-9所示。

使用C#編譯器csc.exe編譯生成的可執(zhí)行程序?qū)嶋H包含的只是IL指令代碼,這是一種托管代碼,只能運(yùn)行在.NET虛擬機(jī)之上。所以,如果某臺計(jì)算機(jī)上沒有安裝.NET Framework,就意味著圖1-9的“虛擬機(jī)”一層不存在,.NET應(yīng)用程序就無法執(zhí)行。對于非Windows的操作系統(tǒng),只要上面有.NET虛擬機(jī),就可以運(yùn)行.NET程序,通常不需要修改.NET應(yīng)用程序源代碼再重新編譯。

圖1-9 托管代碼運(yùn)行原理

一個(gè)應(yīng)用程序可以只采用托管代碼來構(gòu)建,完全依賴CLR以及.NET Framework類庫,也可以混合使用托管代碼和非托管代碼進(jìn)行構(gòu)建。托管代碼調(diào)用非托管代碼的技術(shù),在.NET中被稱為“平臺調(diào)用(Platform Invoke)”(見圖1-10)。

圖1-10 托管代碼與非托管代碼

托管代碼執(zhí)行的過程

.NET下可直接運(yùn)行的.exe文件包含的是IL指令。IL是微軟和第三方編譯器供應(yīng)商磋商而創(chuàng)建的“虛”機(jī)器語言,之所以說它是“虛”的,是說它獨(dú)立于特定架構(gòu)的CPU,并且引入了許多具有面向?qū)ο筇卣鞯闹噶?,與傳統(tǒng)的直接面向硬件的匯編指令有著很大的不同,可以看成是“面向?qū)ο蟮摹眳R編指令。

由于IL指令獨(dú)立于特定架構(gòu)的CPU,因此它必須經(jīng)過一個(gè)“翻譯”過程,轉(zhuǎn)換成本地CPU支持的機(jī)器指令,才可以最終執(zhí)行。這個(gè)“翻譯者”就是“JIT編譯器(Just-In-Time Complier)”,請看圖1-11。

如圖1-11所示,程序源代碼經(jīng)語言編譯器生成程序集,其中包含IL指令代碼。當(dāng)程序運(yùn)行時(shí),“類裝載器(Class Loader)”從外部存儲器中將IL指令讀入內(nèi)存,再經(jīng)過JIT編譯器動(dòng)態(tài)地編譯為本地CPU指令代碼執(zhí)行。在進(jìn)行即時(shí)編譯的過程中,CLR同時(shí)檢查這些IL指令是否違反了一些安全規(guī)則,必要時(shí)CLR會停止編譯并中斷程序的執(zhí)行。

圖1-11 托管代碼的執(zhí)行過程

上述即時(shí)編譯和代碼驗(yàn)證的過程僅僅只是在第一次調(diào)用某個(gè)方法時(shí)發(fā)生。CLR會將編譯好的本地代碼緩存起來,第二次調(diào)用時(shí)就直接調(diào)用緩存中的本地代碼,從而避免了再次編譯所帶來的性能損失。

托管與非托管應(yīng)用程序的差別,是每一個(gè).NET軟件工程師都必須了解的。

主站蜘蛛池模板: 洮南市| 兰西县| 福鼎市| 古蔺县| 响水县| 东乡族自治县| 南华县| 行唐县| 汝州市| 黄龙县| 长宁区| 苍山县| 固安县| 建始县| 罗江县| 西充县| 威信县| 康马县| 深水埗区| 招远市| 中方县| 武宣县| 永吉县| 延庆县| 长武县| 保山市| 黄冈市| 遂川县| 信丰县| 花莲市| 海原县| 施秉县| 淮阳县| 鄂伦春自治旗| 连州市| 桂平市| 定西市| 肥西县| 呼和浩特市| 墨玉县| 东宁县|