CLR(Common Language Runtime)公共语言进行时是一个可由多种编程语言使用的“进行时”。

  • 将源代码编译成托管模块

可用支持CLR的任何语言创建源代码文件,然后用对应的编译器检查语法和分析源代码。无论选择哪个编译器,结果都是托管模块(managed module)。托管模块是标准的32位Microsoft Windows可移植执行体(PE32)文件,或者是标准的64位Windows可移植执行体(PE32+)文件,他们都需要CLR才能执行。(注:PE是Portable Executable(可移植执行体)的简称)

本机代码编译器(native code compilers)生成的是面向特定CPU架构(比如x86,x64或ARM)的代码。相反,每个面向CLR的编译器生成的都是IL(中间语言)的代码。

  除了生成IL面向CLR的编译器还要在每个托管模块中生成完整的元数据(metadata)。元数据简单地说就是一个数据表集合。一些数据表描述了模块中定义了什么(比如类型及其成员),另一些描述了模块引用了什么(比如导入的类型及成员)。

  Microsoft的C++编译器默认生成包含非托管(native)代码的exe/dll模块,并在运行时操作非托管数据(native内存)CLR即可执行。然而,通过指定/CLR命令行开关,C++编译器就能生成包含托管代码的模块。当然,最终用户必须安装CLR才能执行这种代码。在前面提到的所有Microsoft编译器中,C++编译器是独一无二的,只有它才允许开发人员同时写托管和非托管代码,并生成到同一个模块中。它也是唯一允许开发人员在源代码中同时定义托管和非托管数据类型的Microsoft编译器。

  • 将托管模块合并成程序集

  CLR实际不和模块工作。它和程序集工作。

  首先,程序集是一个或多个模块/资源文件的逻辑分组。其次,程序集是重用、安全性以及版本控制的最小单元。

  

  图中一些托管模块和资源(或数据)文件准备交由一个工具处理。工具生成代表文件逻辑分组的一个PE32(+)文件。实际发生的事情是,这个PE32(+)文件包含一个名为清单(mainfest)的数据块。清单也是元数据表的集合。这些表描述了构成程序集的文件、程序集中的文件所实现的公开导出的类型以及与程序集关联的资源或数据文件。(注:所谓公开导出的类型,就是程序集中定义的public类型,它们在程序集内部外部均可见。)

  编译器默认将生成的托管模块转换成程序集。也就是说,C#编译器生成的是含有清单的托管模块。清单指出程序集吸由一个文件构成。对于只有一个托管模块而且无资源(或数据)文件的项目,程序集就是托管模块,生成过程中无需执行任何额外的步骤。但是,如果希望将一组文件合并到程序集中,就必须撑握更多的工具(比如程序集链接器AL.exe)及其命令行选项。

  • 加载公共语言运行时CLR

  可执行文件(exe)运行时,Windows检查EXE文件头,决定是创建32位还是64位进程之后,会在进程地址空间加载MSCorEE.dll的x86,x64或ARM版本。如果是Windows的x86或ARM版本,MSCorEE.dll的x86版本在%SystemRoot%\System32目录中。如果是Windows的x64版本,MSCorEE.dll的x86版本在%SystemRoot%\SysWow64目录中,64位版本则在%SystemRoot%\System32目录中(为了向后兼容)。然后,进程的主线程序调用MSCorEE.dll中定义的一个方法。这个方法初始化CLR,加载EXE程序集,再调用其入口方法(Main)。随即,托管应用程序启动并运行。(PS:微软在64位系统中将所有处理32位程序的工具都放在SysWow64目录下,Wow就是Windows on Windows的意思。而System32目录是处理64位程序的。还叫32,只是延续了以前的叫法,其实应该是64)

  • 执行程序集的代码  

  开发人员一般用c#,VB等高级语言进行编程。它们的编译器将生成IL。然而,和其他任何机器语言一样,IL也能使用汇编语言编写,Microsoft甚至专门提供了名为ILAsm.exe的IL汇编器和名为ILDasm.exe的IL反汇编器。注意,高级语言通常只公开了CLR全部功能的一个子集。然而IL汇编语言允许开发人员访问CLR的全部功能。要知道CLR具体提供了哪些功能,唯一的办法是阅读CLR文档。

  为了执行方法,首先必须把方法的IL转换成本机(navive)CPU指令。这是CLR的JIT(just-in-time或者"即时")编译器的职责。

