C#是怎么跑起来的
解释流程前,需要了解一些基本的概念。
基本概念解释:
CPU :中央处理器,计算机的大脑,内部由数百万至数亿个晶体管组成,是解释和运行最终转换成机器语言(二进制代码)的地方。机器语言是通过CPU内存的寄存器来处理的,不同的类型的CPU,其内部的寄存器的数量、种类以及寄存器存储的数值范围都是不一样的。根据功能的不同,大致分为8类:
对于程序员来说,CPU是具有各种功能的寄存器的集合体,其中,程序计数器、累加寄存器、标志寄存器、指令寄存器和栈寄存器都只有一个,其他的寄存器一般有多个。
需要注意的是CPU的种类是特别重要的参数,CPU只能解释其自身固有的机器语言(指令集),不同的CPU能解释的机器语言的种类也是不同的。如CPU的主要厂商是Intel 和 AMD,前者基于x86指令集,后者基于ARM指令集,其中的区别大家有兴趣可以移驾:https://zhuanlan.zhihu.com/p/95028674
同时要买CPU可以参照核心/线程,主频,多级缓存等参数来选择。
内存:计算机的主存储器(主存),负责存储指令和数据,通过控制芯片等与CPU相连,由可读写的元素构成,每个字节都带有一个地址的编号,CPU可以通过该地址读取主存中的指令和数据,也可以写入数据。指令和数据是有时效性的,会随着计算机的关机而自动清除。
操作系统:程序员并不需要编写操作指令来完成计算机硬件的相关操作,比如键盘鼠标显示器的输入输出等等,也不需要关系内存和IO的不同的构造,因为这些都是通过操作系统的指令来完成的,操作系统克服了除了CPU以外的其他相关硬件的差异,一种操作系统只能支持特定的cpu(比如windows操作系统主流版本支持x86架构的CPU,ARM架构适合嵌入式的liunx操作系统,liunx一般也是用x86),同时操作系统需要为不同的机型分别提供不同的版本。同样的机型,也可以安装不同的操作系统,而应用软件则必须根据不同的操作系统类型来专门开发,因为操作系统的类型不同,应用程序向操作系统传递的指令途径(API)是不同的。以下列举windows对程序员有意义的一些特征:
- 32/64位版本操作系统,数字表示处理效率最高的数据大小
- 通过API函数集来提供系统调用,API通过各个dll文件来提供,各个API的实体都是C语言编写的函数
- 提供采用了图形用户界面的用户界面
- 通过WYSIWYG实现打印输出
- 提供多任务功能,通过时钟分割技术来实现多任务功能,就是多个应用程序“同时运行”(短时间间隔内多个程序切换运行)
- 提供网络功能及数据库功能
- 通过即插即用实现设备驱动的自动设定
运行环境:操作系统 + 硬件。每次下载一个软件的时候,都会让你选择基于什么的运行环境(操作系统+硬件:如基于windows7 版本以上,需要1G以上内存,500M以上的磁盘空间)来下载不同的安装包,代码需要在特定的运行环境中才能执行。
代码运行的流程:
C#是一门高级编程语言,作用是编写程序,而程序是指示计算机每一步动作的一组指令,由指令和数据构成(如:WriteLine(“hello”) 程序中,WriteLine 是指令,”hello”是数据),需要转换成能够被CPU可以直接识别并使用的机器语言(二进制码)。程序(代码)是存放在硬盘和磁盘等媒介上的,而被CPU执行的程序需要在内存中存储(从磁盘中复制到内存)。而内存是保存命令和数据的场所,通过地址来标记和指定。
用编程语言写好的程序,是需要运行环境才能跑起来,运行环境需要操作系统和计算机硬件支持,如果运行环境不同,程序是无法运行的,例如,MacOs,windows,linux系统上的运行的应用程序基本上不能拿在另一个操作系统中运行的。因为不同的操作系统提供出来的API是有差异的,因此,将同样的代码移植到其它的操作系统中,就必须重写应用中利用API的部分,像键盘的输入,鼠标的输入,显示器的输出,文件的输入和输出等外围设备进行输入输出的功能,都是操作系统提供出API供程序员调用。同类型的操作系统下,不管硬件如何,API基本上没有差别。因而,针对某特定操作系统的API所编写的程序,在任何硬件上都可以运行。当然,由于CPU种类不同,机器语言也不相同,因此本地代码当然也是不同的。这种情况下,就需要利用能够生成各CPU专用的本地代码的编译器,来对源代码进行重新编译了。那么有没有方法可以让程序员写的代码,在不同环境下执行呢,经验告诉你肯定是可以的,实现原理是只需要根据不同的环境开发出不同的CLR运行时就可以实现跨平台,像java语言,对应就有不同环境的Java虚拟机JVM。
C#写的代码是怎么样变成CPU可以执行的机器码呢,流程图如下:
-
C# 源代码文件经过编译器生成程序集文件,文件包含了以下内容
- 标准的Windows PE32或者PE32+头,能在windows的32位或者64位版本上运行,如果是PE32+的文件,只能在64位版本上运行
- CLR头。包含使这个模块称为托管模块的信息,头部包含要求的CLR版本,一些标识flag,托管模块入口方法等等
- 元数据,包含2种元数据表,一种描述源代码中定义的类型和成员,另一种描述源代码引用的类型和成员。
- IL中间语言代码,编译器编译源代码生成的代码。
微软创建了好几个面向“运行时”的语言编译器,能够将 C++/CLI,C#,Visual Basic ,F#等语言编译成供CLR运行的IL语言,承载了多语言的特点,后面可以只开发出面向CLR编程的高级语言。
其中:C#源码编译为程序集的过程为编译时,程序集被JIT编译器编译为本地代码,被操作系统调度CPU所执行的过程被称为运行时。 -
CLR 中的JIT编译器将IL编译成该平台下的CPU指令
.NET 平台下面,微软官方只开发了Windows下面运行的CLR,没有开发其他平台的CLR,所以不能做到跨平台,但是在非官方的渠道下,Xamarin公司为了能把.Net做成跨平台,开发了mono虚拟机,虚拟机包含了一个实时编译引擎,该引擎可用于x86, SPARC, PowerPC, ARM等处理器,后被微软收购。后来微软由于战略的更改,拥抱开源和跨平台,重新开发和定义了.Net FrameWork的新一代版本—DotNetCore。如果需要将Core代码在各个平台上运行(跨平台),需要在官网上下载不同环境的运行时,如下图中的3个平台的运行时:
附java跨平台的实现:
CLR是.NET的核心组件,它在操作系统的顶层,负责管理程序的执行,功能如下: