转自:http://www.cnblogs.com/worldisimple/articles/2447577.html
一、概述处理器(CPU)
1.1 处理器位数
在intel处理器的X86系列中,包含8086和8088的16位处理器,以及从80386(即i386)开始的32位处理器,而如今又有X86-64架构的64位处理器。
那这些16位,32位和64位又有什么意义了?位数越高,处理器的寻址能力越强,则可以支持越大的物理内存。具体如下表(可见对于64位处理器的寻址范围已经是非常之大):
处理器位数 |
可支持的物理内存大小 |
寻址范围 |
16 |
2^16 = 64K |
0x0000 ~ 0xffff |
32 |
2^32 = 4G |
0x00000000 ~ 0xffffffff |
64 |
2^64 |
在平时安装操作系统时候,经常会问是要装32位系统还是64位系统?对于32位处理器,只能安装32位的系统,不能安装64位系统。对于64位处理,既能够安装32位的系统,也可以安装64位的系统。在Linux下可以通过下面的方面查询系统装的是几位的:
(1) 查看根目录下是否有有lib64目录。32位系统只有/lib一个目录,64位的系统会有/lib64和/lib两个目录。
(2) 执行getconf LONG_BIT命令查看返回结果。32位的系统中long类型是4字节(32位),64位的系统中long类型已变成了8字节(64位)。
(3) 执行uname -i命令。32位的系统返回i386,64位的系统放回x86_64。
(4) 执行file /bin/ls命令。
32位系统返回如下结果:
/bin/ls: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5,dynamically linked (uses shared libs), stripped
64位系统返回如下结果:
/bin/ls: ELF 64-bit LSB executable, AMD x86-64, version 1 (SYSV), for GNU/Linux 2.6.9,dynamically linked (uses shared libs), for GNU/Linux 2.6.9, stripped
1.2 处理器的寄存器
寄存器是处理器的组成部分,是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和位址。寄存器的访问速度在所有存储阶层(见下图)是最快的。
在大学学习汇编时候,还用寄存器来写汇编程序,现在也忘记差不多了,这边就大致记录一下处理器分别有哪些寄存器。
在8086中,所有的寄存器都是16位的。
在80386中,寄存器的发生的变化如下:
(1) 把16位的通用寄存器、指令指针寄存器以及标志寄存器扩充为32位的寄存器。
(2) 段寄存器仍然为16位,并增加2个16位的段寄存器。
寄存器的分类 |
32位CPU寄存器 |
16位CPU寄存器 |
主要用途 |
|
通 用 寄 存 器 |
数据 寄存器 |
EAX |
AX |
乘、除运算,字的输入输出,中间结果的缓存 |
EBX |
BX |
存储器指针 |
||
ECX |
CX |
串操作、循环控制的计数器 |
||
EDX |
DX |
字的乘、除运算,间接的输入输出 |
||
变址 寄存器 |
ESI |
SI |
存储器指针、串指令中的源操作数指针 |
|
EDI |
DI |
存储器指针、串指令中的目的操作数指针 |
||
变址 寄存器 |
EBP |
BP |
存储器指针、存取堆栈的指针 |
|
ESP |
SP |
堆栈的栈顶指针 |
||
指令指针寄存器 |
EIP |
IP |
存放下一条将要执行指令的偏移量,偏移量加上当前代码段的基地址,就形成了下一条指令的地址。 |
|
标志位寄存器 |
EFLAG |
FLAG |
||
段寄存器 |
CS |
CS |
代码段寄存器 |
|
DS |
DS |
数据段寄存器 |
||
SS |
SS |
堆栈段寄存器 |
||
ES |
ES |
附加数据段寄存器 |
||
FS |
- |
32位CPU新增加的附加数据段寄存器 |
||
GS |
- |
32位CPU新增加的附加数据段寄存器 |
(3) 增加4个32位的控制寄存器(CR0、CR1、CR2 和CR3),在进程管理及虚拟内存管理中会涉及到CR0(其中有2位分别用于指示运行在实模式/保护模式和是否开启分页机制)、CR2以及CR3(是页目录基址寄存器,保存页目录表的物理地址)这几个寄存器。
(4) 增加4个系统地址寄存器,具体如下:
全局描述符表寄存器GDTR(Global Descriptor Table Register),是48位寄存器,用来保存全局描述符表(GDT)的32位基地址和16位GDT的界限。
中断描述符表寄存器IDTR(Interrupt Descriptor Table Register),是48位寄存器,用来保存中断描述符表(IDT)的32位基地址和16位IDT的界限。
局部描述符表寄存器LDTR(Global Descriptor Table Register),是16位寄存器,保存局部描述符表LDT段的选择符。
任务状态寄存器TR(Task State Register)是16位寄存器,用于保存任务状态段TSS段的16位选择符。
(5) 增加8个32位调式寄存器DR0~DR7。
(6) 增加2个32位测试寄存器TR6和TR7。
二、内存管理
下面的内容主要依赖于80386的32位处理器的保护模式下。
2.1 内存地址
物理内存单元的实际地址即物理地址。32位的处理器寻址范围为0x00000000 ~ 0xffffffff,支持4G的物理内存大小。
程序产生的段选择符与段相关的偏移地址部分构成了逻辑地址。在实模式下,没有分段或分页机制,逻辑地址和物理地址是相等的,不需要地址转换可以直接访问物理内存。但是在保护模式下,逻辑地址需要转换成物理地址,才能访问物理内存。那么,这个时候需要处理器中的内存管理单元(MMU)将逻辑地址映射为物理地址,来进行地址转换(如下图)。
那么,MMU是怎么将逻辑地址转换成物理地址?
MMU是一种硬件电路,它包含两个部件,一个是分段部件,一个是分页部件,通过分段机制(把一个逻辑地址转换为线性地址,线性地址也是32位,其地址取值范围为0x00000000~0xffffffff)和分页机制(把一个线性地址转换为物理地址),最终将逻辑地址映射为物理地址。如下图:
2.2 分段机制
在操作系统原理关于分段的说明:段的分配时为了更好的满足用户,段的长度不固定,由用户定义,每个段都有自己的地址空间(通过基址包含某物理内存的地址,和长度值来表示段的长度),表示一个地址需要给出段部分(选择符)和偏移部分。如下图,如果没有分页的话,那么图中的线性地址也就是物理地址,这种方式对于段的离散分配就很容易导致碎片问题(进而使用分页机制来提高内存利用率):
在Linux中,主要设置了:内核代码段,内核数据段,用户代码段,用户数据段。而且每个段的基地址对应线性地址都是为0,而且都可以使用4G的地址空间,相当与绕过了逻辑地址和线性地址的映射,从而完全利用了分页机制。另外,分段中怎样使用段寄存器可以参考:http://book.51cto.com/art/200812/103305.htm
2.3 分页机制
通过使用分页机制可以很好的提高内存利用率。分页机制把一个线性地址转换为物理地址。Linux下一个页大小为4K,把线性地址(32位)和物理地址(32位,2^32=4G物理内存)都按照4K(2^12)页大小来进行划分,那么,一共有4G/4K=1M的页面,如果只是简单的进行线性地址和物理地址一对一记录映射的话,这样在映射表建立的就会占用比较大的物理内存。这个时候就引入了页表和页目录表,地址转换过程见下图:
(1) 通过对32位线性地址划分:第31~22这10位(2^10=1024)定位页目录项,第21~12这10位定位页表项,第11~0这12位(2^12=4K)为页内偏移值。
(2) 对于页目录表,有1024个页目录项,每个页目录项(又含有1024个页表项)指向下一级页表的物理地址(32位=4个字节),那么一共需要1024*4(=4K)字节,即只要分配一页就可以完全存放。
(3) 对于页表,原理和页目录表一样,那么一共需要1024(1024个页目录项)*1024(每个页目录项含有1024个页表项)*4(=1M)字节。
(4) 对于页目录的物理地址,就存放在CR3寄存器中。
所以,这样可以寻址1024*1024*4K=4G的物理内存。另外,在Linux的X86架构引入了3级页表机制(包括了页全局目录、页中间目录和页表)。
【参考】
2、OReilly.Understanding.the.Linux.Kernel.3rd.Edition
3、PPT: http://www.docin.com/p-54896290.html
4、http://www.ibm.com/developerworks/cn/linux/l-memmod/
-----------------------------------------------------------------