一、计算机工作原理
本周实验主要是反汇编C代码,生成汇编程序。冯·诺依曼理论的要点是:数字计算机的数制采用二进制,计算机应该按照程序顺序执行。人们把冯·诺依曼的这个理论称为冯·诺依曼体系结构。CPU通过总线从内存中读取一条条程序,根据程序的内容执行具体的步骤。CPU在读取指令时,通过寄存器IP来指向下一条指令(如果是32位系统,则为EIP),CPU的寄存器分为通用寄存器、段寄存器、状态寄存器。可以用一张图来说明
进程是一个可执行程序的实例。从内核角度看,进程由用户内存空间和一系列内核数据结构组成,其中用户内存空间包含了程序代码及代码使用的变量,而内核数据结构用于维护进程状态信息。每个进程分配的内存由很多部分组成。当计算机在运行时,先从内存中取出第一条指令,通过控制器的译码,按指令的要求,从存储器中取出数据进行指定的运算和逻辑操作等加工,然后再按地址把结果送到内存中去。接下来,再取出第二条指令,在控制器的指挥下完成规定操作。依此进行下去。直至遇到停止指令。
计算机的寻址方式老师介绍了以下几种:
movl %eax,%edx edx=eax 寄存器寻址
movl $0x123,%edx edx=0x123 立即寻址
movl 0x123,%edx edx=*(int32_t*)0x123 直接寻址
movl (%ebx),%edx edx=*(int32_t*)ebx 间接寻址
movl 4(%ebx),%edx edx=*(int32_t*)(ebx+4) 变址寻址
二、实验内容
对于简单C语言程序main.c
int g(int x) { return x + 4; }
int f(int x) { return g(x); } int main(void) { return f(5) + 1; }
使用如下命令编译成汇编代码,然后删除汇编代码中用于链接的辅助信息:
gcc –S –o main.s main.c -m32
得到的文件保存在main.s中
g: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax addl $4, %eax popl %ebp ret f: pushl %ebp movl %esp, %ebp subl $4, %esp movl 8(%ebp), %eax movl %eax, (%esp) call add leave ret main: pushl %ebp movl %esp, %ebp subl $4, %esp movl $5, (%esp) call f addl $1, %eax leave ret
处理后的汇编代码为:
设空栈如下:
从main开始执行,以下为逐句解释:
最开始的时候ebp和esp都指向标号0的位置
pushl %ebp :ebp入栈,保存当前ebp0,esp-4指向标号1 movl %esp, %ebp :将ebp指向标号1 subl $4, %esp :esp-4,指向标号2 movl $5, (%esp) :esp指向的栈中标号2对应的地方存储5 call sec :将eip(25)入栈,esp-4指向标号3,跳转至f执行
接下来执行f中的代码:
pushl %ebp :将ebp(1)入栈,esp-4指向标号4 movl %esp, %ebp :ebp指向标号4 subl $4, %esp :esp-4,指向标号5 movl 8(%ebp), %eax :将ebp指向的地址加八对应地址中的数据放入eax,即标号2对应存储数据5,所以(eax)=5 movl %eax, (%esp) :把eax放入esp对应位置,即栈中标号5对应位置 call g :将eip(16)入栈,esp-4指向标号6,跳转至g执行
接下来执行g中的代码:
pushl %ebp :ebp(4)入栈,esp-4指向标号7 movl %esp, %ebp :ebp指向标号7 movl 8(%ebp), %eax :ebp+8位置中的数据赋给eax,即标号5位置,(eax)=5 addl $4, %eax :eax中数据+4,即5+4=9 popl %ebp :ebp(4)出栈,即ebp指向标号4,esp+4指向标号6 ret :eip(16)出栈,esp+4指向标号5,跳转至第16行继续执行
接下来又回到f中继续执行:
leave :把ebp赋给esp,即esp指向标号4,ebp(1)出栈,esp+4指向标号3,ebp指向标号1 ret :eip(25)出栈,esp+4指向标号2,跳转至第25行继续执行
接下来回到main中继续执行:
addl $1, %eax :eax中的数据+1,即9+1=10 leave :把ebp赋给esp,即esp指向标号1,ebp(0)出栈,ebp指向标号0,esp+4指向标号0 ret :此时栈已经空了,恢复最开始的状态,运行结束,最后的结果保存在eax中,为10。
三、 堆栈变化分析
程序从main.s第17行开始运行,CPU的IP寄存器指向汇编代码的第18行,假设堆栈在内存中的地址分别为0,1,2,3……堆栈基指针寄存器(EBP)和堆栈顶指针寄存器(ESP)均指向堆栈段0处。第18~21行首先为main函数开辟新的内存区域,之后将传的参数5入栈,此时堆栈段如下所示:
然后程序调用call 函数,将IP入栈,IP指向代码第9行f处。
在f函数的代码处,首先为f函数开辟新的内存区域,接着将传入的参数5赋值给EAX,并将EAX入栈
程序在此调用call进入g函数。在g函数中,同样先是开辟内存空间,然后将参数传给EAX,并将EAX的值加上4。之后将EBP出栈,并调用ret命令。此时IP重新指向f函数call之后的命令。
之后就是不断的调用leave与ret命令,跳出当前的内存区域,回到上一级函数的内存区域中
ret指令执行后函数g调用完毕,返回。leave指令用于释放一个子例程的栈帧,等价与以下两条指令:
- movl %ebp, %esp
- popl %ebp
并将EAX的值加4,直到跳出main函数,至此程序结束
四、总结
通过本次课程的学习,我掌握了存储计算机工作模型和汇编的基础知识。计算机的基本原理是存贮程序和程序控制。预先要把指挥计算机如何进行操作的指令序列(称为程序)和原始数据通过输入设备输送到计算机内存贮器中。每一条指令中明确规定了计算机从哪个地址取数,进行什么操作,然后送到什么地址去等步骤。
其中,掌握冯·诺依曼体系结构是理解计算机工作原理的要点。按照冯·诺依曼存储程序的原理,计算机在执行程序时须先将要执行的相关程序和数据放入内存储器中,在执行程序时CPU根据当前程序指针寄存器的内容取出指令并执行指令,然后再取出下一条指令并执行,如此循环下去直到程序结束指令时才停止执行。其工作过程就是不断地取指令和执行指令的过程,最后将计算的结果放入指令指定的存储器地址中。
在课程中提到了很多操作系统的内容由于知识结构,我比较欠缺,今后我会加强这方面的知识储备。
刘帅
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000