【Linux操作系统分析】计算机是怎样工作的

来源:互联网 发布:python项目开发实例 编辑:程序博客网 时间:2024/06/10 22:18

学号:SA***355     姓名:**之


实验内容:使用Example中的C代码分别生成.cpp,.s,.o文件和ELF可执行文件并加载运行,分析.s汇编代码在CPU上的执行过程

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

实验环境:Ubuntu 12.04    GCC 4.6.3


1 example.c 代码

终端中输入命令:

  1. vim example.c  
按‘i’输入example.c代码如下:

  1. int g(int x)  
  2. {  
  3.     return x+3;  
  4. }  
  5.   
  6. int f(int x)  
  7. {  
  8.     return g(x);  
  9. }  
  10.   
  11. int main(void)  
  12. {  
  13.     return f(8)+1;  
  14. }  

按‘Esc’,输入“:wq”退出编辑。



2 预处理->编译->汇编->链接

从源代码到可执行文件共经历四个步骤:预处理->编译->汇编->链接。其中预处理的作用是根据源代码中的预处理指令修改源代码,将宏和常量标识符用相应的代码和值替代;编译的作用是进行代码规范性、语法错误等检查,并生成汇编代码;汇编的作用是将编译生成的汇编代码转换成机器代码即二进制代码(目标代码);链接的作用是将多个目标代码同库文件进行连接最终生成可执行程序。这四个步骤分别生成.cpp、.s、.o/.obj和ELF文件。

2.1 预处理

终端中输入命令:

  1. gcc -E example.c -o example.cpp  

2.2 编译

终端中输入命令:

  1. gcc -S example.cpp -o example.s  

2.3 汇编

终端中输入命令:

  1. gcc -c example.s -o example.o  

2.4 链接

终端中输入命令:

  1. gcc example.o -o example  


3 分析.s汇编代码在CPU上的运行过程

终端中输入命令:

  1. vim example.s  
得到汇编代码:
  1.     .file   "example.cpp"  
  2.     .text  
  3.     .globl  _Z1gi  
  4.     .type   _Z1gi, @function  
  5. _Z1gi:  
  6. .LFB0:  
  7.     .cfi_startproc  
  8.     pushq   %rbp  
  9.     .cfi_def_cfa_offset 16  
  10.     .cfi_offset 6, -16  
  11.     movq    %rsp, %rbp  
  12.     .cfi_def_cfa_register 6  
  13.     movl    %edi, -4(%rbp)  
  14.     movl    -4(%rbp), %eax  
  15.     addl    $3, %eax  
  16.     popq    %rbp  
  17.     .cfi_def_cfa 7, 8  
  18.     ret  
  19.     .cfi_endproc  
  20. .LFE0:  
  21.     .size   _Z1gi, .-_Z1gi  
  22.     .globl  _Z1fi  
  23.     .type   _Z1fi, @function  
  24. _Z1fi:  
  25. .LFB1:  
  26.     .cfi_startproc  
  27.     pushq   %rbp  
  28.     .cfi_def_cfa_offset 16  
  29.     .cfi_offset 6, -16  
  30.     movq    %rsp, %rbp  
  31.     .cfi_def_cfa_register 6  
  32.     subq    $8, %rsp  
  33.     movl    %edi, -4(%rbp)  
  34.     movl    -4(%rbp), %eax  
  35.     movl    %eax, %edi  
  36.     call    _Z1gi  
  37.     leave  
  38.     .cfi_def_cfa 7, 8  
  39.     ret  
  40.     .cfi_endproc  
  41. .LFE1:  
  42.     .size   _Z1fi, .-_Z1fi  
  43.     .globl  main  
  44.     .type   main, @function  
  45. main:  
  46. .LFB2:  
  47.     .cfi_startproc  
  48.     pushq   %rbp  
  49.     .cfi_def_cfa_offset 16  
  50.     .cfi_offset 6, -16  
  51.     movq    %rsp, %rbp  
  52.     .cfi_def_cfa_register 6  
  53.     movl    $8, %edi  
  54.     call    _Z1fi  
  55.     addl    $1, %eax  
  56.     popq    %rbp  
  57.     .cfi_def_cfa 7, 8  
  58.     ret  
  59.     .cfi_endproc  
  60. .LFE2:  
  61.     .size   main, .-main  
  62.     .ident  "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"  
  63.     .section    .note.GNU-stack,"",@progbits  

分析代码:

由于本机是64位机,栈底指针表示为rbp,栈顶指针表示为rsp,程序中以.cfi开头的命令我们暂时忽略不管。为了方便起见,我们将执行本程序前的rbp表示为rbp0,main函数中的rbp表示为rbp1,f函数中的rbp表示为rbp2,g函数中的rbp表示为rbp3。把执行main中call指令的下一条指令(即addl指令)的地址表示为eip0,f函数中执行call指令的下一条指令(即leave指令)的地址表示为eip1。另外,一般情况下eip的值都是指向下一条指令的地址的,只有在执行call和ret命令时才会发生大的变化,因此我只在程序执行到call和ret指令时显示出eip寄存器的变化情况。栈是从高地址向低地址增长的,我们假设一开始栈是空的。下面我们分析CPU执行该段代码过程中栈和寄存器的变化。(注:call = pushl %eip + movl  0X....,%eip;  ret = popl %eip;  leave = movl%ebp,%esp + popl %ebp  (32位机))

(1)首先找到程序的入口main:

根据上面的汇编代码,我们可以看到程序将依次执行pushq %rbp、movq %rsp,%rbp、movl $8,%edi、call_Z1fi,栈和寄存器的变化如下:


(2)执行call _Z1fi后,程序将跳转到f函数入口地址开始执行,此时栈和寄存器的变化如下:


(3)执行call _Z1gi,程序跳转到g函数入口地址开始执行:


(4)执行ret操作后,程序返回到f函数中继续执行函数f剩余指令:


(5)执行ret操作后,程序返回到main函数中继续执行:


(6)执行完main函数中最后的ret操作。此时,eax寄存器中的值为12,rbp的指向rbp0,eip指向eip0,恢复到程序执行前的状态,整个程序执行完成。


4  计算机是怎样工作的

从上述实验我们可以看到一个程序的执行过程中eip等寄存器以及栈的变化情况。因而不难分析出单任务计算机的工作流程:首先将任务代码加载到内存中,eip总是指向下一条将要执行的指令,栈用来保存跳转地址(这里64位机和32位机不同,64位机中的栈只用来保存地址,操作数保存在寄存器中,而32位机中的栈不仅保存地址还保存操作数),数据寄存器用来保存计算结果,故只要CPU把任务代码的入口地址传给eip那么程序就能逐条执行下去直到结束。

而多任务计算机的工作过程则更加复杂,它涉及到中断、保存现场和恢复现场、时间片轮转、任务调度等。多任务由一个个单任务组成,但是基于效率等方面的考虑,不能让任务一个个顺序执行,而是要让多个任务并发执行。当一个任务的时间片完成或者有优先级更高的任务发生时,系统产生一个中断,此时,要把当前正在执行的任务的各种状态保存下来压入栈中,系统运行其他的任务,而当其他任务执行完成或时间片完成时,系统再次运行当前任务,此时就要弹栈恢复当前任务的状态,修改eip等寄存器的值,然后依次执行当前任务的指令直到时间片完成或中断发生再次保存现场执行其他任务。以此往复就是多任务计算机的工作过程。



原创粉丝点击