一、实验
使用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