一、实验
使用gcc –S –o main.s main.c -m32
命令编译成汇编代码,如下代码中的数字请自行修改以防与他人雷同
1 int g(int x)
2 {
3 return x + 3;
4 }
5 int f(int x)
6 {
7 return g(x);
8 }
9 int main(void)
10 {
11 return f(8) + 1;
12 }
修改过后源代码:
经过编译过的原始汇编代码:
简单地汇编代码:
1 g:
2 pushl %ebp
3 movl %esp, %ebp
4 movl 8(%ebp), %eax
5 addl $4, %eax
6 popl %ebp
7 ret
8 f:
9 pushl %ebp
10 movl %esp, %ebp
11 subl $4, %esp
12 movl 8(%ebp), %eax
13 movl %eax, (%esp)
14 call g
15 leave
16 ret
17 main:
18 pushl %ebp
19 movl %esp, %ebp
20 subl $4, %esp
21 movl $2, (%esp)
22 call f
23 addl $3, %eax
24 leave
25 ret
堆栈变化图:
在汇编代码中分析堆栈变化:
1 g:
2 pushl %ebp ;ebp4入栈 ebp指向4 esp指向7
3 movl %esp, %ebp ;ebp = esp =7
4 movl 8(%ebp) ,%eax ;eax = 2
5 addl $4, %eax ;eax + 4 = 6
6 popl %ebp ;ebp出栈 ebp指向esp1 esp减4
7 ret ;ret = pop eip eip14出栈 esp指向5
8 f:
9 pushl %ebp ;ebp1入栈 ebp指向1 esp指向4
10 movl %esp, %ebp ;ebp = esp =4
11 subl $4, %esp ;esp -4 指向5
12 movl 8(%ebp), %eax ;ebp+8=eax 指向2 eax = 2
13 movl %eax, (%esp) ;把eax放入esp所指的位置
14 call g ;调用g函数 eip15入栈
15 leave ;leave = movl %ebp %esp popl %ebp esp = ebp = 4 ebp1出栈 ebp指向1 esp-4指向3
16 ret ;eip23出栈 esp-4指向2
17 main:
18 pushl %ebp ;ebp0入栈 ebp指向0 esp指向1
19 movl %esp, %ebp ;ebp = esp =1
20 subl $4, %esp ; esp-4 esp指向2
21 movl $2, (%esp) ;2入栈
22 call f ;调用f函数 push eip movl eip eip23入栈
23 addl $3, %eax ;eax+3
24 leave ; esp = ebp = 1 esp0出栈 ebp = 0 ebp-4 = 0
25 ret
总结:
通过这个小实验,可以清楚地认识冯.诺依曼体系,也就是计算机工作的过程,通过各种的寄存器存放数据或指令,系统在通过指令一步一步向下执行。从这个堆栈的分析可以看出系统先将main函数压入栈中,调用f函数时将f函数压入栈中,调用g函数同样如此;在g函数使用完毕后,释放g函数所占用的堆栈,继续执行未执行完的函数命令,直到程序运行完毕或者出错。也就是说计算机工作就像是流水线一步一步取指译码执行。
郭皓原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000