CLR via C#-CLR的执行模型
admin
2024-03-19 20:16:00
0

本章目的:
如何把源代码生成为一个应用程序并且运行?


1.将源代码编译成托管代码
CLR(Common Language Runtime):可由多种编程语言使用的运行时
核心功能:内存管理、程序集加载、安全性、异常处理、线程同步
就是说:不关心语言是啥,都可以编译成某种格式运行到CLR上
(包括:C++、c#、VB、F#、等等。。。。)
这个格式叫做 托管模块(managed module),包括中间语言和元数据
托管模块的组成:
a.PE32 或 PE32+ 头:区分多少位,功能是标识文件类型以及生成时间
b.CLR头:CLR版本,标识,托管模块入口 Main 方法的 MethodDef 元数据,以及模块的元数据、资源、强名称、一些标识以及其他不太重要的数据项的位置/大小
c.元数据:两种表,一种:源代码中定位的类型和成员,另一种:描述源代码引用的类型和成员
(智能感知系统用,比如 Rider 或者 VS 代码编辑器方便写代码)
d.IL代码:编译源代码生成的代码,运行时 CLR 将 IL 编译成本机 CPU 指令

2.将托管模块合并成程序集
CLR 不和模块工作,和程序集(Assembly)工作
程序集有一个主要的清单(manifest)数据块,也是元数据表的集合
作用:描述了构成程序的文件、公开类型、以及与程序集关联的资源或者数据文件
比如说C#,通过c#编辑器(CSC.exe)可以将多个托管模块和资源文件合并成一个程序集
程序集可以事:可执行的应用程序 或者 DLL

3.加载公共语言运行时
CLR 管理程序集中的代码执行,要求目标及其必须装好 .Net Framework
Windows有32位和94位的版本,可以通过 -platform 命令控制编译平台
PE32/x86、PE32+/x64
Windows检查EXE文件头,决定创建32位还是64位进程,在进程地址空间加载 MSCorEE.dll 的不同版本
这个 DLL 里面会先调用初始化方法,会初始化 CLR ,然后调用入口 Main 方法
至此,程序启动并运行

4.执行程序集的代码
托管程序及包括元数据和 IL
IL 是和CPU无关的机器语言,可以提供指令创建和初始化对象、调用对象上的虚方法以及直接操作数据元素
也可以抛出和捕捉异常的指令实现错误处理
IL 就是面向对象的机器的语言
IL 基于栈,所有指令都要将操作数 push 执行栈,并从栈弹出 pop 结果
用 C# 等高级语言进行编程,编译器最后都会生成 IL(也可以用汇编去写)
微软有 ILAsm.exe 的IL汇编器,和 ILDasm.exe 的反汇编器
那如何执行 IL 里面的代码呢?
首先需要把 IL 转换成本机 CPU 指令,这是 JIT (just-int-time)即时编辑器的职责

比如说要执行下面的方法

static void Main()
{Console.WriteLine("Hello");Console.WriteLine("World");
}

在 Main 执行之前,CLR 会检测出 Main 的代码引用的所有类型,需要分配一个内部数据结构来管理对引用类型的访问
比如说这里面 引用了 Console 类型,CLR 会产生一个 Console 结构,Console 的每个方法都有一个 Entry ,每个 Entry 都有一个地址,根据地址可以找到方法的实现
CLR 将每个 Entry 都指向内部的一个未编档的函数, 也叫 JITCompiler (即时编辑器)
当首次调用 WriteLine 方法时,JITCompiler 函数会被调用,负责将方法的 IL 代码编译成本机的 CPU 指令
过程如下:
a.负责实现 Console 的程序集的元数据查找被调用的方法 WriteLine
b.从元数据中获取该方法的 IL
c.分配内存块
d.将 IL 编译成本机的 CPU 指令,将本地代码保存到刚才分配的内存中
e.在 Console 表中修改 Entr 的地址,让他指向 刚才分配的内存块
f.跳转到内存中的本地代码
当第二次调用 WriteLine 方法时,直接执行内存块中的代码,并不会经过 JITCompiler 函数

方法仅在首次调用时才会有一些性能损失,以后对该方法的所有调用都是以本机代码的形式全速运行
JIT 编辑器只会讲本机 CPU 指令存到动态内存中。程序一旦停止,编译好的代码就会被丢弃,再次运行,还会重新编译

编译器可以优化对本地代码进行一定优化
通过 -optimize 指令
未优化的 IL 代码会有许多 NOP(no-operation)空操作指令,还包含许多跳转到下一行代码的分支指令
在 VS 里面可以用这些指令调试,或者在 for、while、if 等流程指令上加断点
通过 -debug 指令
如果是 debug 会生成 Program Database(PDB)文件,帮助 VS 调试器查找局部变量并将 IL 指令映射到源代码

PS:
Windows每个进程都有独立的虚拟地址空间。防止应用程序读写无效的内存空间,不会干扰到另一个程序的代码
CLR 提供了在操作系统进程中执行多个托管应用程序的能力。每个托管引用程序都在一个 AppDomain(应用程序域)执行的

c# 编辑器默认生成安全代码,可以验证安全性。也可以写不安全代码(unsafe)
一般只有在对效率比较高的性能算法时,才需要这么做
微软提供了 PEVerify.exe 的程序,可以检查程序集的所有方法,报告其中是否有不安全代码

使用 NGen.exe 工具,可以提前将IL代码编译成本机代码,可以提高程序的性能
但是也有额外的问题,可以反编译,可能失去同步等


额外的 CLR 知识

Framework类库
.Net Framework 包括 Framework 类库(Framework calss library,FCL)
是一组 DLL 程序集的统称,包括了很多功能,可以利用这些创建应用程序
a.Web Service
b.基于 HTML 的 Web 窗体/ MVC 应用层序
c.Windows GUI 应用
d.Windows 控制台应用
e.Windows 服务
f.数据库存储过程
g.组件库

常用的 FCL 组件库命名空间
System、System.Data、System.IO、System.Net、System.Runtime.InteropSercivces、System.Security、System.Text、System.Threading、System.XML

通用类型系统
CLR 一切都围绕类型展开,微软注定了一个正式的规范来描述类型的定义和行为
通用类型系统(Common Type System,CTS)
包括:Field、Method、Property、Event
CTS也制定了类型可见性规则以及类型成员的访问规则
包括:private、protected、internal、protected internal、public

所有类型必须从预定义的 System.Object 类型继承
有一组最基本的功能
a.Equals(比较两个实例的相等性)
b.GetHashCode(获取实例的哈希码)
c.GetType(查询实例的真正类型)
d.MemberwiseClone(执行实例的按位拷贝,值类型是拷贝,引用类型是引用对象地址)
e.ToString(获取实例对象当前状态的字符串表示)

公共语言规范
公共语言规范(Common Language Specfication,CLS),详细定义了一个最小功能集
任何编译器只有支持这个功能集,生成的类型才能兼容由其他符合 CLS、面向 CLR 语言生成的组件

编程语言的各类构造和 CLR 的字段/方法有对应关系,可以通过 ILDasm.exe 反编译器查看

与非托管代码的互操作性
a.托管代码能调用 DLL 中的非托管函数
b.托管公司可以使用现有 COM 组件
c.费托管代码可使用托管类型

相关内容

热门资讯

【MySQL】锁 锁 文章目录锁全局锁表级锁表锁元数据锁(MDL)意向锁AUTO-INC锁...
【内网安全】 隧道搭建穿透上线... 文章目录内网穿透-Ngrok-入门-上线1、服务端配置:2、客户端连接服务端ÿ...
GCN的几种模型复现笔记 引言 本篇笔记紧接上文,主要是上一篇看写了快2w字,再去接入代码感觉有点...
数据分页展示逻辑 import java.util.Arrays;import java.util.List;impo...
Redis为什么选择单线程?R... 目录专栏导读一、Redis版本迭代二、Redis4.0之前为什么一直采用单线程?三、R...
【已解决】ERROR: Cou... 正确指令: pip install pyyaml
关于测试,我发现了哪些新大陆 关于测试 平常也只是听说过一些关于测试的术语,但并没有使用过测试工具。偶然看到编程老师...
Lock 接口解读 前置知识点Synchronized synchronized 是 Java 中的关键字,...
Win7 专业版安装中文包、汉... 参考资料:http://www.metsky.com/archives/350.htm...
3 ROS1通讯编程提高(1) 3 ROS1通讯编程提高3.1 使用VS Code编译ROS13.1.1 VS Code的安装和配置...
大模型未来趋势 大模型是人工智能领域的重要发展趋势之一,未来有着广阔的应用前景和发展空间。以下是大模型未来的趋势和展...
python实战应用讲解-【n... 目录 如何在Python中计算残余的平方和 方法1:使用其Base公式 方法2:使用statsmod...
学习u-boot 需要了解的m... 一、常用函数 1. origin 函数 origin 函数的返回值就是变量来源。使用格式如下...
常用python爬虫库介绍与简... 通用 urllib -网络库(stdlib)。 requests -网络库。 grab – 网络库&...
药品批准文号查询|药融云-中国... 药品批文是国家食品药品监督管理局(NMPA)对药品的审评和批准的证明文件...
【2023-03-22】SRS... 【2023-03-22】SRS推流搭配FFmpeg实现目标检测 说明: 外侧测试使用SRS播放器测...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
初级算法-哈希表 主要记录算法和数据结构学习笔记,新的一年更上一层楼! 初级算法-哈希表...
进程间通信【Linux】 1. 进程间通信 1.1 什么是进程间通信 在 Linux 系统中,进程间通信...
【Docker】P3 Dock... Docker数据卷、宿主机与挂载数据卷的概念及作用挂载宿主机配置数据卷挂载操作示例一个容器挂载多个目...