深入理解计算机系统之程序的机器级表示部分学习笔记
Posted 辛侠平
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解计算机系统之程序的机器级表示部分学习笔记相关的知识,希望对你有一定的参考价值。
不论我们是在用C语言还是用JAVA或是其他的语言编程时,我们会被屏蔽了程序的机器级的实现。机器语言不需要被编译,可以直接被CPU执行,其执行速度十分 快。但是机器语言的读写性与移植性较高级语言低。高级语言被编译后便成为了汇编语言,汇编语言十分接近机器语言。之后汇编代码会转化为机器语言。虽然现代 的编译器能帮助我们将高级语言转化为汇编语言,解决了不少问题,但是对于一个严谨的程序员来说,需要做到能够阅读和理解汇编语言。我们主要围绕Intel来讲 解。
一 Intel处理器的历史演变
Intel处理器最的早是8086, 它是十六位的微处理器,作为第一代单芯片,8086知名度是相当的高。之后又有80286、i386、i486、Pentium、PentiumPro、 Pentium/MMX、PentiumⅡ、PentiumIII等等的一系列处理器出现了。每个时间上相继的处理器都是向后兼容的。Intel称其指令集为IA32,也就是Intel32位体系结 构,也就是我们平常所说的x86。
二 程序编码
我们写一个C程序,用Unix命令行编译p1.c和p2.c两个文件。即为
unix> gcc -02 -o p p1.c p2.c
其中-02 告诉编译器使用第二级优化。而二级优化则是在性能优化与使用方便之间一个最好的权衡。所以代码经常使用二级优化。
(1)机器级代码
一些通常对C程序进行屏蔽的处理器:
程序计数器(%eip)、整数寄存器、条件码寄存器、浮点寄存器
(2)代码示例
int accum = 0; int sum(int x, int y) { int t = x+y; accum+=t; return t; }
在命令行上使用 “-S”选项,
unix〉gcc -02 -S code.c
就能看到编译器产生的汇编代码:
sum: pushl %ebp movl %esp , %ebp movl 12(%ebp) , %eax addl 8(%ebp) , %eax addl %eax , accum movl %ebp , %esp popl %ebp ret
如果对code.c 使用“-c”选项则会产生一个二进制的文件code.o
unix〉gcc -02 -c code.c
code.o里面的是汇编指令的目标代码。
当然我们可以使用反汇编器来将目标代码转化为汇编代码:
unix〉objdump -d code.o
三 数据格式
四 访问信息
这是IA32中央处理器所包含的一组八个存储单元的32位存储器。前六个是通用寄存器,对它们的使用没有限制。前三个寄存器(%eax,%ecx,%edx)的保存和恢复惯例不同于接下来的三个寄存器(%ebx,%esi,%edi)。最后两个寄存器保存着指向程序栈重要位置的指针,称为栈指针和帧指针。数据存放在寄存器中进行加减乘除等一些操作。原来的寄存器是16位的所以如图所示蓝色部分是0-15,之后寄存器进行了扩充,变成了32位的即0-31。
(1) 操作数指示符
操作数被分成三种类型:立即数、寄存器、存储器引用。我们来看看一些不同的寻址模式
(2) 数据传输指令
我们来看看一些常见的汇编程序的数据传送指令
movb、movsbl和movzbl之间的差别如下:
假设 %dh =8D,%eax=98765432 movb %dh ,%a1 %eax=9876548D movsbl %dh , %eax %eax=FFFFFF8D movzbl %dh,%eax %eax=0000008D 在以上的例子中都是将寄存器%eax 的低位字节设置为%edx 的第二个字节。movb指令不改变其他三个字节。根据原字节的最高为,movsbl指令将其他三个字节设为全1或全0。movzbl指令无论如何都是将其他三个字节设为全0。
pushl 与popl是用来将数据压入栈中和从栈中弹出数据的。它们两个的指令都只有一个操作数,即它们所要压入或者弹出的数据。将一个双字值压入栈中,首先要将栈指针减4,然后将值写入到新的栈顶地址。弹出一个双字的操作是从栈顶位置读出数据,然后将栈指针加4。
五 算术与逻辑操作
我们来看看一些双字整数的操作
如图所示共有四种不同的操作:leal 、一元操作、二元操作、位移操作
(1) 加载有效地址
加载有效地址指令leal实际上是movl指令的变形。它的指令形式是从存储器读数据到寄存器,但实际上它根本就没有引用存储器。
(2)一元操作和二元操作
一元操作即只有一个操作数,二元操作即是有两个操作数。
(3)位移操作
位移操作,先给出位移量,然后是待移位的值。可以进行算术和逻辑右移。
六 控制
对于C和汇编代码中的语句,默认的是按照语句或指令在程序中出现的顺序来执行的。
(1)条件码
CPU包含了一组单个位的条件码寄存器,它们描述了最近的算术或逻辑操作的属性。常见的条件码为:进位标志(CF)、零标志(ZF)、符号标志(SF)、溢出标志(OF)
(2)访问条件码
两种最常用的访问的条件码的方法不是直接读取它们,而是根据条件码的某个组合,设置一个整数寄存器或是执行一条分支指令。每个指令根据条件码的某个组合,将一个字节设置为0或者1。同一个机器指令可以有不同的名字。
(3)跳转指令
跳转指令会导致执行切换到程序中的一个全新的位置。这些跳转的目的地通常用一个标号指明。jmp指令是无条件跳转的,可以直接跳转也可以间接跳转。
(4)循环
do-while、while、 for
(5)switch语句
switch语句提供了根据一个整数索引值进行多重分支的能力。在处理具有多种可能结果的测试时,特别有用。它通过使用跳转表使代码更加高效。
七 过程
一个过程调用包括将数据(以过程参数和返回值的形式)和控制从代码的一部分传递到另一部分。另外,它还必须在进入时为过程的局部变量分配空间,并在退出时释放这些空间。
(1)栈帧结构
栈帧结构指的是为单个过程分配的那部分栈。栈帧的最顶端是以两个指针定界的,寄存器%ebp作为帧指针,寄存器%esp作为栈指针。栈指针是可以移动的,所以大多数信息的访问都是相对于帧指针的 。
(2)转移控制
call :过程调用
leave:为返回准备栈
ret:从过程调用中返回
(3)递归过程
以上是关于深入理解计算机系统之程序的机器级表示部分学习笔记的主要内容,如果未能解决你的问题,请参考以下文章