欢迎来到.net学习网

欢迎联系站长一起更新本网站!QQ:879621940

您当前所在位置:首页 »  .NET本质论第一卷:公共语言运行库教程 » 正文

本教程章节列表

.net本质论第10章:基于CLR的程序与其周围环境是如何联系的

创建时间:2013年05月16日 21:49  阅读次数:(3102)
分享到:
前面几章主要讨论了适用于CLR程序的核心编程模型。重点放在CLR的虚拟世界这个层面上,尽可能地避免讨论操作系统或者内存管理的问题。在最后一章里,我们将实际考察与认识:基于CLR的程序与其周围环境是如何联系的。

内存
诸如CLR的现代运行时环境提升了抽象的级别,从手工管理内存,发展为基于类型、对象和值的类型中心(type-centric)模型。在新的模型下,内存的使用是隐式的,而不是显式的。对于大部分程序而言,提高其生产率远比失去对内存的低级控制要重要。然而,有一类问题对于显式内存控制则比较重要,通常这类问题就是内存缓冲区的直接访问,这和直接访问内存是高性能I/O处理、操作内存映射设备以及与现有C函数库或者系统调用交互的关键所在。

大多数运行时环境(包括CLR)都提供与C函数库集成的途径,即通过形实转换层{thunking javer,例如,J/Direct、P/Invoke、Java本机接口[Java Native interfact(JNI)]}。然而,这些形实转换层都会有一定的开销。所有从“运行时模式( runtime mode)”到“C模式(C mode)”
的转化都会带来很大的性能开销。这就导致了违反常规的设计,以尽量减少在两个世界间的转换,从而保持一定的性能。此外,对于JNI(至少一种转换层),则必用C或者C++编写个一适配器库,用于映射Java VM与目标厍。

CLR的独特之处在于,对于那些需要直接操作内存的程,CLR的类型系统和指令集允许使用传统的c风格内存操作。在CJL中,完全支持内存的显式使用,并且不需要依赖于机器的本机码。内存的显式使用与CLR运行时语义完全一致,并且,不需要“模式开关”禁用CLR提供的服务。对于内存的显式使用,要求理解CLR是如何区分对象引用和指针的。

CLR将对象引用和指针区别对待,当然,它们最终都是内存地址。对象引用支持一组不同于指针的操作。尤其是对象引用支持对齐、同一(identity)比较,以及成员防问。但是,对象引用不存在“解除引用”的概念,也没有“对象引用运算”的概念。最后.对象引用被假定为指向垃圾回收堆上的某个特定对象,这就意味着当垃圾收集器压缩[托管(managed)
堆时,包含在对象引用里的地址值将会被调整。

CLR支持指针,是将其作为不同于对象引用的构件。不同于对象引用,指针意味着能被解除引用:不同于对象引用,指针是有序的,能够用<和 >操作符进行比较;此外,不同于对象引用,指针支持算术操作符,允许随机访问内存。最后一个差别需要更进一步的推敲。

因为C风格的指针允许程序访问任意内存,所以,C风格的指针使程序的验征难以处理。程序验证是CLR的关键特征,用于确保CLR或者宿主环境的安全性不受到损害。不使用C风格指针的程序能够被验证,是因为能够对所有对象和值的访问都进行类型安全的验证。然而,如果存在随机访问内存的情形,就有可能欺骗系统相信:任何内存都是某一个完全信任组件的实例。由此,CLR支持两种指针;一种是不放弃验证的,一种是放弃验证的。

基于CLR的程序按部就班地使用验证的指针。这种指针被称为托管(managed)指针,c#和VB NET编译器在方法参数声明为传引用时,就使用托管指针。不同于c风格指针,托管指针不支持算术运算。此外,托管指针的初始化和赋值也受到约束,以确保不损害CLR类型系统。为此,托管指针是强类型的,其本身就是类型的实例。例如,指向System.Int32的托管指针是System.Int32$类型的实例。托管指针之所以称为“托管”,其最终原因是,在堆压缩( heap compaction)过程中当被引用的对象被移动时,垃圾回收器能够调整指针的值。

托管指针主要用于方法参数实现传引用(pass-by-reference)。CLR也支持第二种风格的指针,即类似C风格的指针。这种风格的指针被称为“非托管(umnanaged)”指针。这里使用形容词“非托管”,是因为垃圾回收器在堆压缩时将忽略非托管指针。同托管指针一样,非托管指针也是某个类型的实例。例如.指向System.Int32非托管指针是System.Int32$类型的实例。

不同于托管指针,非托管指针支持指针的算术运算和非检查的类型转换。对非托管指针而言,下面的代码完全合法:
int x=0x12345678 ;
void *pv=&x ;
double *pd=(double*)(pv);
*(pd+5)=2.0

C#编译器很乐意将这个颇具危险的代码转换为CIL。此外,CLR也很乐意将CIL转换为本机代码,然后再执行它。然而,如果开发人员、系统管理员或者用户不采取显式的行为,上述情形哪个也不会发生。

非托管指针的使用将导致代码的类型安全得不到验证,执行“不需要进行类型安全验证的代码”的能力意味着高度信任的安全权限。默认情形下,CLR不会给本地文件系统以外的代码赋予这种权限。特别是对于那些包含非验证代码的程序集,必须在其程序集清单上请求SecurityPermissionFlagskipVerification极限(并且必须获得CLR的允秆)。在CLR的默认安装中,这个权限没有授予由远程文件系统加载的文件,因此,执行非验证代码的惟一途径,就是欺骗用户将它拷贝到本地文件系统的执行区。当然,管理员或者最终用户可能会显式地授权给信任的运行代码,但是,这也需对部署的机器有信任访问权的用户谨慎行事。

由于c++程序中C风格指针的角色,C++编译器只发射非验证的代码。相反,VB.NET只发射验证的代码。c#对验证和非验证代码都支持。默认情况下.C#编译器发射验证的代码.这样,就能够比较容易地从远程文件系统或者Web服务器上部署C#代码。为了防止程序员随意生成非验证代码,c#编译器要求他们明确地表示,他们想要在程序中使用非托管指针的意图。这个声明采用编译器开关和语言关键字的形式。

为了编译使用非托管指针的c#程序,必须使用/unsafe或者/unsafe+命令行参数。这个开关导致编译器发射请求SipVerification权限的权限集。这个开关也允许在程序的源代码中使用非托管指针。

c#不鼓励使用非托管指针,为此,它要求非托管指针的使用只出现在一个被声明为unsafe的作用域(scope)内(例如,方法作用域、类型作用域)。例如,由于缺少unsafe关键字,下面的代码将不会被编译:
public class Bob{
public void Hello(){
int x=3;
int *px=&x; //非托管指针的使用
}
}

下面的代码能够编译:
public class Bob{
public unsafe void Hello(){
int x=3;
int *px=&x; //非托管指针的使用
}
}

下面的代码也能够编译:
public unsafe class Bob{
public void Hello(){
int x=3;
int *px=&x; //非托管指针的使用
}
}

当然,最后两个程序只有在/unsafe或者/unsafe+命令行开关使用时,才将会被编译。
来源:.net学习网
说明:所有来源为 .net学习网的文章均为原创,如有转载,请在转载处标注本页地址,谢谢!
【编辑:Wyf】

打赏

取消

感谢您的支持,我会做的更好!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

最新评论

共有评论0条
  • 暂无任何评论,请留下您对本文章的看法,共同参入讨论!
发表评论:
留言人:
内  容:
请输入问题 95+84=? 的结果(结果是:179)
结  果: