学号:SA*****81 姓名: **堂
- 实验问题:使用Example的c代码分别生成.cpp,.s,.o和ELF可执行文件,并加载运行,分析.s汇编代码在CPU上的执行过程。
- 实验要求:通过实验解释单任务计算机是怎样工作的,并在此基础上讨论分析多任务计算机是怎样工作的。
1. 实验代码
int g(int x)
{
return x+3;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(8)+1;
}
这段代码中,主函数调用函数f,然后z在函数f中调用函数g
2. 实验过程
2.1 预处理
预处理是在程序源代码被编译之前,由预处理器(Preprocessor)对程序源代码进行的处理。这个过程并不对程序的源代码进行解析,但它把源代码分割或处理成为特定的符号用来支持宏调用。
gcc -E -o example.cpp example.c
这条指令生成预处理文件example.cpp,内容如下
# 1 "example.c"
# 1 "<built-in>"
# 1 "<命令行>"
# 1 "example.c"
int g(int x)
{
return x+3;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(8)+1;
}
2.2 编译
gcc -S -o example.s example.c
这条指令生成汇编指令example.s文件,内容如下
.file "example.c"
.text
.globl g
.type g, @function
g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $3, %eax
popl %ebp
ret
.size g, .-g
.globl f
.type f, @function
f:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call g
leave
ret
.size f, .-f
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $4, %esp
movl $8, (%esp)
call f
addl $1, %eax
addl $4, %esp
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
.section .note.GNU-stack,"",@progbits
用的老版本的gcc生成的内容相对比较容易理解。
2.3 汇编
把汇编语言翻译成机器语言的过程称为汇编。
可以使用以下指令完成汇编工作
gcc -c -o example.o example.c
或者
gcc –x assembler –c example.s -o example.o
2.4 连接
将机器代码按一定格式存储在文件中(Linux下是ELF),以便以后操作系统执行程序时能按照那个格式找到应该执行的第一条指令或其他东西,如资源等。
连接的指令如下
gcc -o example example.o
通过file指令可以查看example的文件属性。
2.5 加载运行
输入指令./example回车,即可完成加载执行,没有打印,所以不会有输出显示在终端。
3 汇编代码在CPU上的执行过程
执行完addl之后,就开始恢复到调用前的下一条指令。其中
leave: movl %ebp , %esp;
popl %ebp
ret: pop %eip(*)
4. 计算机是怎样工作的
单任务计算机:同一时间只能运行一个应用程序。程序从外存载入到内存,按照指令的顺序逐条执行,遇到跳转指令进行相应的跳转。在这个过程中,函数的调用借助堆栈来实现,调用函数先将自己的栈基址压入堆栈,再压入自己下一条指令的地址即返回地址。然后,此时的栈顶作为被调用函数的栈底,之后进入被调用函数继续执行。函数返回时,函数栈执行相反的操作,返回值由寄存器保存。
多任务计算机:多任务计算机在同一时间段内可以运行多个程序。这样的工作方式离不开中断,进程调度等机制。在实现上,多任务计算机的任务切换可以类似单任务计算机中的函数调用,在切换之前,保存前一个任务的ebp,eip,标志等信息,保护现场。这样,当该任务再次获得CPU资源的时候可以恢复现场继续执行。