【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 代码
终端中输入命令:
- vim example.c
- int g(int x)
- {
- return x+3;
- }
- int f(int x)
- {
- return g(x);
- }
- int main(void)
- {
- return f(8)+1;
- }
按‘Esc’,输入“:wq”退出编辑。
2 预处理->编译->汇编->链接
从源代码到可执行文件共经历四个步骤:预处理->编译->汇编->链接。其中预处理的作用是根据源代码中的预处理指令修改源代码,将宏和常量标识符用相应的代码和值替代;编译的作用是进行代码规范性、语法错误等检查,并生成汇编代码;汇编的作用是将编译生成的汇编代码转换成机器代码即二进制代码(目标代码);链接的作用是将多个目标代码同库文件进行连接最终生成可执行程序。这四个步骤分别生成.cpp、.s、.o/.obj和ELF文件。
2.1 预处理
终端中输入命令:
- gcc -E example.c -o example.cpp
2.2 编译
终端中输入命令:
- gcc -S example.cpp -o example.s
2.3 汇编
终端中输入命令:
- gcc -c example.s -o example.o
2.4 链接
终端中输入命令:
- gcc example.o -o example
3 分析.s汇编代码在CPU上的运行过程
终端中输入命令:
- vim example.s
- .file "example.cpp"
- .text
- .globl _Z1gi
- .type _Z1gi, @function
- _Z1gi:
- .LFB0:
- .cfi_startproc
- pushq %rbp
- .cfi_def_cfa_offset 16
- .cfi_offset 6, -16
- movq %rsp, %rbp
- .cfi_def_cfa_register 6
- movl %edi, -4(%rbp)
- movl -4(%rbp), %eax
- addl $3, %eax
- popq %rbp
- .cfi_def_cfa 7, 8
- ret
- .cfi_endproc
- .LFE0:
- .size _Z1gi, .-_Z1gi
- .globl _Z1fi
- .type _Z1fi, @function
- _Z1fi:
- .LFB1:
- .cfi_startproc
- pushq %rbp
- .cfi_def_cfa_offset 16
- .cfi_offset 6, -16
- movq %rsp, %rbp
- .cfi_def_cfa_register 6
- subq $8, %rsp
- movl %edi, -4(%rbp)
- movl -4(%rbp), %eax
- movl %eax, %edi
- call _Z1gi
- leave
- .cfi_def_cfa 7, 8
- ret
- .cfi_endproc
- .LFE1:
- .size _Z1fi, .-_Z1fi
- .globl main
- .type main, @function
- main:
- .LFB2:
- .cfi_startproc
- pushq %rbp
- .cfi_def_cfa_offset 16
- .cfi_offset 6, -16
- movq %rsp, %rbp
- .cfi_def_cfa_register 6
- movl $8, %edi
- call _Z1fi
- addl $1, %eax
- popq %rbp
- .cfi_def_cfa 7, 8
- ret
- .cfi_endproc
- .LFE2:
- .size main, .-main
- .ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
- .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等寄存器的值,然后依次执行当前任务的指令直到时间片完成或中断发生再次保存现场执行其他任务。以此往复就是多任务计算机的工作过程。
- 【Linux操作系统分析】计算机是怎样工作的
- 【Linux操作系统分析】计算机是怎样工作的
- 【Linux操作系统分析】实验一:计算机是怎样工作的
- Linux操作系统实验一:计算机是怎样工作的
- Linux操作系统分析(一)计算机是怎么工作的?
- 计算机是怎样工作的
- 计算机是怎样工作的?
- 计算机是怎样工作的
- 计算机是怎样工作的
- Linux操作系统分析之计算机工作原理
- 【实验一】计算机是怎样工作的?
- [Lab1]计算机是怎样工作的
- 实验:计算机是怎样工作的?
- Linux操作系统分析-(3)Linux操作系统是如何工作的?破解操作系统的奥秘
- 《Linux内核分析》 之 操作系统是如何工作的。2
- 《Linux内核分析》 之 计算机是如何工作的。1
- Linux内核分析 计算机是如何工作的? 学习笔记
- linux 操作系统-lab1计算机怎么工作的?
- MC新手入门(四十九)------ 文件操作
- 谷歌开发工具android studio启动不了的解决方法
- 对于输入字符串数组的处理,三维数组
- 复选框单选
- CLR线程池的作用与原理介绍
- 【Linux操作系统分析】计算机是怎样工作的
- latex 如何使用bib产生多种文献格式
- dimension reduce(梯度下降)self-organizing maps(自组织映射)
- MC新手入门(五十)------服务器连接 一
- HDU 4288 && Codeforces 85D (线段树+离散化+离线处理)
- 关于NavigationBar的View的一些设置
- 努力成为优秀的工程师
- [x264] fps与timebase, num与den
- Spring中scope属性singleton和prototype使用的区别