首页 技术 正文
技术 2022年11月19日
0 收藏 871 点赞 3,310 浏览 5992 个字

.NET框架的核心便是通用语言运行时(CLR),顾名思义它是一个可被各种不同的编程语言所使用的运行时.CLR的很多特性可用于所有面向它的编程语言.比如,如果CLR用异常来报告错误,那么所有面向它的语言都将通过异常来得到错误报告.如果CLR允许我们创建线程,那么所有面向它的语言也都可以创建线程.在实际中,CLR在运行时对开发人员用何种编程语言来完成源代码一无所知.这意味着我们应该选择那些能够最容易表达我们意图的编程语言.我们可以用任何自己喜欢的语言来编写代码,前提是我们使用的编译器能够面向CLR的代码.

针对c#:   c#源代码文件(.cs)→c#编译器→托管模块(IL和元数据) .最终是将源代码编译为托管模块.托管模块是一个需要CLR才能执行的标准Windows可移植执行文件(PE).

PE的各个组成部分:

①PE表头:该表头指出了文件类型,比如GUI和DLL,另外还包括一个时间标记用于表示文件创建日期,对于仅包含IL代码的模块,该表头的大多数信息会被忽略.对于包含本地CPU代码的模块,该表头还会包含有关本地CPU代码的一些信息.

②CLR表头:包含标识托管模块的一些信息(可以被CLR或者一些实用工具解析).这些信息包括托管模块所需要的CLR版本号,一些标记,托管模块入口点方法(Main)方法的MethodDef元数据标记,以及有关模块的元数据、资源、强命名、标记和其他一些意义不是太大的信息的位置和尺寸.

③元数据 每个托管模块都包含一些元数据表.元数据表主要分两种,一种用于描述源代码中定义的类型和成员,一种用于描述源代码中引用的类型和成员

④中间语言(IL)代码 编译器在编译源代码时产生的指令.CLR在运行时会将IL代码编译成本地CPU指令.

由于生存期和执行行为受CLR管理的缘故,IL代码有时也被称为托管代码.实际上元数据总是和这些IL代码一起被嵌入到同一个EXE/DLL文件中,两者根本不能分离.因为编译器总是同时产生元数据和IL代码,并且总是同时将它们嵌入到生成的托管模块中,所以元数据和它描述的IL代码之间总能保持同步.

元数据的作用:

1.元数据省去了源代码编译时对头文件和库文件的需求,这是因为在含有实现类型和成员的IL代码文件中,已经包含了所有被引用的类型和成员的信息.编译器可以直接从托管模块中读取元数据来获得这些信息.

2.VS可以利用元数据来辅助我们编写代码.它的智能感知特性就是通过分析元数据来告诉我们某个类型提供了哪些方法,以及某个方法有哪些参数.

3.CLR的代码验证过程可以利用元数据来确保代码仅执行”安全”操作.

4.利用元数据,我们可以将一个对象的字段序列化到一个内存中,然后远程传送给另一台机器,最后再在远程机器上执行反序列化,从而重新创建对象和它的状态.

5.利用元数据,垃圾收集器可以追踪对象的生存期.对于任何对象,垃圾收集器都能够通过元数据来确定该对象的类型,并且可以获知该对象的哪些字段引用了其他对象.

程序集

    CLR其实并不和托管模块打交道,它直接打交道的对象是程序集.程序集是一个抽象的概念,刚开始往往很难理解.首先,程序集是一个或多个托管模块,以及一些资源文件的逻辑组合.其次,程序集是组件复用,以及实施安全策略和版本策略的最小单位.根据我们对编译器和相关工具所做的选择,程序集可以是一个文件或者多个文件.

当生成一个EXE程序集时,编译器/链接器会产生一些特殊的信息,并将它们嵌入到结果程序集的PE文件表头及其各个组成文件的.text部分.当EXE文件被调用时,这些特殊的信息将导致CLR被加载并初始化.CLR随后会定位到应用程序的入口点方法,从而以此来启动应用程序.类似地,如果一个非托管应用程序通过调用LoadLibrary来加载一个托管程序集,那么该托管程序集DLL的入口点函数也会知道去加载CLR来处理包含其中的代码. 加载原理如下:

.NET 框架基本原理透析⑴

方法的执行

在方法执行之前,CLR首先会检测该方法中代码引用到的所有类型.这会导致CLR分配一个内部的数据结构,该数据结构用于管理对所引用的类型的访问.例如控制台的Main方法,假设它只有一个Console.writeLine(“ddd”); 此时Main方法只引用了一个Console类型,CLR将会为此分配一个单独的内部结构.在这个内部的数据结构中,Console类型中定义的每一个方法都会有一个对应的条目.每个条目中将保存一个方法实现代码的地址.当该数据结构被初始化时,CLR将把每一个条目设置为CLR内部的一个没有正式记录的函数,我们暂且称该函数为JITCompiler. 当Main方法第一次调用WriteLine时,JITCompiler函数将被调用,该函数负责将一个方法的IL代码编译成本地CPU指令.因为IL代码是被”即时”编译的,所以CLR的这一部分通常称作JITter或者即时编译器.

