赵连讯 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ”
本课程是后来补充进来的,因为错过了提交时间。
冯诺依曼体系结构
为了更好的讲清楚计算机的工作原理,老师首先提出了冯诺依曼体系结构这个概念。
从网上总结了部分内容。与冯诺依曼体系结构相对的概念是哈佛体系结构。两者都是存储系统计算机结构。冯诺依曼体系结构将数据和程序存储在同一个存储器上。因此当cpu获得程序和数据的时候,数据宽度都是相同的,从同一个存储器上获得。而哈佛结构的存储系统,是指程序和数据存储在不同的存储器上。cpu从程序存储器上拿到程序后,翻译出来数据的地址,再去数据区域取得数据。这里的灵活性是是程序和数据的宽度可以不同。这两种体系结构都有不同的芯片对应。而对于软件开发者或者操作系统研究者来讲,这两种体系结构的区别是透明的,一般看不到两者的区别。
哈佛结构应用好处是,将程序和数据空间分开,使得程序独立性更好。取值和取数据可以同时进行,此种并行性提高了程序的执行速度。
存储程序计算机
老师从冯诺依曼体系结是为了引出存储程序计算机这样一个概念。存储程序存储在内存中。CPU从存储器中读出每一条语句,依次执行,完成了程序的整个流程。
简单图示:
无论读取数据还是程序,数据宽度都是相同的。
为什么程序知道读取哪一行代码呢?因为在CPU中的EIP中,存储了当前程序的地址。指向了代码段。代码段使用寄存器CS来表示。
EIP具有自动加1的功能,那么就能从程序存储空间一条指令一条指令的取出来,然后执行。
EIP
EIP具有自动加1的功能,自动加一条指令,而不是加1,这里一条指令可能是变长的。硬件会自动处理这种情况。
EIP只能被如下指令修改,JMP,RET,IRET,CALL。EIP寄存器不能使用mov等指令修改。
X86的汇编基础
通用寄存器的列表:
EAX
EBX
ECX
EDX
EBP:堆栈的基指针
ESI
EDI
ESP:
段寄存器
CS
DS
ES
SS:堆栈段的段值
FS
GS
精确的定位一条指令:cs:eip
标志寄存器EFLAGS
虽然老师是略讲了这个寄存器,但是EFLAGS在后续中还会多次提到。所以这里可以查看一些重要的位数。
TF:表示trap,单步执行的用到
IF:interrupt enable flag中断使能寄存器
针对此EFLAGS的操作,有专门的命令:
pushf 把flag的值压栈
popf 把栈顶的值更新到eflag寄存器中
AT&T汇编格式
汇编语言的格式有两种类型,一种是AT&T格式,一种是intel格式,在如下文件文档中详细讲解了两者的不同。
http://sourceware.org/binutils/docs/as/i386_002dMemory.html#i386_002dMemory
在linux中使用AT&T的汇编指令格式。
重要指令
push %eax
等价的指令为:
subl $4,%esp
movl %eax,(%esp)
首先esp的值减去4,然后将eax寄存器中的值存入到esp中。栈顶被压栈。
pop %eax
等价的指令为:
movl (%esp),%eax
addl $4,%esp
把栈顶的值存储到eax中,然后esp加上4.
call 0x1234
等价的指令为:
pushl (%eip)
movl $0x1234,%eip
ret
等价的指令为:
popl (%eip)
汇编分析:
C语言源程序:
#include <stdlib.h>
int g(int x)
{
return x + ;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f() + ;
}
gcc -S -o test.s test.c -m32
生成的汇编代码,删除了其中的.开头的信息。
g:
pushl %ebp
movl %esp, %ebp
movl (%ebp), %eax
addl $3, %eax
popl %ebp//这里没有leave,因为这个函数没有修改esp,所以没必要恢复。
ret
f:
pushl %ebp
movl %esp, %ebp //清空栈,此时ebp指向新的栈底部
subl $4, %esp//esp下移动
movl (%ebp), %eax//ebp+8处的值是入参,放入eax中
movl %eax, (%esp)//把参数存储压栈
call g
leave//与开始对应,并且恢复了ebp,esp指向了调用f之前的栈顶位置
ret//修改eip,esp栈顶继续减少
main:
pushl %ebp //保存ebp
movl %esp, %ebp//清空栈,此时ebp==esp
subl $4, %esp//esp向下移动4字节
movl $8, (%esp)//将数据8存储到esp指向的位置
call f//此时会将下一行代码的地址压栈,更新eip=f
addl $1, %eax//返回值加上1,存储到eax中
leave //恢复ebp,esp重新指向上一个栈顶位置
ret//上一条指令恢复给eip
leave是伪代码,等价指令为,撤销栈清空,恢复ebp
movl %ebp,%esp
popl %ebp
enter是伪代码,等价指令为,实现清空栈
pushl %ebp
movl %esp,%ebp
从上述代码我们可以看到ebp指向的栈底,上一个栈的栈顶位置。用于恢复上一个栈的栈顶。