王迅 sg5050【实验一】计算机是怎样工作的一、实验内容: 请使用Example中的c代码分别生成.cpp,.s,.o和ELF可执行文件,并加载运行,分析.s汇编代码在CPU上的执行过程。 二、实验报告要求: 通过实验解释单任务计算机是怎样工作的,并在此基础上讨论分析多任务计算机是怎样工作的。
howtowork.c代码:int g(int x) { return x+3; } int f(int x) { return g(x); } int main(void) { return f(8)+1; }
实验环境:Ubuntu10.04
工具:gcc、makefile
一般情况下,c 程序的编译过程为 1. 预处理 2. 编译成汇编代码 3. 汇编成目标代码 4. 链接 预处理:使用 -E 参数 输出文件的后缀为“.cpp” gcc -E -o example.cpp example.c 编译成汇编代码: 预处理文件 → 汇编代码 1. 使用-x参数说明根据指定的步骤进行工作,cpp-output指明 从预处理得到的文件开始编译 2. 使用-S说明生成汇编代码后停止工作 gcc -x cpp-output -S -o example.s example.cpp 也可以直接编译到汇编代码 gcc -S example.c 编译成目标代码 汇编代码 → 目标代码 gcc -x assembler -c example.s 直接编译成目标代码 gcc -c example.c 使用汇编器生成目标代码 as -o example.o example.s 编译成执行代码 即elf文件 目标代码 → 执行代码 gcc -o example example.o 直接生成执行代码 gcc -o example example.c
1,直接编写makefile
2,make后生成预编译文件(*.cpp)、汇编文件(*.s)、目标文件(*.o)、执行文件elf 查看汇编代码:
三、分析汇编代码:
我们从main函数开始对汇编代码进行分析:
//%ebp = %esp,ebp指向新函数的基栈地址,此时与esp一样指向栈底,用来保护整个函数的栈变量
//ebp指向esp,ebp指向新的调用函数基栈地址,在此ebp和esp又开始维护新的函数栈空间
/* 下面,%eax入栈,即将8入栈,这里可以看到实参8传了过来,但是传给的函数f并没有直接使用原实参的8,而是重新分配了一个空间保存,即所谓的副本,函数参数传递为值传递。*/
//下面,注意在pop之前,esp指向的是ebp3即f函数的基栈地址,pop之后,ebp指向f函数的基栈地址。
//上面,寄存器为eax,不会是exa。
leave 等价于 mov %ebp,%esp //可以看到其实是在返回函数,f将g函数的栈空间释放
pop %ebp //ebp又恢复main函数的基栈地址
四、总结:
C语言是以函数为基本单元的,从本次实验分析来看,整个程序也是从一个函数进入到另一个函数的顺序调用,然后顺序返回,如下图所示。
当一个函数跳转到另一个函数的时候,系统会利用寄存器来保护现场。从本次实验中可以看到,寄存器ebp和esp一起用来保护函数的栈空间,通过esp来申请和释放栈空间,ebp来保存调用函数的基栈地址,当进行函数跳转时,会保存函数跳转处的下一条指令和基栈地址(入栈保存),这样在函数在恢复现场的时候可以根据栈的顺序来恢复调用函数的基栈地址,这里常用的汇编指令是leave,当程序返回到调用函数中时,会用leave指令来释放被调用函数的堆栈空间,并恢复自己的基栈地址,这样函数与函数之间的调用就能完好的单步执行下去,并能够进行相互传参,直到整个单任务程序结束。
单任务计算机大体上的工作步骤如下:先把机器码加载到内存,ebp,esp,eip,PC 等寄存器初始化完毕;PC存放当前执行指令,eip中存放下一条即将执行的指令,当eip中的值被PC取走,eip自动加1(如无跳转指令等)。单任务计算机简单归结为“取指、执行、取指、执行”如此循环地工作。
多任务系统本质上也是“取指、执行、取指、执行”循环,但是只是这一段时间取的是任务1的指令,下一段时间取的是任务2的指
令,再下一段取的是任务i的指令......这就需要增添最基本的中断机制来实现多任务的轮流调度。调度法则则有优先级、分时等等。
无论单任务还是多任务系统,由于存在函数调用或调度等因素,均需有现场保护机制及恢复现场机制。单任务系统中栈可实现此
功能;多任务系统中则是由栈与进程描述块共同实现此机制。
比如单任务系统: 函数执行过程中需调用子函数,则将父函数的ebp,eip等入栈(保护现场);为子函数开辟新栈;子函数调用完毕,可由ret指令找回 父函数的栈空间以及该执行的父函数指令(恢复现场)。
比如多任务分时系统: 1:任务a的时间戳到,会产生一个系统级别的中断,系统响应中断,将任务a的现场保护起来——将a运行时用到的ebp,esp,eip,PC, eax等等寄存器的值保存到a的进程描述块中;
2:加载另一个任务b,此时将b之前在b的进程描述块中保存的现场的值加载到cpu中,运行任务b;
3:循环