试验题目:计算机是怎么样工作的?
实验环境:ubuntu 9.10
实验内容和步骤:
在linux下C代码经过预处理,编译成汇编代码,汇编成目标代码和链接成ELF可执行文件,依次生成.cpp .S .o elf文件,下面通过example.c观察其过程
1、预处理 gcc -E -o example.cpp example.c
预处理的过程主要处理那些源代码文件中以“#”开始的预编译指令,如#include,#define等内联展开等,下面截取的一部分预编译后的文件
可通过wc指令查看具体的差异example.c 和 example.cpp 具体的差异会发现明显的比源文件大不少
1、编译成汇编代码 gcc -x cpp-output -S -o example.s example.cpp
查看汇编代码
3、把汇编代码汇编成目标代码 gcc -x assembler -c example.o example.s
4、把目标代码链接成elf可执行代码 gcc -o example example.o
example.s无法查看到代码的实际运行地址,于是用gdb 的disas反汇编查看
命令如下:
gdb example
disas main
disas f
disas g
main 反汇编
f 函数反汇编
g 函数反汇编
分析整个程序的执行过程
pushl %ebp //入栈,该ebp是main函数的基址地址
movl %esp,%ebp // %ebp = %esp ,ebp指向新函数的基址地址,此时与esp一样指向栈底
subl $4 , %esp //栈向下生长,esp=esp-4
movl $8,(%esp) //把8放入esp所指的内存空间中,即入栈,局部变量保存
call f //等价于push %eip //eip 存放当前指令的下一条指令
movl 0x80483b8f %eip //进入对f调用
f:
push %ebp
mov %esp,%ebp
sub $0x4 %esp
mov $0x8(%esp), %eax //把esp+8地址里存放的数值,就是:8放入eax中
这里可以看到把8传给f不是直接把实参传递,而是对其做 一个copy的副本传递的
mov %eax ,(%esp) //把eax中的值放入esp所指的内存的中
call 0x80483b4<g> //调用g函数
相当于:push %eip
mov $0x80483b4 %eip
以上是单个程序的执行过程,但是在多任务的情况下情况是什么样的呢?
在单核的计算机系统中不存在真正的并行运算/执行,在任务的执行是在宏观上并行,微观上串行,他的执行过程是,每个任务在执行时,以时间轮片的方式(多种策略中的一种),给每一个任务设定一个时间片,当时间片用完时,触发中断,系统调用另外一个任务运行,在中断的过程中要确保下一次任务能正确的返回被中断处继续执行,这就要求,保存当前被中断的任务要执行的下一条指令入栈,待到返回时能够正确的返回当前被中断的任务而继续往下执行
总结:
计算机的工作并不是表面上看的c代码表面上的意思,它的真正的执行过程
整个程序的执行过程是main—>f—>g—f—>g—>main,多任务条件下(单核计算机系统中)的任务的执行也是按照与此类似的过程