当JITCompiler函数被调用时,它会知道正在调用的是哪个方法,以及该方法是由哪个类型定义的.JITCompiler函数随后会在被调用方法所定义的程序集中的元数据搜索其IL代码的位置.然后验证这些IL代码并将它们编译为本地CPU代码.这些本地CPU指令将被保存在一个动态分配的内存块中.然后JITCompiler将前面内部数据结构中被调用方法的地址替换为包含背地CPU指令的内存块地址.最后JITCompiler将跳转到该内存块中的代码上.这里的代码就是WriteLine方法(接受一个String类型参数的那个版本)的实现代码.当该代码执行完毕,它将返回到Main函数中,Main函数会接着继续执行下面的代码.

IL与代码验证

IL是一种基于堆栈的语言,这意味着它的所有指令或者是将操作推进一个执行堆栈中,或者是从堆栈中弹出结果.因为IL没有提供操作寄存器的指令,所以编译器开发人员可以很容易地产生出IL代码来.它们不必再考虑管理寄存器,并且需要的IL指令也比较少(因为不再需要寄存器的指令).

托管代码相较于非托管代码的一个优势就是:通过验证托管代码,我们可以确保他们不会访问它们不应该访问的内存,因此也就不会干扰另一个应用程序.这意味着我们可以在一个单独Windows虚拟地址空间运行多个托管应用程序.达到减少系统的进程数提高系统的性能,降低资源需求.默认情况下,一个托管EXE仅执行在它自己单独拥有的地址空间中(该地址空间中仅含有一个应用程序域),但是,CLR的宿主进程(例如IIS服务器和SQL Server)可以决定在一个操作系统中运行多个应用程序域.

.net框架内裤

在.NET框架中包括一组.NET框架类库(Framework Class Library) 简称FCL程序集,其中含有几千个类型的定义,每一个类型都提供了某种功能.总的来说,CLR和FCL允许开发人员创建以下几种应用程序:

㈠XML Web服务 即XML WebService 又简称为Web服务(Web Services) .该服务使得我们可以非常容易地通过互联网来进行方法调用.XML Web服务是.NET整个平台创新中最重要的一环.

㈡Web窗体  用的最多的,就不简介了

㈢Windows窗体,即WInform,Windows图形用户界面(GUI)应用程序.和使用Web窗体创建应用程序相反,Windows窗体可以使我们利用Windows桌面提供的更强大,更高效的功能.

㈣Windows控制台应用程序,对于较简单的用户界面需求,控制台应用程序为我们提供了一个快速,轻便的创建方式.各种编译器,实用程序,工具通常被实现为控制台应用程序.

㈤Windows服务 利用.NET框架,我们可以创建出由Windows服务器控制管理控制的服务程序.

㈥组件库  .net框架允许我们创建单独的组件(类型),它们可以应用于前面提到的各种类型应用程序.

FCL中的大多数命名空间提供的类型都可用于各种应用程序.表1.2列出了一些较为通用的命名空间,并且简要地描述了其类型的用途:

⑴System:其中的类型是为所有应用程序使用的一些基本类型.

⑵System.Collections 其中的类型用于管理对象集合.包括常用的集合类型,例如堆栈,队列,散列表等

⑶System.Diagnostics 其中的类型用于帮助诊断和调试应用程序

⑷System.Drawing 其中的类型用于操作二维图形.它们典型地用于Windows窗体应用程序,以及创建Web窗体页面中显示的图像

⑸System.EnterpriseServices 其中的类型用于管理事务,队列组件,对象池,JIT激活(这里的JIT不同于.NET框架中所言的JIT编译,它特指COM+组件服务,即.NET内的企业服务中对象的即时激活技术)、安全以及其他一些提高服务器程序中托管代码效能的特性.⑹System.Globalization 其中的类型用于多国语言支持,例如字符串比较,格式化以及日历功能

⑺System.IO 其中的类型用于操作I/O流,遍历目录和文件

⑻System.Management 其中的类型通过Windows管理设备(Windows Management Instrumentation 简称WMI)来管理企业中的计算机

⑼System.Net 其中的类型用于网络通信

⑽System.Reflection 其中的类型用于查看元数据以及延迟绑定类型和它们的成员

⑾System.Resources 其中的类型用于操作外部数据资源

⑿System.Runtime.InteropServices 其中的类型允许托管代码访问非托管操作系统平台中的一些功能,如COM组件和Win32 DLL内的函数

