Windows Mobile 進階系列.第一回.真的瞭解.NET CF嗎?
阿新 • • 發佈:2019-02-08
第一回. 真的瞭解.NET Compact Framework嗎?
作為系列文章的開篇,有必要先詳細瞭解一下基於CE.NET的.NET Compact Framework(以後簡稱.NET CF),本文敘述了.NET CF的設計目標,架構特徵和執行環境。
.NET CF的目標在哪裡?
1.專為裝置設計的行動式小型.NET CLR
具有.NET Framework的子集屬性,支援多種語言開發。我們知道,在英文裡面“行動式”對應的單詞是Portable,這個Portable我們可以從兩方面理解:一方面,.NET CF工作在一個靈活的,移動的,資源有限的環境下;另一層意思則體現在.NET CF本身的特性上,比如說它與OS的寬鬆耦合,OS管與CLR託管的不同就在這裡。從程式設計的角度Portable體現在很多“和諧”的方面,比如I/O記憶體對映,比如僅支援Unicode編碼等等。
2.與Visual Studio系列IDE高度相容
不僅僅是編譯,除錯託管和非託管的程式碼,在Visual Studio 2008中你還可以通過Device Security Manager來為已連線的裝置管理證書和設定安全級別。甚至可以程式設計訪問模擬器資源。
3.與主機的作業系統有良好的共存性
這個共存性是多方面的,包括應用程式的執行模型,記憶體管理,使用者輸入和UI介面。這些在後面的文章中您都會接觸到。
當然還有一些要求是.NET CF做不到的,暫時也不是它的目標,為了不使大家對.NET CF的要求太“苛刻”,我覺得必須把這些“非目標”也列舉出來:
1.Compact VS. Full
.NET CF不是對桌面版本.NET Framework的部分簡單平移,把.NET Framework完整移植到移動裝置上並不是.NET CF的目標,儘管表面上看起來有些內容和完整版的.NET Framework是一致的,但是其實現方式可能很不一樣。
2.實時性
Windows Mobile是一個32位的民用作業系統,你不能要求它和VxWorks一樣工作!
.NET CF也並沒有提供對強實時性的支援(問題是您真的需要那麼高的實時性嗎?)。
3.語言支援
.NET CF目前支援的開發語言並不像完整版本的那麼豐富,目前比較流行的是C/C++,C#和VB。但是.NET CF完全支援精簡版本的ECMA CLI Profile,這意味著你也可以為更多的語言編寫針對.NET CF的編譯器。
.NET CF的結構模型
.NET CF 的架構跟完整版的.NET Fx有相似之處,同時又具有自己特色,如圖表 1所示。
圖表 1 .NET CF Architecture
最下面的是硬體層,由於圖幅有限,我僅列出了主要的一些硬體,而這所有的硬體都是由Windows CE 作業系統所控制的,WinCE提供了記憶體管理規範和用於載入可執行檔案的Program Loader,Program Loader負責將可執行檔案Push到記憶體中並啟動它。當然,WinCE作為作業系統還有很多其他職能,比如執行緒管理,繪製窗體,相應來自GUI的事件,管理網路連線等等。
作為使用.NET CF的程式設計師,我們主要關注的是圖中虛線以上的部分,現在來看看.NET CF 的Common Language Runtime(CLR),圖中灰色背景的方框表示.NET CF CLR。
用Native Code(如C/C++)編寫的基於WinCE的程式,直接被編譯成CPU可以識別的指令,但是依賴WinCE去載入它們並提供所需的服務。而CLR是一個用來託管應用程式的平臺,託管的應用程式被編譯成Microsoft Intermediate Language(MSIL),IL在這裡提供了一個與CPU鬆耦合的機會,CLR根據不同的CPU體系結構將IL編譯成不同的CPU指令,這一點在行為上與桌面版的CLR是一致的。
有一點要弄清的是託管的EXEs或者DLLs是由IL構成的,並不能被CPU直接執行,而是需要被CLR編譯成適用於本地CPU的指令或者是原生代碼。可見,CLR的工作是執行託管程式碼,這個過程就是託管程式碼本地化並被執行的過程,簡單來講,它包括以下步驟:
1.將IL從檔案系統載入到記憶體中。
2.這些IL中的部分或全部將被轉化成原生代碼供CPU執行
3.如果這些IL引用了某些DLL的內容,則當DLL被找到並正確載入後,這些被用到的部分也會被轉化成原生代碼並被執行。
可見這個Just In Time Compiler在CLR的執行中扮演著十分重要的角色。在.NET Compact Framework中有兩種形式的JIT編譯器,iJIT和sJIT:
iJIT適用於所有的CPU,如ARM,MIPS,SHx和X86等。iJIT較簡單,編譯的速度也比較快,但是它編譯出來的原生代碼並不像sJIT那樣經過優化的。
sJIT編譯器是ARM指令體系下特有的,它充分利用了AMR處理器的優勢。雖然編譯速度不及iJIT,但是編譯後的程式碼在第二次執行的時候會迅速得多。
所以在編寫應用程式的時候你應當考慮到你的程式是否是專為基於ARM的裝置而設計,或是有考慮到使用者的機器可能是一臺效能一般的MIPS。
預設狀況下,僅當無法使用sJIT Compiler的時候,CLR才會選擇使用iJIT的方式。這樣做的原因是,通常在程式執行過程中,花在JIT編譯上的時間和執行程式的時間相比是微不足道的。
需要注意的是在我們的程式當中應當避免額外的JIT行為,因為有些情況下,這會明顯影響到應用程式的執行速度,當然,要做到這一點需要您對CLR有一定的認識。JIT按需編譯,並嘗試對編譯後的程式碼在程式生命週期內進行保留,這樣下一次呼叫的時候就不必再執行JIT了。說是嘗試是不考慮記憶體的緣故,這樣的快取不會無限制的進行下去,當記憶體不夠用時,CLR會逐方法的將JIT過的程式碼清除掉,這就是所謂的 Code Pitching。清除掉之後再次呼叫該方法CLR就會重新進行JIT編譯。有趣的是這個Pitch的過程也是智慧化的,哪些最不常用的方法的JITed Code會被最先清除。
如果我們的程式編寫不當,在某些極端情況下,重複的JIT可能會出現在一個迴圈中,每一次迴圈都將重新JIT一次,這顯然會使效能大打折扣,而且很可能使你的程式因此down掉。
效能問題在今後的文章中還會專門介紹。
用一句話概括圖表1,可以說“是.NET CF CLR使得.NET應用程式得以執行在各種不同的基於Windows CE.NET的移動裝置上”。
Compact CLR VS. Full CLR
對於習慣了桌面應用的程式設計師,有必要了解一下精簡版的.NET CF與完成版本的.NET CF有哪些不同。
首先從體系結構上,.NET CF CLR與桌面版本的CLR不盡相同。從圖表一我們看到,.NET CF CLR 構建在Platform Abstraction Layer(PAL)之上,PAL位於執行引擎與OS之間,將CLR從硬體的層面抽象出來,如果您需要將CLR移植到其他平臺,只需要改變PAL層,併為該平臺的CPU編寫相應的JIT Compiler。正是這種靈活性,使得.NET CF CLR也隨著日新月異的Mobile硬體裝置不斷前行。
在JIT編譯之後,.Net CF CLR對生成的原生代碼的處理也與桌面版本不同。在桌面版本的CLR中,JIT過後的程式碼有時候會在程式退出之後依然存在,這樣在它下次載入的時候會快一些。但是.Net CF CLR僅僅在程式執行期間儲存JIT生成的程式碼。每一次程式啟動,JIT編譯必然再次發生。
在對程式集的定義上面,.Net CF CLR和桌面版本的CLR也有所不同,桌面版本的程式集支援多檔案構成一個程式集。而.NET CF CLR不支援這一性質,這在移植桌面應用程式到.NET CF下的時候是需要注意的。
這些不同都是與移動裝置本身的特性密不可分的,如果您要了解更多.NET CF與.NET Framework的不同,請參照這裡
.Net CF應用程式的執行環境
我們通常在除錯程式的時候主要有兩種執行環境---模擬器和真實裝置。要注意,這裡模擬器並不是說就是在Windows(x86)下面工作的用來模仿基於WinCE作業系統行為的一個程式,而是一個真正的CE.NET或者Windows Mobile作業系統的映象,只是它是由x86的作業系統所編譯並執行。而在真實裝置上執行的程式則是.NET CF CLR所掌管的一個例項。
前面講JIT的時候已經提到過應用程式的執行了。我們說“是.NET CF CLR使得.NET應用程式得以執行在各種不同的基於Windows CE.NET的移動裝置上”。CLR無疑是.NET最重要的組成部分,它負責將已編譯成MSIL的託管程式集裝配到應用程式域中,以JIT的編譯方式將他們編譯成原生代碼供宿主CPU執行, 同時它還要在執行過程中管理記憶體分配,垃圾回收以及載入其他類庫等。
從組成上可以把.NET CF CLR分成兩部分:執行引擎和基礎類庫。
執行引擎與底層作業系統提供的各種服務介面打交道(這離不開PAL的作用),而基礎類庫則是構成.NET應用程式的基本程式單元。
其中基礎類庫發展到現在已經十分豐富,想必大家也比較熟悉,在此無需也無法作多的介紹。下面看看執行引擎(Execution Engine)。
執行引擎為託管程式碼的執行提供了眾多基本服務,比如:
? 程式集的Loader和全域性程式集快取(GAC)
元資料引擎/快取
對類層次模型的描述
“反射”技術
(關於程式集的載入,後續的文章中也會介紹)
? JIT的編譯和校對機制
? 安全的執行體系
異常探知,
原生代碼互操作,
OS安全性保障
? 垃圾回收器
? 對除錯的支援
為Debug版本的程式生成可方便除錯(如斷點)的程式碼
? 對某些託管的API(Class Libs)採用本地化的實現
執行引擎(EE)是.NET的核心元件,它掌管著.NET CF的所有其他東西。其本身是一個本地可執行的檔案,它通過平臺抽象層(PAL)與底層作業系統進行互動,他們共同被安置在一個可執行檔案中(Mscoree.dll),你可以在Windows/System32目錄下面找到它。 之前一再提到Portable這個詞,裝置是Portable的,.NET CF也是Portable的,移植到新的平臺只需重新編寫JIT Compiler和PAL層,執行引擎是無需改變的,不同的PAL導致了MSCoree.dll不同的實現方式。執行引擎用標準C語言編寫,這也是Portable的體現。
PAL層(the Platform Adaptation Layer)就如同基於WinNT的作業系統(Windows NT 4.0, 2000, XP, 2003)的硬體適配層(HAL),用於在常規程式碼和不同硬體水平的CPU之間做一個適配。正是PAL的存在,使得.NET CF的程式集能執行在使用任何CPU的任何WinCE相容的裝置上,而以往用經典的EVC開發的應用程式可能還需要為不同的OS和CPU單獨編譯。,
另外,.NET CF同樣支援GAC,你可以以可複用的方式部署你的應用程式,它是通過預載入某些基礎類庫,你可以在你的應用中通過應用的方式來呼叫他們,這樣減少了程式碼量,也提高了效能。
在異常處理方面,.NET CF還有一個有趣的特點,我們知道,通常一個error發生的時候,一條錯誤訊息會隨之而來,開發者可以捕獲到這個訊息,並選擇怎麼處理。在.NET CF中,基於對記憶體問題的考慮,微軟摘錄了所有這些錯誤資訊,併為各種支援的語言把它們分別單獨放在一個字串檔案中 (SYSTEM.SR.dll),部署的時候,你可以選擇是否將這些錯誤資訊檔案隨你的應用程式一同部署到裝置上。當然選擇不部署這個檔案在load的時候會節省一些效能,在這篇文章也有提到這個問題。
總結
.NET Compact Framework使得熟悉.NET Framework的程式設計師得以用他們熟知的C#或者VB來開發移動裝置上的程式。
.NET CF 和完整版的.Net Framework不單純是子集關係。開發的時候除了要注意,使用者可能使用不同處理器,還應考慮到PPC與PC的區別,Smartphone與PPC的區別。
.NET CF CLR(the CLR Designed for .NET Compact Framework)是專為裝置設計的程式碼託管執行機構,在垃圾回收機制,異常處理和安全性等方面繼承了.NET託管應用程式的優勢。同時它又有自身的特性,比如PAL。兩種JIT的方式中,sJIT是僅適用於ARM處理器的。
執行環境方面,MSCoree.dll是應用程式執行的基礎,它由執行引擎(EE)和平臺抽象層(PAL)構成。
對於.NET CF 3.5中的新特性,可以參考這裡