Introduction
这一章主要讲解汇编语言,从汇编语言的角度看待代过程调用的执行我觉得这个对于后面学习操作系统很重要,最后讲了讲数据对齐。
prequistion
- Intel x86处理器
- 运行在台式电脑,笔记本电脑,服务器
- 指令集分为CISC,RISC
intel x86处理器历史
- 8086 第一个16位intel处理器,地址空间1M[1978]
- 386 32位处理器,叫IA32,加入平坦寻址(flat addressing),可以在unix操作系统运行【1985】
- Pentium 4F 第一个64位处理器,x86-64【2004】
- core 2 第一个多核处理器【2006】
- core i7 4核【2008】
- haswell GPU【2013】
从下面这个图看出,intel处理器从16位到64位,2004年出现多核处理器,因为单核处理器性能提高已经很难了。晶体管集成度提高很大从29k到1.4B
汇编程序员的view
- PC:程序计数器(program counter)
- 存放下一条指令的地址
- 调用eip(IA32)或者rip(x86-64)
- 寄存器文件(register file)
- 存放需要被频繁使用的数据和程序
- 状态码(conditon codes)
- 存放最近算数操作的状态信息
- 用于条件分支
存储器
- 相当于大的可以寻址的数组
- 用来代码和用户数据
- 为程序维持一个栈[程序栈]
C语言到目标代码的过程
文件 p1.c,p2.c,使用如下命令执行,二进制代码放到文件p中
gcc -O1 p1.c p2.c -o p
执行过程如下图
- 汇编器(assembler)执行一下几步
1. 把.s程序翻译为.o程序
2. 把每条指令进行二进制译码
3. 几乎是可执行代码一个映像,就是还没有使用链接库
4. 不同的文件之间缺少链接包
- Linker
1. 解析不同文件之间的关系
2. 结合静态链接库,比如代码中有malloc,printf
3. 链接动态链接库【链接在代码执行的时候进行】
c程序(p1.c,p2.c)通过编译为汇编程序(p1.s,p2.s),通过汇编器编译为2进制目标程序(p1.o,p2,o),再通过连接器和库编译为2进制可执行程序p.下面一步步动手操作一下实际步骤.
- Step1 将c程序编译为汇编程序
执行gcc -O1 -S code.c
- 汇编语言特征1 数据类型
- 整数 1,2,4字节 即立即数和地址
- 浮点指针4,8,10个字节
- 汇编语言特征2 操作数
数据之间的传递在寄存器和存储器之间
- 把数据从存储器加载到寄存器
- 把寄存器中的值放到存储器中
汇编语言特征3 转移控制
- 非条件转移
- 条件分支
机器指令分析
int t = x + y // C代码,两个有符号整数相加
addl 8(%ebp),%eax //汇编代码,两个四位的数相加
x:寄存器 %eax
y:存储器 M[%ebp+8]
t:寄存器 %eax
上面的代码可以这样理解
x += y
更进一步
int eax
int *ebp
eax +=ebp[2]
IA32
基本概念
- 程序计数器
指示下一条指令在存储器中的位置(在IA32中通常称为”PC”,用%eip表示) - 整数寄存器文件
8个寄存器,用来存放整数数据和指针,下图所示8个寄存器,前6个可以用作通用寄存器%esp作为栈顶指针,%ebp作为栈底指针