马哲骅 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
计算机是如何工作的
我们平时使用的计算机属于“冯·诺依曼结构计算机”,这种计算机将程序存储在内存中,CPU从内存中不断地取指令并执行指令。CPU能够执行的指令是二进制指令,为了便于理解,人们发明了汇编指令,它是对机器指令的简单翻译,但是增强了可读性。我们平时所使用的编程语言多属于高级语言,它具有良好的可读性,却不能被计算机直接执行,因此,高级语言通常都要经过编译或解释说明等过程,转换为计算机能够执行的机器语言。下面我们以一个简单的C程序为例,分析计算机如何执行指令。
C程序代码参考截图一。将其命名为main.c,然后执行一条命令生成对应的汇编代码:
gcc -S -o main.s main.c -m32
copy
其中-S选项表示在生成汇编代码后停止编译过程,-o选项表示输出编译结果,即输出汇编代码至main.s文件中,-m32则表示生成32位的汇编代码。
生成的main.s文件较复杂,我们把其中以句点"."开始的行全部删去,这些行是链接文件时需要使用的,与程序本身关系不大。留下的汇编代码就是C程序转换后获得的代码了,参考截图二。可以看到汇编代码中也有三个函数,分别对应于C代码中的main(), f(), g()函数。
要说明汇编语言的执行过程,还需要引入另外一个重要的计算机概念——栈。它是一个“先进后出”的数据结构,栈底由寄存器ebp指示,栈顶由寄存器esp指示,栈在内存中向下增长,即每压入一个元素,esp的值减小,每弹出一个元素,esp的值增大。
现在我们来分析汇编代码的执行过程,不妨假设程序开始时esp和ebp两个寄存器指向同一初始位置,以序号0表示,序号延栈增长的方向增大,即压入一个元素后esp指向位置1,弹出一个元素后esp指向位置0,以此类推。要注意相邻两个序号的物理地址相差4而不是相差1。
pushl %ebp
copy
esp的值首先减4,然后将ebp的值存储在esp指示的位置,则栈中序号为1的位置存储了ebp的当前值(0),ebp仍指向位置0,esp指向位置1;
movl %esp, %ebp
copy
此时ebp与esp都指向位置1;
subl $4, %esp
copy
esp指向位置2;
movl $12, (%esp)
copy
栈的位置2存储立即数12;
call f
copy
首先将eip压入栈中,eip此时的值是下一条将要执行的指令的位置,我们不妨以行号代表,即将行号23存入栈中位置3,此时esp指向位置3,ebp指向位置1;然后将函数f的起始位置赋给寄存器eip,即下一条指令为f的第一条指令;
pushl %ebp
copy
栈中位置4存储ebp的值(位置1),esp指向位置4;
movl %esp, %ebp
copy
esp和ebp都指向位置4;
subl $4, %esp
copy
esp指向位置5;
movl 8(%ebp), %eax
copy
将ebp之前两个位置处的值给寄存器eax,即将位置2处存储的值12给eax;
movl %eax, (%esp)
copy
将eax中的值存储到esp指向的位置,即栈中位置5存储12;
call g
copy
首先将eip压入栈中,即将行号15存入栈中位置6,此时esp指向位置6,ebp指向位置4;然后将函数g的起始位置赋给寄存器eip,即下一条指令为g的第一条指令;
pushl %ebp
copy
栈中位置7存储ebp的值(位置4),esp指向位置7;
movl %esp, %ebp
copy
esp和ebp都指向位置7;
movl 8(%ebp), %eax
copy
将ebp之前两个位置处的值给寄存器eax,即将位置5处存储的值12给eax;
addl $6, %eax
copy
eax中的值加上6,变为18;
popl %ebp
copy
将栈顶数据弹出到ebp中,即将栈中位置7的值“位置4”赋给ebp,同时esp指向位置6;
ret
copy
将栈顶数据弹出到eip中,即将栈中位置6的值“行15”赋给eip,同时esp指向位置5,下一条指令变为第15行处的指令;
leave
copy
这条指令首先将ebp的值赋给esp,即esp和ebp都指向位置4,然后将栈顶元素弹出到ebp中,即将栈中位置4的值“位置1”赋给ebp,同时esp指向位置3;
ret
copy
将栈顶数据弹出到eip中,即将栈中位置3的值“行23”赋给eip,同时esp指向位置2,下一条指令变为第23行处的指令;
addl $1, %eax
copy
将寄存器eax中的值加1,变为19;
leave
copy
首先将ebp的值赋给esp,即esp和ebp都指向位置1,然后将栈顶元素弹出到ebp中,即将栈中位置4的值“位置0”赋给ebp,同时esp指向位置0;
ret
copy
将栈顶数据弹出到eip中,即将栈中位置0的值赋给eip,同时esp指向位置-1,下一条指令变为程序开始前的指令。
总结
以上我们逐条分析了汇编程序的运行过程,尤其是栈的变化情况。通过这种分析我们可以看到计算机运行的基本机制:计算机将编辑好的程序存储与内存中,然后逐条执行程序代码,利用寄存器和栈结构来存储中间结果,直至程序执行完毕。
学习时间 13分钟
操作时间 12分钟
按键次数 559次
实验次数 1次
报告字数 2502字
是否完成 完成