• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

C#底层运行机制

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

 

源代码——》托管模块——》程序集—JIT—》编程CPU指令

1.1     .NET框架下,首先将源代码编译为托管模块

CLR编译过程:C#源码文件——(C#编译器编译)——》托管模块

     

 

 

托管模块是一个需要CLR环境才能执行的标准windows PE文件,包含IL元数据以及PE表头CLR表头

 

iL: 又叫托管代码,是编译器编译原文件后产生的指令,CLR会在运行时将IL编译成本地CPU指令。

元数据实际上是一个数据表集合,用来描述托管模块中所定义和引用的内容。VS能够智能感知就得益于元数据的描述

内容: 1.描述了模块中定义的内容,比如类及其成员
             2.
指出了托管模块引用的内容,比如导入的类及其成员
             3.
清单manifest,描述了构成Assembly的文件,由Assembly中的文件实现的公共导出类型,与Assembly相关联的资源/数据文件
元数据总是嵌入到与代码相同的EXE/DLL中,始终与IL保持同步

用途:

        1.消除了对头/库文件的依赖,直接从托管模块中读取
              2.
智能感知,从元数据中解析
              3.
代码验证,使用元数据确保代码只执行安全操作
             4.
正反序列化
             5.
垃圾收集器跟踪对象的生存期以及对象的类型

 

 

 

PE表头:标准Windows PE文件表头,包含文件类型(如GUICUI等),以及文件创建时间等信息,在32位还是64位上运行

CLR表头:包含标识托管模块的一些信息。如CLR版本号,托管模块入口点方法main方法)以及MethodDef元数据等等

 

 

 

1.2托管模块组合为程序集

程序集:一个或多个托管模块/资源文件的逻辑分组,是最小的重用,安全性以及版本控制单元。
        
既可以生成单文件程序集,也可以生成多文件程序集,这由编译器工具决定。
        CLR
是和程序集一起工作的,而不是和托管模块

 

一般编译器会默认将生成的托管模块生成一个程序集,CLR直接打交道的是程序集(assembly),程序集包含一个或多个托管模块,以及资源文件的逻辑组合。组合过程如下:

 

 

 

左侧为一些托管模块,在经过一些工具的处理后,生成了一个PE文件,也就是程序集。程序集中有一个清单(manifest)数据块,用来描述组成程序集的所有文件。此外,程序集还包含它所引用的程序集的信息,这就使得程序集可以实现自描述。这样CLR就直接知道了程序集所需要的所有内容,因此程序集的部署比非托管组件要容易。

1.2     加载 CLR

 CLRVer命令,查看机器上所有CLR版本
    csc
/plattform开关,决定生成什么样的程序集:AnyCPUx86x64Itanium

程序要运行,首先确定及其安装了.NET框架.然后,加载并初始化CLR

 

 

 

1.4 程序集执行

ILAsm命令,将IL编译成AssemblyILDasmAssembly编译成IL

IL代码要通过及时编译器JITter转化成CPU指令

 

方法第一次调用:

(     在方法首次执行时,CLR检测出Main的代码引用的所有类型,于是CLR分配一个内部数据结构,用于管理对引用类型的访问

在这个内部结构中,每个方法都有一条对应的纪录以及地址

   对此结构进行初始化时,CLR将每条纪录都设置为CLR内部包含的一个未文档化的函数,即 JITCompiler函数
        JITCompiler
函数被调用时,找到相应方法的IL,编译成本地CPU指令,并保存到一个动态内存块中,将该内存地址存入内部结构中,最后JITCompiler函数会跳转到内存块中的代码

)

1. 当程序第一次运行时,会调用JITCompiler函数,它可以知道调用了那些方法,以及定义该方法的类。

2. 然后JITCompiler函数在元数据中搜索该IL代码的位置,验证后转换成本地CPU指令。将指令保存在动态分配的内存中

3. JITCompiler将被调用方法地址改为第2步的内存地址

4. 跳转到上述代码块上执行代码

5. 执行完成后返回

 

 

IL是基于堆栈的语言,而且是无类型的。

 

再次调用方法

在一个程序中,我们经常反复调用同一个方法,当再次调用该方法时就不需要重复进行验证了,可以直接调用内存块中已有的本地代码,完全跳过JITCompile函数的验证和编译过程。所以同一方法只有在第一次调用时会产生一些性能损失,后续调用就可以全速进行了。

关闭程序

 

由于编译器将本地代码保存在动态内存中,所以关闭程序时本地代码将发生丢失。当再次启动程序或者同时运行程序的两个实例时,JIT编译器将再次将IL代码编译为本地指令。

 

 

谈谈IL安全问题


IL
是基于堆栈的。所有指令都是:将操作数压栈,结果则从栈中弹出
IL
有安全验证机制,保证每一行IL代码是正确的,不会非法访问内存,每个托管EXE都在独自的AppDomain中运行。

不安全代码:允许C#直接操作内存字节,在COM互操作时使用,csc/unsafe开关标记包含不安全代码,其中所有方法都使用unsafe关键字。
PEVerify
命令检查程序集所有方法,指出其中的不安全代码方法

 


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
C#:const和readonly的区别发布时间:2022-07-13
下一篇:
转c#:开源项目(大全)发布时间:2022-07-13
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap