《深入理解计算机系统》学习(3):程序编译和处理器
编译过程
假设一个C程序有两个文件p1.c、p2.c,我们用Unix命令行编译这些代码:
linux>gcc -o p p1.c p2.c
gcc命令指的就是GCC C编译器,因为这是Linux上默认的编译器,gcc命令调用了一整套程序,将源代码转化为可执行代码。
- 首先,C预处理器扩展源代码,插入所有用
#include
命令指定的文件,并拓展所有用#define
申明指定的宏。 - 其次,编译器产生两个源文件的汇编代码,名字分别为p1.s和p2.s。
- 接下来,汇编器会将汇编代码转化为二进制目标代码文件p1.o、p2.o。目标代码是机器代码的一种形式,包含所有指令的二进制表示,但是还没有填入全局值的地址。
- 最后,连接器将两个目标文件与实现库函数的代码合并,并产生最终的可执行代码文件p。可执行代码是机器代码的第二种形式,也就是处理器执行的代码格式。
在整个编译系统中,编译器会完成大部分的工作,将把用C语言提供的相对比较抽象的执行模型表示的程序转化为处理器执行的非常基本的指令。汇编代码表示非常接近机器代码。与机器代码的二进制格式相比,汇编代码使用文本格式表示,可读性更好,更容易理解汇编代码和原始C代码的联系。
虽然C语言提供了一种模型,可以在内存中声明和分配各种类型的对象,但是机器代码只是简单地将内存看作一个很大的、按字节寻址的数组,汇编代码不区分有符号或无符号整数,不区分各种类型的指针,甚至于不区分指针和整数。
计算机执行机器代码,用字节序列编码低级的操作,包括处理数据、管理内存、读写存储设备上的数据,以及利用网络通信。编译器基于编程语言的规则、目标机器的指令集和操作系统遵循的惯例,经过一系列的阶段生成机器代码。
处理器
处理器即CPU,Central Processing Unit。一般家用电脑的主板上只有一个CPU,在服务器上需要多个CPU。一个CPU由控制器、运算器、寄存器和缓存等物理单元组成。
随着多核技术的发展,CPU的概念转变为了一个容器,而CPU中的core才是真正的物理处理单元。core是相互独立的,都有自己单独的控制器、运算器、寄存器和缓存等物理单元。
同时多线程技术和超线程技术可以在一个物理核心core上同时运行两个线程,但这两个线程共享核心的资源。
Intel处理器系列俗称x86,x86一开始是第一代单芯片、16位微处理器之一。
一些Intel微处理器的模型如下:
- 8086(1978年,29k个晶体管):第一代单芯片、16位微处理器之一。
- i386(1985年,275k个晶体管):将体系结构扩展到32位,这是Intel系列中第一台全面支持Unix操作系统的机器。
- Core 2(2006年,291M个晶体管):Intel第一个多核微处理器,多核在一个芯片上。
- Core i7(2008年,781M个晶体管):最初的版本支持每个核上执行两个程序,每个芯片上最多四个核。
Intel处理器系列有好几个名字,包括IA32,也就是“Intel 32位体系结构”,以及最新的Intel64,即IA32的64位拓展,也称为x86-64。最常用的名字是“x86”,代指整个系列,也反映了直到i486处理器命名的惯例。
机器级编程的两种抽象
计算机系统使用多种形式的抽象,利用更简单的抽象模型来隐藏实现的细节。对于机器级编程来说,两种抽象尤为重要。
- 第一种是指令集架构(比如x86-64)来定义机器级程序的格式和行为,定义了处理器的状态、指令的格式、以及每条指令对状态的影响(指令集决定了处理器的架构,在处理器基础上决定了操作系统和编译器的实现)。
- 第二种抽象是机器级程序使用的内存地址是虚拟地址(堆、栈等),提供的内存模型看上去是一个非常大的字节数组。存储器系统实际上是将多个硬件存储器和操作系统软件组合起来。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!