作者:奋斗的白杨(杨延生)
注:原创作品转载请注明出处
《Linux内核分析》 MOOC课程http://mooc.study.163.com/course/USTC-1000029000
1. 冯诺依曼结构计算机
早期的计算机(例如第一台通用计算机ENIAC)采用的程序与数据分享的形式进行计算,数据保存在穿孔纸带上,而对于数据的控制则通过开关和连线来控制。计算机内部采用十进制,这样就大大增加了计算机电路复杂性。
冯诺依曼参与了刚才所说ENIAC的研发,并提出了自己的修改意见,写成了EDVAC的草案中,草案中第一次提出了存储程序的思想,也就是说把程序与数据不加区分的对待,都存储在存储器中,并首次把计算机分别了5大重要组成部件。
- 控制器
- 运算器
- 存储器
- 输入设备
- 输出设备
2. 存储程序计算机执行程序的过程
计算硬件设计保证了CPU会一条一条的执行存储器中的指令(对应了一条汇编代码),那么每一条指令的执行过程都包括了4个步骤:
取址->译码->执行->回写
取址:就是将指令的内容从存储器取回到CPU,在存储器中的位置是写在IP(指令指针寄存器,也叫PC)中,取回的指令放在IR(指令寄存器)中。
译码:CPU中的译码把IR中的二进制指令解析出一系列的控制信号。
执行:CPU根据译码的结果,进行电路控制,将相关的数据都放在对应的寄存器中,然后计算器(ALU)开始计算。
回写:CPU将计算结果写到指定的寄存器中。
3. 示例程序的汇编代码分析
int g(int x) {
return x + 42;
}
int f(int x) {
return g(x);
}
int main(void) {
return f(42) + 1;
}
在Intel X86_64位上编译为32位下的汇编代码
g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $42, %eax
popl %ebp
ret
f:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call g
leave
ret
main:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl $42, (%esp)
call f
addl $1, %eax
leave
ret
下面分析程序在执行过程中进程栈的变化情况(主要是ebp和esp的变化)
4. 总结
计算机以进程的概念处理程序,程序中的代码与数据被不加区分的以二进制的形式存储在存储器中(存储的位置由存储器决定)。CPU可以自动的逐条的执行程序的代码,相关的数据以及指令跳转信息都通过进程栈来存储。
函数间的跳转主要需要做的事为:1。记录之前eip到栈内,方便回头继续执行。2. 进入到新的函数中,要保存原来的函数的栈基址,然后有了自己新的栈基址。
函数返回时,就会从栈中读出原来进入函数时保存的信息,包括了外部函数的栈基址,以及继续执行的指令的位置。
实验楼实验截图