⒀System.Runtime.Remoting 其中的类型用于从远程机器上访问类型

⒁System.Runtime.Serialization 其中的类型用于持久化对象实例,以及一个流中重新产生对象实例

⒂System.Security 其中的类型用于保护数据和资源

⒃System.Text 其中的类型用于以不同的编码方式来操作文本

⒄System.Threading 其中的类型用于异步操作,以及同步访问资源

⒅Stytem.Xml其中的类型用于处理XML模式和数据

除此之外,FCL还提供了一些命名空间,其中的类型用于创建某些特定的应用程序.如下:

1.System.Web.Services 其中的类型用于创建XML Web服务

2.System.Web.UI 其中的类型用于创建Web窗体

3.System.Windows.Forms 其中的类型用于创建Windows GUI应用程序.

4.System.ServiceProcess 其中的类型用于创建由SCM控制的Windows服务

由以上可知,CLR的所有内容都是围绕着类型展开的.类型为应用程序和组件提供了它们所需的功能.类型也作为一种机制使得一种语言编写的代码可以和另一种语言编写的代码进行无缝地集成.由于类型是CLR的基础,微软为此专门制定了一个正式的规范—–通用类型系统(CTS Common Type Sytstem)来描述类型的定义和行为.

通用类型系统

CTS规范规定一个类型可以包含0个或多个成员.先简单了解一下

•字段  字段是一个属于对象状态部分的数据成员.字段由它们的名称和类型标识

•方法 方法是一个在对象上执行某种操作的函数,通常会改变对象的状态.方法有一个名称、签名和修饰符.方法的签名指定了方法的调用约定、参数个数(以及他们的顺序)、参数类型以及返回值类型.

•属性 对于属性的调用者,属性看起来非常类似于字段.但是对于属性的实现者,属性看起来更像一个或两个方法.属性允许实现者在访问数值之前验证输入参数和对象状态的有效性,或者仅在需要的情况下进行求值运算.属性使得类型的用户可以使用简化的语法表达他们的意图.可以创建只读,只写和可读可写三种属性.•事件  事件允许在一个对象和其他关联的对象之间建立一个通知机制.例如,一个按钮可以提供一个事件,当该按钮被按下时,其他对象得到通知.

CTS还定义了类型可见性和访问类型成员的一些规则.例如,将一个类型修饰为public(在c#中使用public),将使得它对于任何程序集都是可见的.另一方面,将一个类型修饰为assembly(在c#中使用internal),将使得它仅对于其所定义的程序集中的代码可见.CTS建立了以程序集作为类型可见性边界的规则,而CLR实现了这种可见性规则.访问类型有:private,protected,internal,protected internal,public.

此外,CTS还定义了诸多规则来管理类型继承,虚函数,对象生存期等事项.设计这些规则的目的是使它们的语意可以用现代编程语言方便表达出来.实际上,我们甚至不需要学习CTS规则,因为我们选择的语言已经提供了我们熟悉的语言语法和类型规则,并且在生成托管模块时会将这些特定语言的语法映射为CLR”语言”.

通用语言规范

如果希望创建的类型可以被其他编程语言方便地访问,只能使用编程语言中那些对其他语言来说也可用的特性.为了解决这一问题,微软定义了一个通用语言规范(CLS specification),该规范为编译器厂商详细描述了面向CLR的编译器必须支持的一个最小特性集合. 如果打算采用某种语言设计一个类型,并且希望该类型能被其他语言使用,那么就不应该利用任何CLS之外的特性.那样做将意味着使用其他编程语言的开发人员可能访问不到该类型的成员.

与非托管代码互操作

托管代码调用Dll中的非托管函数  托管代码可以很容易地使用一种称作P/invoke(即Platform Invoke,平台调用)的机制来调用DLL(译注:这里的DLL指的是传统动态链接库文件)中的函数.实际上,许多FCL中定义的类型内部都调用了从Kernel32.dll,User32.dll等动态链接库中导出的函数.许多编程语言都提供了这样的机制,使得从托管代码中调用DLL中的非托管函数变得非常容易.例如,C#或者Visual Basic 应用程序就可以调用从Kernel32.dll中导出的CreateSemaphore函数.

托管代码使用现存的COM组件(非托管组件作为COM服务器)  许多公司已经实现了大量的COM组件.利用这些COM组件的类型库,我们可以创建出描述它们的托管集.托管代码可以访问其他一样访问这些托管程序集中的类型.

非托管代码使用托管类型(托管类型作为COM服务器) 许多现存的非托管代码都需要提供COM组件才能正常运行.但是用托管代码来实现这些组件会更容易,因为这样可以避免处理引用计数和COM接口.例如我们可以用C#或者Visual Basic来创建ActiveX控件或者shell扩展程序.

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,105
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,582
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,429
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,200
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,836
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,919