品味细节,CLR的执行模型

📅 2026/7/5 1:58:32
品味细节,CLR的执行模型
从菜鸟刚接触到.net时菜鸟就知道CLR VIA C#是一本很牛的书为什么CSDN会告诉你——总会有人问“学.net什么书籍好”这个本没有标准答案的问题菜鸟却从各种大牛一致的回答中找到了标准答案C#入门经典—C#高级编程—CLR VIA C#,于是乎对于大牛们的信任这三本书都躺在菜鸟的床头。虽然菜鸟很菜但菜鸟喜欢在CDSN、博客园、codeproject菜鸟英语不堪忍睹每次都还需要打开Google翻译上闲逛属于那种不厚道的看帖不回帖的一员甚至过了相当长的一段时间都还没有注册不是菜鸟不想回答而是菜鸟水平实在不堪忍睹怕误导人家更怕关公面前耍大刀。菜鸟发现自己错了为什么你懂的为此在大三暑假的时候菜鸟在CSDN上走火入魔似的回答了别人一个月的问题即时菜鸟不会的其实大多都不会菜鸟会去翻书、找度娘、搜谷姐想尽办法帮别人回答一个月后菜鸟的等级竟然有一颗星星了当时激动的跑到灌水区散了下分但之后菜鸟又回到了看帖不回帖的状态或许是开学了或许是DOTA去了或许是没激情了反正具体什么原因已记不清楚。“纸上得来终觉浅须知此事要躬行”如果仅仅是看别人的文章或书籍可能就在看的时候有感觉合上之后就什么也不记得了所以菜鸟决定将自己看到的或学到的好东西记录下来——不再为看不懂高深的技术文章而烦恼而是拿起《CLR VIA C#》踏踏实实从头到底边阅读边写笔记。在菜鸟上篇菜鸟CLR VIA C#之旅—开始旅行千里之行始于足下完成后得到了很多大牛们的支持在此菜鸟深表感谢更要谢谢那些提出意见的高手们志志同学首先发了了菜鸟一个低级的错误“将Hello world写成了Hello word”当时冯同学的一句“啥时候出一片 Hello,excel! 啊”我还疑惑不解哎太粗心了。对于 toEverybody的留言“又要人在研究C#编译器产生的乱码呀//唉....”菜鸟不敢苟同但从这个留言中可以看出很多的园友都把目光和焦点注意在如何理解IL代码这个问题上。这真是个莫大的好消息因为很明显大家的思路慢慢的从应用向底层发生着转变技巧性的东西是一个方面的积累底层的探索为也是必不可少的修炼anytao。对于.NET程序员来说IL代码意味着Ø通用的语言基础是.NET运行的基础当我们对程序运行的结果有异议的时候如何透过本质看表面需要我们从本质入手来探索这时IL是你必须知道的基础Ø 元数据和IL语言是CLR的基础了解必要的中间语言是深入认识CLR的捷径Ø 大量的事例分析是以IL来揭密的因此了解IL是读懂他人代码的必备基础可以给自己更多收获。很明显这些优越性足以诱惑我们花时间和精力涉猎其中。然而了解了IL的好处并不意味着我们应该过分的来关注IL有人甚至可以洋洋洒洒的写一堆IL代码来实现一个简单Hello world程序但是正如我们知道的那样程序设计已经走过了几十年的发展如果纯粹的陶醉在历史中除了脑子不好没有其他的解释。不然看见任何代码都以IL的角度来分析又将走进另一个误区我们的宗旨是追求但不过分。好了开始《CLR VIA C# 》第一章 “CLR的执行模型”的学习了解下应用程序是如何执行的。如果能清楚的了解代码是如何运行的菜鸟认为对于以后代码的调试和优化将起到关键作用。1 将源代码编译成托管模块只要编译器是面向CLR的任何语言其创建的源代码都会用一个对应的编译器来检查语法和分析源代码最终结果都是生成一个托管模块managed module。托管模块托管模块是一个标准的32位Microsoft Windows可移植执行体PE32文件或者是一个标准的64位Windows可移植执行体PE32文件它们都需要CLR 才能执行。顺便说一句托管的程序集总是利用了Windows的数据执行保护Data Execution PreventionDEP和地址空间布局随机化Address Space Layout RandomizationASLR这两个功能旨在增强整个系统的安全性。托管模块包括以下几个部分1PE32或PE32头PE32能在windows32位或64位机器上运行PE32只能在64位机器上运行。2CLR头包含了需要的CLR版本一些flag托管模块入口方法Main方法以及各种信息。3IL中间语言代码编译器编译源代码后生成的代码在运行的时候CLR再将IL编译为本地CPU指令。4元数据每个托管模块都包含元数据表有两种类型一种类型的表描述源代码中定义的类型和成员一种类型的表描述源代码中引用的类型和成员。元数据的用途Ø在编译时元数据消除了对头和库文件的需求。ØVS的“智能感知功能”就是依靠元数据来解译一个类型提供了什么方法、属性、事件和字段。如果是一个方法将是传入的参数。ØIL的“安全”检查需要元数据。Ø元数据可以对一个对象的代码序列化存入到内存发生到另一台机器上。可以在另一台机器上进行反序列化来重建对象状态。Ø元数据可以运行垃圾收集器跟踪对象的生存期。2 将托管模块合并成程序集在这幅图中一些托管模块和资源或数据文件准备交由一个工具处理。该工具生成单独一个PE32()文件来表示文件的逻辑性分组。实际发生的事情是这个PE32()文件包含一个名为“清单”manifest的数据块。清单是由元数据表构成的另一种集合。这些表描述了构成程序集的文件由程序集中的文件实现的公开导出的类型以及与程序集关联在一起的资源或数据文件。程序集的类型既可以生成单文件程序集也可以生成多文件程序集取决于你对于编译器或工具的选择。程序集清单中的内容信息说明程序集名称指定程序集名称的文本字符串。版本号主版本号和次版本号以及修订号和内部版本号。公共语言运行时使用这些编号来强制实施版本策略。区域性有关该程序集支持的区域性或语言的信息。此信息只应用于将一个程序集指定为包含特定区域性或特定语言信息的附属程序集。具有区域性信息的程序集被自动假定为附属程序集。强名称信息如果已经为程序集提供了一个强名称则为来自发行者的公钥。程序集中所有文件的列表在程序集中包含的每一文件的散列及文件名。请注意构成程序集的所有文件所在的目录必须是包含该程序集清单的文件所在的目录。类型引用信息运行时用来将类型引用映射到包含其声明和实现的文件的信息。该信息用于从程序集导出的类型。有关被引用程序集的信息该程序集静态引用的其他程序集的列表。如果依赖的程序集具有强名称则每一引用均包括该依赖程序集的名称、程序集元数据版本、区域性、操作系统等和公钥。3 加载公共语言运行时每个程序集既可以是一个可执行应用程序也可以是一个DLL但最终都是由CLR管理这些程序集中代码的执行 这意味着必须在目标机器 上安装好.NET Framerwork.要知道是否已安装.Net Framework只需检查%SystemRoot%System32目录中的MSCorEE.dll文件。存在该文件表明.Net Framework已安 装。.NET Framework SDK提供了一个名为CLRVer.exe的命令行实用程序它能列出一台机器上安装的所有CLR版本,比如菜鸟电脑上4 执行程序集的代码托管程序集同时包含元数据和ILIL是与CPU无关的机器语言为了执行一个方法必须把它的IL转换成本地CPU指令。方法的第一次调用Main方法首次调用WriteLine时Ø JITCompiler函数被调用它知道要调用的是哪个方法以及具体是什么类型定义了该方法Ø JITCompiler会在定义该类型的程序集的元数据中查找被调用的方法的IL。Ø JITCompiler验证IL代码并将IL代码编译成本地CPU指令。本地CPU指令被保存到一个动态分配的内存块中。Ø JITCompiler返回CLR为类型创建的内部数据结构找到与被调用的方法对应的那一条记录修改最初对JITCompiler的引用让它现在指向内存块其中包含了刚才编译好的本地CPU指令的地址。Ø JITCompiler函数跳转到内存块中的代码。这些代码正是WriteLine方法获取单个String参数的那个版本的具体实现。这些代码执行完毕并返回时会返回Main中的代码并跟往常一样继续执行。一个方法只有在首次调用时才会有一些性能损失以后对该方法的调用都以本地代码的形式全速运行无需重新验证IL并把它编译成本地代码。一旦应用程序终止编译好的代码也会被丢弃所以再次运行JIT编译器必须再次将IL编译成本地指令。最后了解下通用类型系统Common Type SystemCTS以及公共语言规范Common Language SpecificcationCLSCLR是完全围绕类型展开的而了解类型则有必要把焦点放在.NET类型体系的公共基础架构上由于通用类型系统的存在.NET平台下的各种语言可以无缝的集成从MSDN的官方解释上我们可以看到通用类型系统定义了如何在运行库中声明、使用和管理类型同时也是运行库支持跨语言集成的一个重要组成部分。通用类型系统执行以下功能建立一个支持跨语言集成、类型安全和高性能代码执行的框架。提供一个支持完整实现多种编程语言的面向对象的模型。定义各语言必须遵守的规则有助于确保用不同语言编写的对象能够交互作用。