最近买了一本书《CLR via C#》阅读了第一章 - CLR 的执行模型,对 .NET 一直提到的 CLR 和 .NET Framework 有了一个大致的了解。我理解主要体现在:
■ 各种术语有了一个大致的体会:CTS CLS 和 CLI 、CLR 与 .NET Framework、 IL(Intermediate Language) 、CIL(Common Intermediate Language)和 托管代码(Managed Code)。
■ 用 C# 写完一个程序被编译成应用程序后,是如何执行的。
■ CTS, CLS 是 CLI 规范的一部分,.NET Framework 是微软的 CLI 实现,当然微软也是 CLI 规范的发起者。
(注:以下是根据书中内容做的笔记)
按照书中的章节,CLR 的执行模型如下:将源代码编译成托管模块 -> 将托管模块合并成程序集(assembly) -> 加载公共语言运行时(CLR)-> 执行程序集的代码。
■ 什么是 CLR ?
引用 MSDN 中的一段文字:
“The .NET Framework provides a run-time environment called the common language runtime, which runs the code and provides services that make the development process easier.
Compilers and tools expose the common language runtime's functionality and enable you to write code that benefits from this managed execution environment. Code that you develop with a language compiler that targets the runtime is called managed code”。
CLR 是一种概念性的统称,.NET 提供了这么一个运行时环境叫 CLR ,具体体现在编码阶段用到的类库,内存管理,异常处理等;编译阶段用到的编译器;执行阶段 JIT 编译器将 IL 转换成本机 CPU 指令。CLR 支持多种编程语言,采用 CLR 可以用不同的编程语言进行同一个项目开发。
■ 什么是托管模块,如何生成?什么是程序集,如何生成?托管模块和程序集有什么关联?
由书中可知,托管模块和程序集都是 PE(+) 文件,32 位或者 64 位 Windows 能加载它,并且能执行某些操作,但是托管模块需要被合并到程序集后才可以被使用。那么他们到底包含哪些内容,托管模块与程序集有哪些不同呢?
托管 PE 文件由 4 部分组成: PE32(+) 头、 CLR 头、元数据及 IL 。 IL 就是编译生成的托管代码。元数据是由几个表构成的二进制数据块。有三种表,定义表( definition table )、引用表( reference table )、清单表( mainifest table )。
程序集是一个或多个类型定义文件及资源文件的文件集合。在程序集的所有文件中,有一个文件容纳了清单(即,元数据中含有清单表)。清单也是一个元数据表集合,表中主要包含作为程序集组成部分的那些文件信息。以下是书中的部分描述:”程序集是抽象的概念,利用'程序集'这种概念性的东西,一组文件可作为一个单独的实体来对待。在 CLR 的世界中,程序集相当于'组件'。生成的每个程序集可以是可执行程序,也可以是 DLL ,最终由 CLR 管理这些程序集中的代码的执行。这意味着目标机器必须安装好 .NET Framework 。“。
托管模块在书中没有明确定义,应该就是指编译器生成的元数据表中未包含清单元数据表的 PE 文件。
常用的定义元数据表:
ModuleDef TypeDef MethodDef FieldDef ParamDef PropertyDef EventDef
常用的引用元数据表:
AssemblyRef ModuleRef TypeRef MemberRef
清单元数据表:
AssemblyDef FileDef ManifestResourceDef ExportedTypesDef
以上信息可以通过 ildasm.exe 工具查看。
关于日常描述这里归纳一下,假设 A.dll B.netmodule C.netmodule 这 3 个文件组成一个程序集, 其中 A.dll 文件中含有清单数据表( B C 中都未包含),我们则习惯叫 A.dll 是程序集文件,运行时 A.dll B.netmodule C.netmodule 要一起,缺少了 B 或者 C 运行时当用到其中的类型而文件找不到时,进程会报出异常。 A.dll 文件中含有清单数据表,清单数据表中具有关于 B 和 C 的信息,逻辑的认为 A.dll 已经包括了 B.netmodule 和 C.netmodule 。其他程序集需要和 B 或者 C 打交道时,也是间接的通过 A 进行,所以简略的叫 A.dll 是程序集文件。所以当遇见某文件是程序集文件时,需要再确认该程序集是否包含其他的文件。在这个例子中,B.netmodule 和 C.netmodule 则叫做托管模块。
指定以下任何命令行开关, C# 编译器都会生成程序集: /t[arget]:exe 、 /t[arget]:winexe 、 /t[arget]:appcontainerexe 、 /t[arget]:library 或者 /t[arget]:winmdobj 。所有这些开关都会造成编译器生成含有清单元数据表的 PE 文件。
C# 编译器还支持 /t[arget]:module 开关。这个开关指示编译器生成一个不含有清单元数据表的 PE 文件(有时叫托管模块)。 CLR 想访问其中的类型,必须将该文件添加到程序集。可以在 C# 编译器编译时用 /addmodule 开关添加模块到程序集。
另外也可以通过 AL.exe (程序集链接器)生成程序集。AL.exe 可以作用于多个编译器生成的托管模块。
☆ 生成单文件程序集。
文件 A.cs
class Program {
static void Main()
{
System.Console.WriteLine("Hello, World!");
}
}
csc /out:A.exe /target:exe /reference:mscorlib.dll A.cs (或 csc A.cs)
生成的 A.exe 是一个程序集,可以直接运行。这个程序集也就是它自身,未包含其他文件。
☆ 生成多文件程序集。
文件 A.cs
public class A {
public static void AFunc()
{
System.Console.WriteLine("This is AFunc!");
}
}
文件 B.cs
public class B {
public static void BFunc()
{
System.Console.WriteLine("This is BFunc!");
}
}
csc /out:B.netmodule /t:module B.cs
csc /out:A.dll /t:library /addmodule:B.netmodule A.cs
先生成托管模块 B.netmodule ,然后生成含有清单元数据表的文件 A.dll 并添加了 B.netmodule 到其中。这样就组成了一个多文件程序集。
文件 tmp.cs
class Program {
static void Main()
{
A.AFunc();
B.BFunc();
}
}
csc /r:A.dll tmp.cs
生成 tmp.exe 测试结果。
☆ 通过 AL.exe 生成多文件程序集。
csc /t:module A.cs
csc /t:module B.cs
al /out:MultiFileLib.dll /t:library A.netmodule B.netmodule
csc /r:MultiFileLib.dll tmp.cs
生成 tmp.exe 测试结果。这时生成的 tmp.exe 是程序集文件,含有清单元数据表,但不含有 IL 代码。
■ 程序集中的代码是如何执行的?
程序集中可以执行的代码是 IL 。面向 CLR 的不同编译器都可以生成 IL 。如书中例子。
C# 源代码文件 -> C# 编译器 -> 托管模块(IL 和 元数据)
Basic 源代码文件 -> Basic 编译器 -> 托管模块(IL 和 元数据)
IL 源代码文件 -> IL 汇编器 -> 托管模块(IL 和 元数据)(ILAsm.exe IL 汇编器和 ILDasm.exe IL 反汇编器)
高级语言只提供了 CLR 全部功能的一个子集。然后,IL 汇编语言允许开发人员访问 CLR 的全部功能。总之,采用最合适的编程语言来完成任务。
首先,IL 代码是无法直接执行的,为了执行方法,必须把 IL 转换成本机(native)CPU 指令。这是 CLR 的 JIT(just-in-time)编译器的职责。
以如下代码为例,程序集中的代码如何被执行呢?
static void Main() {
System.Console.WriteLine("Hello");
System.Console.WriteLine("Goodbye");
}
编译生成单文件程序集,将程序集加载到内存中时,为了执行方法 System.Console.WriteLine ,首先会在实现类型 System.Console 的程序集的元数据中查找方法 WriteLine ,获取到它的 IL ,JIT 编译器将 IL 翻译成本机 CPU 指令,然后再执行指令。再次调用 System.Console.WriteLine 函数时,由于函数已被 JIT 编译,此时直接执行内存中的本机 CPU 指令。以上就是程序集执行的简要过程。
★ 说明。
对于 IL 的说明。IL(Intermediate Language,中间语言)是一种与 CPU 无关的抽象机器语言,它需要被转换成本机 CPU 指令才可以执行。对于 CLR,IL 具体是指 CIL(Common Intermediate Language)起源于 MSIL(Microsoft Intermediate Language),之后 CLI(Common Language Infrastructure)标准化,现在已被正式称作 CIL 。具体资料可以查询 CIL 。
对于 托管 的说明。似乎 .NET Framework 习惯将与 CLR 有关的一些东西称为托管 xxx 。如 托管代码,托管 PE 文件,托管模块等。而与 CLR 没有关系与本机 CPU 直接相关,则称作非托管 xxx 。如用 C/C++ 编译器写的非托管代码。叫法而已。具体要根据上下文能找到说描述的对象。
■ CLI 规范。
以下是书中的内容:”CRL 一切都围绕类型展开。到目前为止,这一点应该很清楚了。类型向应用程序和其他类型公开了功能。通过类型,用一种编程语言写的代码与用另一种编程语言写的代码沟通。由于类型是 CLR 的根本,所以 Microsof 制定了一个正式的规范来描述类型的定义和行为。这就是通用类型系统(Common Type System,CTS)“。
以下是书中的内容:”不同的语言创建的对象可通过 COM 相互通信。CLR 则集成了所有语言,用一种语言创建的对象在另一种语言中,和用后者创建的对象具有相同的地位。之所以能实现这一的集成,是因为 CLR 使用了标准类型集,元数据以及公共执行环境。
要创建很容易从其他编程语言中访问的类型,只能从自己的语言中挑选其他所有语言都支持的功能。为了在这个方面提供帮助,Microsoft 定义了公共语言规范(Common Language Specification,CLS),它定义了一个最小功能集。任何编译器只有支持这个功能集,生成的类型才能兼容由其他符合 CLS 、面向 CLR 的语言生成的组件。“。
以下是书中的内容:”CLR/CTS 提供了一个功能集。有的语言公开了 CLR/CTS 的一个较大的子集。如果开发人员用 IL 汇编语言写程序,可以使用 CLR/CTS 提供的全部功能。“。具体如下图(仿照书中)所示。
实际上微软已经把 CTS, CLS 等进行了标准化,统称公共语言架构(Common Language Infrastructure, CLI),具体参考维基百科。下面是维基百科中引用的
一句话:”The .NET Framework and the free and open source Mono and Portable.NET are implementations of the CLI.“。可知 .NET Framework 是 Windows 上的实现。而 Mono 则支持跨平台,且开源。
目前有部分手游服务器就是采用 Mono 。因此这也是我认真学习 C# 的原因。毕竟了解一种 GC 的静态语言还是有必要的。其实我的想法是精通 C C# Lua ,以此为基础进行扩展。不知道最终能不能完成这个目标。加油。
引用:
- 认识CLR [《CLR via C#》读书笔记]
认识CLR [<CLR via C#>读书笔记] <CLR via C#>读书笔记 什么是CLR CLR的基本概念 通用语言运行平台(Common Language Runti ...
- CLR基础之一---认识CLR [《CLR via C#》读书笔记]
<CLR via C#>读书笔记 什么是CLR CLR的基本概念 通用语言运行平台(Common Language Runtime,简称CLR)是微软为他们的.Net虚拟机所选用的名称.这 ...
- 《CLR via C#》读书笔记 之 泛型
第十二章 泛型 2014-06-15 初始泛型 12.3 泛型基础结构 12.3.1 开放类型与封闭类型 12.3.2 泛型类型和继承 12.3.3 泛型类型同一性 12.3.4 代码爆炸 12.6 ...
- 《CLR via C#》读书笔记 之 计算限制的异步操作
<CLR via C#>读书笔记 之 计算限制的异步操作 2014-07-06 26.1 CLR线程池基础 返回 如25章所述,创建和销毁线程是一个比较昂贵的操作: 太多的线程也会浪费内存 ...
- 初入SG-UAP
初入SG-UAP SpriderMan 关注 2019.06.19 14:10 字数 1130 阅读 10评论 0喜欢 0 初次接触SG-UAP,将自己的见解以文字形式记录下来,希望能对初入的伙伴们有 ...
- SuperMap iClient for JavaScript初入
SuperMap iClient for JavaScript初入 介绍SuperMap for Js的简单使用. 推荐先看下这篇文档:SuperMap iClient for JavaScript ...
- Scala初入
何为Scala物 Scala为基于JVM虚拟机中的面向对象与函数式编程思想并且完全兼容Java的混合编程语言,可以是Scala与Java是同根同源的,既然Scala与JAVA都是基于JVM之上的编程语 ...
- 初学HTML5、初入前端
学习HTML5是一个漫长的过程,当中会遇到很多技术与心态上的变化.刚开始学习,我们不能发力过猛,需要一个相对稳定的状态去面对.多关注一些自己感兴趣的网站和技术知识,建立自己的信心与好奇心,为以后的学习 ...
- 0x00linux32位汇编初入--前期准备
0x00汇编初入--前期准备 一.汇编工具 在linux平台下常用的编译器为as,连接器为ld,使用的文本编辑器为vim,汇编语法为att 以下是一些工具: addr2line 把地址转换为文件名和行 ...
- 初入职场的建议--摘自GameRes
又开始一年一度的校招了,最近跑了几个学校演讲,发现很多话用短短的一堂职业规划课讲还远远不够,因为那堂课仅仅可能帮大家多思考怎样找到一份合适的工作,并没有提醒大家怎样在工作中发展自己的职业. 见过这么多 ...
随机推荐
- java与javax有什么区别?
http://zhidao.baidu.com/question/8702158.html java和javax都是Java的API包,java是核心包,javax的x是extension的意思,也就 ...
- java中的值传递和引用传递区别
值传递:(形式参数类型是基本数据类型):方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参数值的改变不影响 ...
- iOS陆哥开发笔记(七) (AVFoundation简单介绍)
在AVFoundation框架中AVAudioRecorder类专门处理录音操作,支持多种音频格式. 以下是经常使用的属性和方法: 属性 说明 @property(readonly, getter=i ...
- Realm多线程中的那些坑...
个人在开发中遇到的一些小坑... 可能会持续更新... 1.RealmObject自带线程保护功能.仅仅能在创建它的线程中訪问.在子线程中不能訪问. 也就是说.假设你在主线程中new了一个RealmO ...
- TPM:dTPM(硬件)和fTPM(固件模拟的软件模块)
转:Bitlocker.TPM和系统安全 自从微软在Windows Vista首次引入Bitlocker以来,它已经越来越多的出现在我们的周围.尤其是企业用户,Bitlocker的保护已经变得不可缺少 ...
- java中异或加密
static String simple_xor(String base_data, String encrypt_key) throws UnsupportedEncodingException { ...
- PowerBuilder -- 调试(Debug)
@.进入代码调试,执行下一步直接就退出了调试 原文:http://bbs.csdn.net/topics/390126005 处理方法:尝试把 Watch 中查看的变量全部删掉.
- 【转】cmd chcp命令切换字符格式
cmd chcp命令切换字符格式 命令介绍: chcp 65001 #换成utf-8代码页 chcp 936 #换成默认的gbk chcp 437 #美国英 ...
- 一步一步学ios UITextView(多行文本框)控件的用法详解(五5.8)
本文转载至 http://wuchaorang.2008.blog.163.com/blog/static/48891852201232014813990/ 1.创建并初始化 创建UIText ...
- 配置springMVC时出现的问题
配置springMVC时出现的问题 项目结构如图: