斗地主你什么时候才会托管?(.NET中的托管于非托管)

文章部分引自《.NET4.0面向对象编程漫谈(基础篇)》第1章.NET面向对象编程基础(作者:金旭亮)

  无意间看到一位四五岁左右小朋友在玩斗地主,总开始到结束,她一直都在使用“提示”(托管)出牌,你猜的没错,到最后她赢了。

  那么你什么时候才会使用“托管”呢?“我想继续游戏,但是牌太烂了,索性托管吧或者我想玩,但是我会儿刚好有事需要处理”

  实际上,我们在选择了.NET Framework平台后,就该选择使用什么语言了,但是每种语言都有自己的优缺点,例如,在非托管C/C++可对系统(Windows)进行一些很低的控制,可以按照自己的想法管理内存,控制线程,当然这些事情你如果不想关心,完全可以丢给CLR去处理,它可以帮你处理的核心功能,比如内存管理,程序集加载,安全性,异常处理,线程同步,当然这些babyservice的待遇只能给托管的代码使用。

  

非托管应用程序的执行过程

首先看一下Windows操作系统执行一个普通程序(即非托管程序)的基本过程。

软件工程师写的程序,经过编译器转为机器指令后,一般以文件的方式保存在外部存储器中,当CPU执行程序时,要先把外部存储器中的程序指令代码读到内存。

内存被分成很多块(称为"内存单元"),每个内存单元都有一个唯一的地址,指令就存放在以某个特定的地址开始的内存区域(即"若干个内存单元的集合")中。保存要执行的第一条机器指令的那个内存单元就是程序的"入口点(Entry Point)"。

当程序执行时,CPU从入口点取出第一条指令,开始执行,然后再取第二条,依次类推……

把一个程序从外部存储器上装入内存执行是一个复杂的过程,这个功能由操作系统实现,开发具体应用程序的软件工程师通常不需要手动去写这部分代码。

由此可知,程序的运行必须依赖于操作系统(如Windows),而且编译器生成的程序文件包含的是仅适用于特定CPU架构的机器指令,由于不同CPU架构的机器指令集不同,所以,这个可执行程序无法不加修改地在拥有不同CPU架构的计算机上运行。

以这种方式生成的机器指令代码称为"非托管代码(Unmanaged Code)"。非托管代码不仅不能在不同CPU架构的计算机上执行,而且通常在不同的操作系统下也不能执行,比如一个Windows应用程序就无法直接在 Linux下运行,反之亦然,这说明非托管代码的可移植性是受到较大限制的。

如果需要在拥有不同CPU架构的计算机和多种多样的操作系统上实现同一功能,必须针对每种操作系统和CPU架构编写特定的代码,这明显是一种重复且低效的劳动。

程序能不能只写一次,到处运行?

完全可以的,这就是 "跨平台"的设计思想(Java就是一个典范)。.NET也采用了这种设计思想,而且走得更远,.NET在架构设计上不仅允许.NET应用程序在各种操作 系统和CPU架构上运行,而且允许在同一个程序中混合使用由不同的编程语言开发出来的软件组件,.NET的这一特性被称为"跨语言"。

要支持跨平台这一特性,软件工程师编写的程序经过编译器生成的结果就不能是依赖于操作系统和特定CPU架构的机器指令了,而必须是一种"中立"的、 在各种操作系统和CPU架构上都能执行的代码,这种代码Java称为"Byte Code(字节码)",.NET称之为"IL(中间语言)"。

但程序最终还是要靠CPU执行的,所以,Java的字节码和.NET的IL代码仍然需要最终被翻译成本地CPU能执行的机器指令,这部分功能由一个运行在特定操作系统之上的软件系统来完成,这个软件系统被称之为"虚拟机(Virtual Machine,VM)"。

 
图1-9  托管代码运行原理

只需为每种操作系统和CPU架构提供一个虚拟机,就可以让同样一个应用程序不加修改地"跑"在运行不同操作系统、拥有不同CPU架构的计算机上。

这种运行在虚拟机之上的代码,被称为"托管代码(Managed Code)",其原理如图1-9所示。