就在Main方法执行之前,CLR会检测出Main的代码引用的所有类型。这导致CLR分配一个内部数据结构来管理对引用类型的访问。图中Main方法引用了一个Console类型,导致CLR分配一个内部结构。在这个内部数据结构中,Console类型定义的每个方法都有一个对应的记录项。每个记录项都含有一个地址。根据此地址即可找到方法的实现。对这个结构初始化,CLR将每个记录项都设置成(指向)包含在CLR内部的一个未编档函数。我将该函数称为JITCompiler。

Main方法首次调用WriteLine时,JITCompiler函数会被调用。JITCompiler函数负责将方法的IL代码编译成本机CPU指令。由于IL是”即时“(just in time)编译的,所以通常将CLR的这个组件称为JITter或者JIT编译器。

JITCompiler函数被调用时,它知道是要调用的是哪个方法,以及具体是什么类型定义了该方法。然后,JITCompiler会在定义(该类型的)程序集的元数据中查找被调用方法的IL。接着JITCompiler验证IL代码,并将IL代码编译成本机CPU指令。CPU指令保存到动态分配的内在块中。然后 ,JITCompiler回到CLR为类型创建的内部数据结构,找到与被调用方法对应的那条记录,修改最初对JITCompiler的引用,使其指向内在块(其中包含了刚才编译好的本机CPU指令)的地址。最后,JITCompiler函数中跳转到内存块中的代码。这些代码正是WriteLine方法(获取单个String参数的那个版本)的具体实现。代码执行完毕并返回时,会回到Main中的代码,并像往常一样继续执行。

现在,Main要第二次调用WriteLine。这一次,由于已对WriteLine的代码进行了验证和编译,所以会直接执行内存块中的代码,完全跳过JITCompiler函数。WriteLine方法执行完毕后,会再次回到Main。

  • IL和验证

  将IL编译成本机CPU指令时,CLR执行一个名为验证(verification)的过程。

  CLR确实提供了在一个操作系统中执行多个托管应用程序的能力。每个托管应用程序都在一个AppDomain中执行。每个托管EXE文件默认都在它自己的独立地址空间中运行,这个地址空间只有一个AppDomain。然而,CLR的宿主进程(比如IIS或者Microsoft SQL Server)可决定在一个进程中运行多个AppDomain。

  • 本机代码生成器:NGen.exe

  使用用.NET Framework提供的NGen.exe工具,可以在应用程序安装到用户的计算机上时,将IL代码编译成本机代码。由于代码在安装时已经编译好,所以CLR的JIT编译器不需要在运行时编译IL代码,这有助于提升应用程序的性能。NGen.exe能在以下两种情况下发挥重要作用。

  1提高应用程序的启动速度

  2减少应用程序的工作集(所谓工作集,是指在进程的所有内存中,已映射的物理内存那一部分(即这些内存全在物理内存中,CPU可以直接访问);进程还有一部分虚拟内存,它们可能在转换列表中(CPU不能通过虚拟地址访问,需要Windows映射之后才能访问);还有一部分内存在磁盘上的分页文件里。)

  NGen.exe生成的文件有以下问题

  1没有知识产权保护

  2NGen生成的文件可能失去同步

  3较差的执行时性能

  • Framework类库

  FCL(Framework Class Library)

  • 通用类型系统

  Microsoft制定了一个正式规范来描述类型的定义和行为,这就是“通用类型系统”(Common Type System,CTS)。

  • 公共语言规范

  要创建很容易从其他编程语言中访问的类型,只能从自己 的语言中挑选其他所有语言都支持的功能。为了在这个方面提供帮助,Microsoft定义了“公共语言规范”(Common Language Specification,CLS),它详细定义了一个最小功能集。任何编译器只有支持这个功能集,生成的类型才能兼容由其他符合CLS、面向CLR的语言生成组件。
  (个人的理解:CLS是为了不同编程语言之间互相调用而设计的,如果只用一种语言,就不用考虑CLS的规范)

  (说明:文中99%内容来自书本原文。把知识要点搬运到这里,只是为了方便本人复习、查阅)

