遇到的问题
一、计算机是如何工作的?
现代计算机大都采用冯诺依曼结构,冯诺依曼结构是一种将程序指令存储器和数据存储器合并在一起的存储器结构。程序指令存储地址和数据存储地址指向同一个存储器的不同物理位置,所以计算机在运行的时候需要从中将数据取出,然后用程序进行处理,最后得到输出。我们以实验中简单的C语言程序作为例子讨论计算机如何工作:
int g(int x)
{
return x + 3;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(8) + 1;
}
将.c文件汇编为.s文件:gcc –S –o main.s main.c -m32
,把前面是“.”的代码行删去,结合教材和云班课视频对汇编代码进行分析,充分体会计算机的工作原理,查看的main.s文件内容如下:
g:
pushl %ebp // 压栈
movl %esp,%ebp
movl 8(%ebo),%eax // 将[ebp+8]地址里的数据传入eax中,即取出输入参数
addl $3,%eax
popl %ebp // 出栈
ret
f:
pushl %ebp
movl %esp,%ebp
subl $4,%esp
movl 8(%ebp),%eax
movl %eax,(%esp)
call g
leave
main:
pushl %ebp // 帧指针入栈,便于以后恢复(此帧指针为调用main函数的帧指针)
movl %esp,%ebp // 设置当前的帧指针(始终指向栈底,便于对参数之类的进行寻址及以后的恢复)
subl $4,%esp // 将栈指针-4
movl $8,(%esp) // 传递参数8
call f // 调用函数f
addl $1,%eax // 将eax的值+1并赋给eax
leave // 相当于 movl %ebp, %esp; popl %ebp
ret // 返回函数调用的下一句
堆栈的变化过程如图所示:
二、遇到的具体问题
1、关于堆栈的问题:对计算机中的堆栈的知识感到陌生,这一块完全需要自己去补补。
通过上网查询资料,了解到了,栈的生长跟内存地址的生长刚好相反,栈是由内存高地址向内存低地址的方向生长的,所以每进行一个入栈操作的时候,栈顶指针所指向的内存地址自减4(为什么是自减4呢?因为一个内存单元是四个字节,所以当进行一个压栈操作的时候,就把指针自减4个字节,然后再把数据放进去)。
2、在学习过程中曾感到困惑的一个地方是,指令中逗号前面%esp和(%esp)的区别可以理解,但为什么逗号后面仍然有时用%esp有时用(%esp)呢?
通过反复观察,发现最本质的还是:%esp表示地址,(%esp)表示地址对应的存储单元上存放的数据。
如movl %ebp, %esp是让栈顶指针esp指向的存储单元改变为栈底指针ebp指向的存储单元;而movl %ebp, (%esp)是让栈顶指针esp指向的存储单元上存放的数据改变为栈底指针ebp所指的存储单元地址。
3、变址寻址movl 8(%ebp), %eax中ebp寄存器存放的数值有没有变化?
经搜索,ebp寄存器存放的数值没有变化。
三、本章知识总结
- 冯·诺依曼体系结构的要点:
- 计算机硬件由运算器、存储器、控制器、输入设备和输出设备五大基本组件构成;
- 计算机内部采用二进制来表示指令和数据;
- 将编好的程序和原始数据事先存入存储器中,然后再启动计算机工作,这就是存储程序的基本含义。
- 中央处理器(CPU)由运算器、控制器和一些特殊寄存器组成,与内存和I/O设备进行交互,它们之间通过总线连接,内存中存放指令和数据,CPU负责解释和执行这些指令,这也是计算机可以自动化执行程序的原理。
- 几个经常用到的寄存器:
AX ( Accumulator ) :累加寄存器
BP ( Base Pointer ) :基指针寄存器,指向栈底,此处指相对栈底
SP ( Stack Pointer ) :堆栈指针寄存器,指向栈顶
IP ( Instruction Pointer ) :指令指针寄存器,除特殊指令外,该指针自动指向下一条指令的地址 - 五种寻址方式
寄存器寻址,如:movl %eax, %edx
立即寻址,如:movl $8, %edx 以上两种和内存没有关系
直接寻址,如:movl 0x12, %edx
间接寻址,如:movl ( %ebx ), %edx
变址寻址,如:movl 4( %ebx ), %edx - 六种常用汇编指令
- push 压栈,pushl %eax相当于
subl $4, %esp
movl %eax, (%esp)
- pop 出栈,popl %eax 相当于
movl (%esp), %eax
addl $4, %esp
- call 函数调用,call 0x1234 相当于
pushl %eip ()
movl $0x1234, %eip ()
- ret 函数返回,相当于
popl %eip (*)
- leave 撤销函数堆栈,相当于
movl %ebp, %esp
popl %ebp
- enter 建立函数堆栈,相当于
pushl %ebp
movl %esp,%ebp