分享

Windows Mobile 进阶系列.第一回.真的了解.NET CF吗- 黄季冬

 zhongfeiying 2010-12-22

Windows Mobile 进阶系列 .第一回 .真的了解 .NET CF吗 ?

作者:黄季冬

发表时间:2008-02-25

 

第一回 真的了解 .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目录下面找到它。 

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的时候会节省一些性能,在这篇文章也有提到这个问题。

之前一再提到 Portable这个词,设备是 Portable的, .NET CF也是 Portable的,移植到新的平台只需重新编写 JIT Compiler和 PAL层,执行引擎是无需改变的,不同的 PAL导致了 MSCoree.dll不同的实现方式。执行引擎用标准 C语言编写,这也是 Portable的体现。

总结

.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中的新特性,可以参考这里

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多