实验内容:

请使用Example的c代码分别生成.cpp,.s,.o和ELF可执行文件,并加载运行,分析.s汇编代码在CPU上的执行过程

实验报告要求

通过实验解释单任务计算机是怎样工作的,并在此基础上讨论分析多任务计算机是怎样工作的。

example.c代码如下

int g(int x)

{

return x+3;

}

intf(intx)

{

return g(x);

}

intmain(void)

{

return f(8)+1;

}

一、代码编译过程

编译编译程序读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,再由汇编程序转换为机器语言,并且按照操作系统对可执行文件格式的要求链接生成可执行程序。

C源程序-->预编译处理(.c)-->编译、优化程序(.s.asm)-->汇编程序(.obj.o.a.ko)-->链接程序(.exe.elf.axf等)

流程如图所示:

计算机是怎样工作的_Linux

预处理:使用 -E 参数

输出文件的后缀为“.cpp”

gcc -E -o  example.cpp  example.c

计算机是怎样工作的_编译_02

编译成汇编代码:

预处理文件 → 汇编代码

1. 使用-x参数说明根据指定的步骤进行工作,cpp-output指明

从预处理得到的文件开始编译

2. 使用-S说明生成汇编代码后停止工作

gcc -x cpp-output -S -o  example.s  example.cpp

也可以直接编译到汇编代码

gcc -S  example.c

计算机是怎样工作的_编译_03

编译成目标代码

汇编代码 → 目标代码

gcc -x assembler -c  example.s

直接编译成目标代码

gcc -c  example.c

使用汇编器生成目标代码

as -o  example.o  example.s

计算机是怎样工作的_Linux_04

编译成执行代码 即elf文件  

目标代码 → 执行代码

gcc -o  exampleexample.o

直接生成执行代码

gcc -o  exampleexample.c

计算机是怎样工作的_编译_05

查看汇编代码:

计算机是怎样工作的_编译_06

计算机是怎样工作的_Linux_07

计算机是怎样工作的_Linux_08


涉及的几个寄存器有ebp,esp,eip,其中ebp和esp分别指向当前栈的栈底和栈顶,eip是指令寄存器,存储着CPU要执行的下条指令的地址。

从main函数开始分析:


  1. pushl   %ebp  

  2. movl     %esp, %ebp  

显示ebp入栈(保存之前的ebp),然后使ebp指向与esp相同,此时栈情况如下:

计算机是怎样工作的_Linux_09

调用函数之前,要将参数(本例是8)压栈,


  1. subl  $4, %esp  

  2. movl $8, (%esp)  


这两条指令完成参数入栈的作用,此时,栈的情况如下图:

计算机是怎样工作的_编译_10

然后是call f;

call f 的过程包括将eip入栈,即保存返回地址,然后将函数f的地址给eip,然后开始执行函数f

计算机是怎样工作的_编译_11

执行函数f的过程,首先仍然是保存旧栈,


  1. pushl %ebp  

  2. movl %esp, %ebp  

计算机是怎样工作的_编译_12


  1. subl    $4, %esp  

  2. movl    8(%ebp), %eax  

  3. movl    %eax, (%esp)  


这个过程是参数x(这里是8)入栈,执行完后

计算机是怎样工作的_编译_13


接着是call g,

计算机是怎样工作的_Linux_14

然后开始执行g,同理,先保存ebp,


  1. pushl %ebp  

  2. movl %esp, %ebp  

计算机是怎样工作的_编译_15

  1. movl    8(%ebp), %eax  

  2. addl    $3, %eax  

这两条指令进行计算,计算结果放入eax寄存器,内存不发生变化


  1. popl    %ebp  

  2. ret  

完成计算后,出栈

计算机是怎样工作的_编译_16

此时,返回到f中,

  1. leave  

  2. ret  

其中,leave等价于movl %ebp , %esp;popl %ebp;

ret 相当于popl %eip

计算机是怎样工作的_Linux_17

再次回到main中。

  1. addl    $1, %eax  

该指令进行计算,结果仍然存在eax中。

  1. leave  

  2. ret  

函数执行完毕。
关于计算机单任务与多任务的运行:

单任务运行,CPU通过读取EIP寄存器的值得值下一条指令的内存地址,然后读取指令并执行。整个过程中,始终由EIP保存将要执行的下一条指令的地址。函数调用过程中,如上面分析,需要保存现场,执行完后要返回,主要是通过EBP,ESP等寄存器实现的。

多任务情况要复杂一些,依靠进程调度、中断机制等实现。

SA*****232 张轩