[音乐] 下面我们用一个例子来说明前面那个过程, 比如说有两个源程序文件 main.c 和 test.c, 最终呢生成一个可执行文件 test,我们可以用这样 一个命令,在这个命令里面指出两个源程序文件点c文件。 然后输出是 test, 可执行文件是 test. 那么这个 -O 又表示输出文件名, test 是输出文件名。 这个大 O, 是表示优化级别,大 O 后面是 1 表示进行一级优化,2 呢就表示二级优化。 是这样的,假定这个 test.c 里面这样的一个函数。 这个函数我们可以通过相应的命令 把它转换成这个高级语言的源程序,转换成这个 汇编指令构成的汇编语言程序, 可以用这两个命令先通过 -E这个命令 来生产预处理结构,然后对预处理结果再进行 编译生成汇编也可以用这个一条命令直接对 .c 文件 生产汇编指令过程的汇编语言程序。 这两个都是一样的,那么这个是生成的这个函数对应的汇编语言程序, 当然我们还可以进一步的对这个汇编语言程序 通过汇编命令就是 -c 这个命令生成可重定位的 目标代码,可重定位的目标代码。 然后这个可重定位的目标代码是一个二进制文件。 因此我们看它的话,必须用相应的工具来看, 进行反汇编来看,因为它是二进制 0/1 序列,已经是一个用机器指令表示的一个文件了。 那么这样的话我们要看 0/1 序列我们是看不出来是什么指令,所以我们要进行反汇编, 这个反汇编我们可以用相应的这个工具,比如说 objdump -d 这样一个选项,这样得到的是反汇编的一个结果。 在这个反汇编的结果里面,最前面的这边是位移量就是表示这个指令这是一条一条的 机器指令,后面给出来的是汇编指令,这是一一对应的,这些指令是 add 这个 函数里面的一条一条指令,这个函数在这个 test.o 里面 它的起始地址总是从零开始,因为它还没有 链接,还没生成可执行文件,所以它的地址是不确定的,所以 总是从 0 开始,每条指令的位移量相对于这个起始地址0 的位移量在最左边显示出来。 中间这个呢是机器指令,右边这个就是汇编指令。 那么这个就是反汇编的结果, 我们可以看到这是编译得到的 汇编指令,但是反汇编得到的汇编指令 它们在形式上有一点差别, 编译得到的汇编指令它的这个助记符当中都带长度后缀, 而反汇编的都没有,然后呢,这个地方的 常数用的是十六进制,这个给的直接是十进制,就是 十六,十六就等于十六进制的10。 这边是 c 这边是 12,其他的是一样的,稍稍有一点不一样。 好,我们来看看两种目标文件。 刚才我们看到的是 test.o,它是一种可重定位的目标文件。 最终生成的可执行目标文件在linux 里面它是没有 后缀的。 就是直接是 test, 我们可以取个名字,直接就是 test 的话我们可以 dump 出来它们的结果,因为这两种都是二进制表示的文件。 通过这个命令反汇编出来的结果,刚才我们已经看到了,是这样子的。 它的这个位移量是从0开始的, 如果我们对可执行目标文件也用相应的 命令把它反汇编出来,后面的二进制的代码 和汇编指令这边都是 一样的。 不同的只是地址不一样,每条指令的地址不一样。 这个可执行文件当中的地址是一个确切的值。 比如说它是从 add这个函数的第一条指令, 开始指令是从 80483d4, 从这个地方开始的,它是一个确切的指令, 而可重定位目标文件里面是从零开始。 也就是说这个已经是链接以后的, 然后这个确切的地址是什么地址呢?实际上是一个虚拟地址,它是在一个虚拟地址空间里面的。 后面我们专门讲链接的时候会详细的介绍。 我们可以看到在这个可执行文件里面这些代码 它的地址是确切什么地址呢,是这个虚拟地址空间里面的地址, 比如说我们刚才看到的 80483d4 这条指令的地址,然后 3d5 就是下面这条指令的地址,实际上就是在这个虚拟空间里面的 8048 3d4 就在这个位置,3d5 在这个位置,所以 add 那个函数就在这个位置 所以可执行文件当中每条指令的 地址都已经在这个虚拟地址空间里面进行了定位了, 因此把这个可执行文件装入到内存进行运算的时候, 它这个装入的过程在这一部分实际上是装入到 这个地址空间的这个地方,也就是实际上是一种映射,后面我们讲链接的 时候还会讲到,数据区呢是映射到虚拟地址空间当中的这个 段的,这些都是不同的 segment, 因此这一块是从可执行文件装入的 其他的还有堆区,共享库区以及栈区等等相关的 一些概念,我们后面会陆陆续续的介绍,现在只要了解个大概就可以了。 在第一周的时候我们还介绍了指令集体系结构 ISA, 指令集体系结构它是位于软件和硬件交界面上的, 它是一种规约,它规定了软件如何使用硬件, 所谓使用硬件,也就是这个硬件提供的功能 怎么被软件用,硬件的所有的功能实际上是被抽象成 指令,然后软件就是一条一条指令构成的, 通过执行一条条指令来完成软件的功能,来使用硬件。 所以ISA它实际上是规定了 计算机硬件可以执行的指令的集合, 到底这台机器有多少条指令可以被软件用呢? 而且每一条指令的格式是什么样子的?所有的这些指令 一共有多少种操作类型,比如说有没有乘法操作啊,以及 每一种操作对应操作数的相应的规定,这条指令的操作数 是带符号的无符号的,还是浮点操作数啊, IEEE 的这种格式啊,以及每条指令可以接受的数据类型。 还有操作数所能存放的寄存器组的结构,操作数可以放在存储器里面。 也可以放在寄存器里面,那么这个寄存器到底有多少个啊,每个寄存器 的编号是什么,名称是什么?寄存器有多长,这个寄存器是专门用来干什么的? 然后操作数还可以放在存储器里面,那么这个存储空间到底有多少个单元? 每个单元里面到底可以放多少位?这是编址方式, 以及操作数在存储空间当中存放的时候是按大端方式还是按小端方式存放。 指令获取操作数的这个方式应该是什么样子的,也就是这个寻址方式应该是什么样子的。 指令执行过程当中怎么样得到下一条指令的地址啊。 以及要进行转移的时候怎么样控制转移等等等等。 这些都是 指令集体系结构规定的,也就是 ISA 规定的,这个我们也简称叫指令系统。 因此我们可以看出 ISA 在计算机系统当中 它是一个必不可少的抽象层,它实际上 是我们刚才讲过它是对硬件的一种抽象。 为什么说它必不可少呢?因为没有它软件是无法使用计算机硬件的 没有指令我怎么用硬件呢,然后没有它 一台计算机也不能成为通用计算机, 因为有了指令我们可以对指令进行各种组合,组合成不同功能的程序。 因此我们说这个计算机它是通用的,它可以干很多不同的事情。 这个不同的事情用不同的指令序列来描述它 因此我们有了指令以后,我们才可以进行编程,进行程序设计,才能设计出 不同的功能的程序,才是一种通用,因此ISA是非常非常重要的 ISA也就是指令集体系结构,和计算机组成,也就是计算机的底层的结构 我们也称为微体系结构,是什么关系呢?这个是计算机的基本的微体系结构 在这样的一个微结构当中,比如说 我提供一套指令系统,不同的ISA 它规定的指令级是可以不一样的,比如说Intel的这个架构,有Intel的指令系统 MIPS的,ARM的,PowerPC的等等,每个不同的 厂商的这些处理器,都有不同的指令集 然后计算机组成,也就是这个微体系结构,必须能够实现 指令集规定的那些功能,那么你要能够实现 规定的功能,你就必须提供相应的,比如说通用寄存器组 因为这个指令里面已经规定了,我用这个寄存器,也就是 规定了我这个厨房有多少个盘子,盘子大小有多少 那么你就必须给这个厨房配置这样的盘子 也就是配置相应的通用寄存器,然后我这个指令系统当中规定了,我要用到哪些标志 那我这个里面就要配置相应的标志寄存器里面 加入一些位,还有相应的运算电路,比如说这个指令系统当中 提供了乘法指令,那我就要配置相应的乘法器,或者相应的 乘法运算电路,那么因此指令集体系结构和计算机组成 它们之间的关系是非常密切的的。 同一种ISA有不同的计算机组成 就是说,同一套指令系统,我可以用不同的底层的微结构来实现 这个一些相互的关系,因为你们没有任何背景知识,现在 听,可能不是很明白,当你们学了后续很多内容之后 会慢慢的体会出,它们之间的一些关系 所以ISA是计算机组成的一种抽象,我们刚才讲过了,指令实际上是计算机硬件 和计算机组成的一种抽象,Intel32位架构的体系结构 是怎样的呢?比如说IA-32当中到底有多少个寄存器呢这个地方? 到底有多少个寄存器呢?寄存器的宽度到底有多宽呢? 存储空间,这个存储空间到底有多大呢?这个编号的时候,这个编址的单位 是按字节编址的还是按16位这样的一个字来编的呢?指令的格式是什么样子的? 可变长指令还是不可变长指令?一共提供多少条 指令?指令的操作功能到底提供了哪些操作? 寻址方式什么样的?数据类型是大端小端?这个标志寄存器每一位的含义是什么? 指令的地址到底用多少位来表示?I/O端口的编址方式等等等等 这些是什么样子的,那么从下一节课呢我们就来介绍这个IA-32的指令系统 在刚刚我们讲的这一节课里面,我们主要介绍了这个高级语言程序 大概是怎么样转换成机器级代码的这个过程 二进制的代码我们是看不见摸不着,但是我们可以用工具来进行反汇编 出每条指令的地址啊,每条指令的二进制代码是什么样子的 每条指令的汇编形式是什么样子的,然后在这个里面 实际上我们讲这个转换过程当中一个最最重要的一个 环节是指令集体系结构,也就说我们最终转换成的这个 二进制代码表示的这个指令,它一定是满足或者符合这个指令集体系结构 规定的一些内容的,那么也就是ISA规定了一台机器的 指令系统涉及到的所有的方面,我们前面介绍过的这些方面 那么对于IA-32这样的体系结构,它的这些内容是什么样子的,这是我们下堂课 的主要内容 [音乐]