使用C#编译器csc.exe编译生成的可执行程序实际包含的只是IL指令代码,这是一种托管代码,只能运行在.NET虚拟机之上。所以,如果某台 计算机上没有安装.NET Framework,就意味着图 1 9的"虚拟机"一层不存在,.NET应用程序就无法执行。对于非Windows的操作系统,只要上面有.NET虚拟机,就可以运行.NET程序,通常不需 要修改.NET应用程序源代码再重新编译。

一个应用程序可以只采用托管代码来构建,完全依赖CLR以及.NET Framework类库,也可以混合使用托管代码和非托管代码进行构建。托管代码调用非托管代码的技术,在.NET中被称为"平台调用(Platform Invoke)"(见图1-10)。

 
(点击查看大图)图1-10  托管代码与非托管代码

托管代码执行的过程

.NET下可直接运行的.exe文件包含的是IL指令。IL是微软和第三方编译器供应商磋商而创建的"虚"机器语言,之所以说它是"虚"的,是说它 独立于特定架构的CPU,并且引入了许多具有面向对象特征的指令,与传统的直接面向硬件的汇编指令有着很大的不同,可以看成是"面向对象的"汇编指令。

由于IL指令独立于特定架构的CPU,因此它必须经过一个"翻译"过程,转换成本地CPU支持的机器指令,才可以最终执行。这个"翻译者"就是"JIT编译器(Just-In-Time Complier)",请看图1-11。

如图1-11所示,程序源代码经语言编译器生成程序集,其中包含IL指令代码。当程序运行时,"类装载器(Class Loader)"从外部存储器中将IL指令读入内存,再经过JIT编译器动态地编译为本地CPU指令代码执行。在进行即时编译的过程中,CLR同时检查这 些IL指令是否违反了一些安全规则,必要时CLR会停止编译并中断程序的执行。

 
(点击查看大图)图1-11  托管代码的执行过程

上述即时编译和代码验证的过程仅仅只是在第一次调用某个方法时发生。CLR会将编译好的本地代码缓存起来,第二次调用时就直接调用缓存中的本地代码,从而避免了再次编译所带来的性能损失。

当然这样的性能损失可以通过升级硬件设备来减少(避免)。

 

时间: 2024-10-05 18:54:29

斗地主你什么时候才会托管?(.NET中的托管于非托管)的相关文章

浅谈 .NET 中的对象引用、非托管指针和托管指针

目录 前言 一.对象引用 二.值传递和引用传递 三.初识托管指针和非托管指针 四.非托管指针 1.非托管指针不能指向对象引用 2.类成员指针 五.托管指针 前言 本文主要是以 C# 为例介绍 .NET 中的三种指针类型(本文不包含对于函数指针的介绍):对象引用.非托管指针 .托管指针. 学习是一个不断深化理解的过程,借此博客,把自己关于 .NET 中指针相关的理解和大家一起讨论一下,若有表述不清楚,理解不正确之处,还请大家批评指正. 开始话题之前,我们不妨先对一些概念作出定义. 变量:给存储单元

c# 如何调用非托管函数2-传递结构和类

在上一篇c# 如何调用非托管函数 中介绍了调用非托管函数的简单方法,其中传递的都是简单的值类型,如uint等.本篇将介绍如何传递结构或类到非托管函数. 以系统函数 GetSystemTime 为例,以下分别演示了如何传递结构和类. 一 传递结构 参考MSDN可以知道他的定义为: void WINAPI GetSystemTime(__out LPSYSTEMTIME lpSystemTime); 其中LPSYSTEMTIME就是一个结构体: C++-Code: typedef struct _S

利用C#Marshal类实现托管和非托管的相互转换

Marshal 类 命名空间:System.Runtime.InteropServices 提供了一个方法集,这些方法用于分配非托管内存.复制非托管内存块.将托管类型转换为非托管类型,此外还提供了在与非托管代码交互时使用的其他杂项方法. Marshal 类中定义的 static 方法对于处理非托管代码至关重要.此类中定义的大多数方法通常由需要在托管和非托管编程模型之间提供桥梁的开发人员使用.例如,StringToHGlobalAnsi 方法将 ANSI 字符从指定的字符串(在托管堆中)复制到非托

C#内存管理之托管堆与非托管堆( reprint )