CLR via C#读书笔记一:CLR的执行模型的更多相关文章

  1. CLR via C# 读书笔记-21.托管堆和垃圾回收

    前言 近段时间工作需要用到了这块知识,遂加急补了一下基础,CLR中这一章节反复看了好多遍,得知一二,便记录下来,给自己做一个学习记录,也希望不对地方能够得到补充指点. 1,.托管代码和非托管代码的区别 ...

  2. [Clr via C#读书笔记]Cp1CLR执行模型

    Cp1CLR执行模型 本章的概念点 CLR=Common Language Runtime 内存管理,程序集加载,安全性,异常处理和线程同步.CLR是基础,支持着面向它的各种语言.各种语言会被对应的编 ...

  3. CLR via C# 读书笔记---常量、字段、方法和参数

    常量 常量是值从不变化的符号.定义常量符号时,它的值必须能在编译时确定.确定后,编译器将唱两只保存在程序集元数据中.使用const关键字声明常量.由于常量值从不变化,所以常量总是被视为类型定义的一部分 ...

  4. Clr Via C#读书笔记---I/O限制的异步操作

    widows如何执行I/O操作      构造调用一个FileStream对象打开一个磁盘文件-----FileStream.Read方法从文件中读取数据(此时线程从托管代码转为本地/用户模式代码)- ...

  5. Clr Via C#读书笔记---计算限制的异步操作

    线程池基础 1,线程的创建和销毁是一个昂贵的操作,线程调度以及上下文切换耗费时间和内存资源. 2,线程池是一个线程集合,供应你的用程序使用. 3,每个CLR有一个自己的线程池,线程池由CLR控制的所有 ...

  6. Clr Via C#读书笔记---CLR寄宿和应用程序域

    #1 CLR寄宿: 开发CLR时,Microsoft实际是将他实现成包含在一个dll中的COM服务器.Microsoft为CLR定义了一个标准的COM接口,并为该接口和COM服务器分配了GUID.安装 ...

  7. Clr Via C#读书笔记---程序集的加载和反射

    #1 加载程序集 Assembly.Load: public class Assembly { public static Assembly Load(AssemblyName assemblyRef ...

  8. Clr Via C#读书笔记---垃圾回收机制

    #1 垃圾回收平台的基本工作原理: 访问一个资源所需的具体步骤: 1)调用IL指令newobj,为代表资源的类型分配内存.在C#中使用new操作符,编译器就会自动生成该指令.2)初始化内存,设置资源的 ...

  9. Clr Via C#读书笔记---线程基础

    趣闻:我是一个线程:http://kb.cnblogs.com/page/542462/ 进程与线程 进程:应用程序的一个实例使用的资源的集合.每个进程都被赋予了一个虚拟地址空间. 线程:对CPU进行 ...

  10. CLR via C# 读书笔记 6-2 不同AppDomain之间的通信 z

    跨AppDomain通信有两种方式 1.Marshal By reference : 传递引用 2.Marshal By Value : 把需要传递的对象 通过序列化反序列化的方式传递过去(值拷贝) ...

随机推荐

  1. Flask的错误日志处理和|ORM操作

    flask有个很人性化的处理就是 你的错误的输出是可以通过错误日志来自定义  ,让你输入的错误不再是“大黄页”, 通过 errorhandler()来装饰函数之后你的所有的输入错误的函数你都会进入这个 ...

  2. linux something

    使用的UbuntuKylin  source error e1:apt-get update提示没有证书 e2:访问不了阿里云源服务器 e3:GPG 错误:http://download.mono-p ...

  3. [翻译] BTSimpleRippleButton

    BTSimpleRippleButton https://github.com/balram3429/btSimpleRippleButton This is a custom button for ...

  4. php实现session入库

    为什么要把session存入数据库?有什么用? 可以:统计在线人数,现实多站点session共享(通行证),控制同个账号登入人数等. 要实现session的入库,有关键的几个基本知识: session ...

  5. 团队开发中,eclipse中安装jre

    团队合作中配置jre时,jre名称应该保持一致,否则不要提交.classpath文件 window-preferences     团队合作中,JRE name一定要一致!

  6. 学习python第三天单行函数

    1.去重:distinct关键字 需求:查看公司一共有多少部门? select department_id from employees;此代码会查出107条记录,存在部门重复的问题! select ...

  7. luogu P4199 万径人踪灭

    嘟嘟嘟 方案:回文子序列数 - 回文子串数. 回文子串数用manacher解决就行了,关键是怎么求会问序列数. 一个比较好的\(O(n ^ 2)\)的算法:对于一个回文中心\(i\),\(O(n)\) ...

  8. 记一次爬虫经历(友话APP的Web端)

    背景:学校为迎接新生举办了一个活动,在友话APP的校园圈子内发布动态即可参与活动,最终抽取数名同学赠送福利. 分析:动态的数量会随着迎新的开始逐渐增加,人工统计显然不现实,因此可以使用爬虫脚本在友话A ...

  9. python基础整理6——爬虫基础知识点

    爬虫基础 什么是爬虫: 网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本.另外一些不常使用的名字还有蚂蚁. ...

  10. svn 查找指定文件和后缀变化

    有时候需要批量查找一些包含特定关键字文件名和后缀的文件,先把所有变化文件通多svn diff命令 输出到一个文件,然后使用如下命令: grep -i 'data*.xml' change.txt |a ...