在 .NET Framework 中,内存中的资源(即所有二进制信息的集合)分为“托管资源”和“非托管资源”.托管资源必须接受 .NET Framework 的 CLR (通用语言运行时)的管理(诸如内存类型安全性检查),而非托管资源则不必接受 .NET Framework 的 CLR 管理.(了解更多区别请参阅 .NET Framework 或 C# 的高级编程资料)托管资源在 .NET Framework 中又分别存放在两种地方:“堆栈”和“托管堆”(以下简称“堆”):规则是,所有的值类型(

在C#调用C++的DLL方法(一)生成非托管dll

C#与C/C++相比,前者的优势在于UI,后者的优势在于算法,C++下的指针虽然恶心,若使用得当还是相当方便的,最重要的问题是,市面上很多流行的开发工具库,几乎没有不支持C++的,但全面支持C#只能说是难得.在CPU发展到今天,若说C#的执行效率跟C++相比有很大的差距,并不是那么靠谱,若非万不得已我还是宁愿用C#来写代码,调试什么的也很方便. 不得已的情况下,要在C#下使用C++的函数或类,最好的方式就是使用动态链接库(dll),至于COM什么的我是至今没弄明白其原理,也许主要是因为使用起来太

说说非托管资源的回收

释放未托管的资源有两种方法 1.析构函数 2.实现System.IDisposable接口 一.析构函数 构造函数可以指定必须在创建类的实例时进行的某些操作,在垃圾收集器删除对象时,也可以调用析构函数.析构函数初看起来似乎是放置释放未托管资源.执行一般清理操作的代码的最佳地方.但是,事情并不是如此简单.由于垃圾回收器的运行规则决定了,不能在析构函数中放置需要在某一时刻运行的代码,如果对象占用了宝贵而重要的资源,应尽可能快地释放这些资源,此时就不能等待垃圾收集器来释放了. 实例 C# 代码   复

.net非托管资源的回收

释放未托管的资源有两种方法 1.析构函数 2.实现System.IDisposable接口 一.析构函数 构造函数可以指定必须在创建类的实例时进行的某些操作,在垃圾收集器删除对象时,也可以调用析构函数.析构函数初看起来似乎是放置释放未托管资源.执行一般清理操作的代码的最佳地方.但是,事情并不是如此简单.由于垃圾回收器的运行规则决定了,不能在析构函数中放置需要在某一时刻运行的代码,如果对象占用了宝贵而重要的资源,应尽可能快地释放这些资源,此时就不能等待垃圾收集器来释放了. 实例 C# 代码   复

C#中托管与非托管

在.net 编程环境中,系统的资源分为托管资源和非托管资源. 对于托管的资源的回收工作,是不需要人工干预回收的,而且你也无法干预他们的回收,所能够做的 只是了解.net CLR如何做这些操作.也就是说对于您的应用程序创建的大多数对象,可以依靠 .NET Framework 的垃圾回收器隐式地执行所有必要的内存管理任务. 对于非托管资源,您在应用程序中使用完这些非托管资源之后,必须显示的释放他们,例如 System.IO.StreamReader的一个文件对象,必须显示的调用对象的Close()方

[.net 面向对象程序设计进阶] (8) 托管与非托管

本节导读:虽然在.NET编程过程中,绝大多数内存垃圾回收由CLR(公共语言运行时)自动回收,但也有很多需要我们编码回收.掌握托管与非托管的基本知识,可以有效避免某些情况下导致的程序异常. 1.什么是托管与非托管? 托管资源:一般是指被CLR(公共语言运行时)控制的内存资源,这些资源由CLR来管理.可以认为是.net 类库中的资源. 非托管资源:不受CLR控制和管理的资源. 对于托管资源,GC负责垃圾回收.对于非托管资源,GC可以跟踪非托管资源的生存期,但是不知道如何释放它,这时候就要人工进行释放

C# 托管和非托管混合编程

在非托管模块中实现你比较重要的算法,然后通过 CLR 的平台互操作,来使托管代码调用它,这样程序仍然能够正常工作,但对非托管的本地代码进行反编译,就很困难. 最直接的实现托管与非托管编程的方法就是使用C++/CLI 介绍 项目存档一直是企业的采用的做法,而是事实证明他们也是对的!对于一个程序员,这是几千men-days的工作量.为什么不开发一小段代码去重新利用那段代码,项目. 现在提供了一个渐渐的转向C#的新技术: 使用托管与非托管的混合编程.这是一个可行的方案在top-down issue(f