失意高调,得意低调
分类: LINUX
2013-09-05 16:55:44
原文地址:Linux 内存管理 作者:formycuteboy
内存 是 Linux 内核所管理的最重要的资源之一;
内存管理子系统 是操作系统中最重要的部分之一.
对于立志从事内核开发的工程师来说,熟悉Linux 的内存管理系统非常重要.
物理地址是指出现在 CPU 地址总线上的 寻址物理内存的地址信号,
是地址变换的最终结果;
线性地址又名 虚拟地址,在 32 位 CPU 架构下,可以表示 4G 的地址空间,
用 16进制 表示就是 0x00000000 到 0xFFFFFFFF.
程序代码经过编译后,在汇编程序中使用的地址;
CPU 要将一个 逻辑地址 转换为 物理地址,
需要两步:
首先 CPU 利用 段式内存管理 单元,将逻辑地址 转换成线性地址;
再利用 页式内存管理 单元,把 线性地址 最终转换为 物理地址.
16 位 CPU 内部拥有 20 位的地址线,它的寻址范围就是 2 的 20 次方,也就是 1M 的内存空间.
但是 16 位 CPU 用于存放地址 寄存器 ( IP , SP ......)只有 16位,因此只能访问 65536 个存储单元,64K.
为了能够访问 1M 的内存空间,CPU 就采用了 内存分段 的管理模式,并在 CPU 内部加入了段寄存器.
16 位 CPU 把 1M 内存空间分为若干个逻辑段,
每个逻辑段的要求如下:
1,逻辑段的起始地址 ( 段地址 ) 必须是 16 的倍数,即 最后 4 个 二进制位必须全为 0.
2,逻辑段 的最大容量为 64 K.
由于段地址 必须是 16 的倍数,所以值的一般形式 为 XXXX0H , 即 前 16 位 二进制是变化的,后 4 位 是固定的.
鉴于段地址的这种特性,可以只保存前 16 位 二进制来保存整个段基地址,
所以每次使用时要用段寄存器左移补 4 个 0 ( 乘以16 ) ,来得到实际的段地址.
在确定了某个存储单元所属的段后,只是知道了该存储单元所属的范围 ( 段地址 -> 段地址 + 65536 ),
如果想确定 该内存单元的具体位置,还必须知道 该单元在内存的偏移,有了段地址 和偏移量,
就可以唯一的 确定内存单元在 存储器中的具体位置.
逻辑地址 = 段基地址 + 段内偏移量
由 逻辑地址 得到 物理地址 的公式为:
PA = 段寄存器的值 * 16 + 逻辑地址
段寄存器是为了对内存进行分段管理而增加的,
16位 CPU 有四个段寄存器,
用于代码段的访问, CS 指向 存放程序的段基址, IP 指向下条要执行的指令在 CS 段的偏移量,
用这两个 寄存器就可以得到一个内存物理地址,该地址存放着一条要执行的指令.
用于堆栈段的访问, SS 指向栈顶,
可以通过 SS 和 SP 两个寄存器直接访问栈顶单元的内存物理位置.
用于数据的访问. DS 中的值左移四位得到数据段起始地址,再加上 BX 中的偏移量,
得到一个存储单元 的物理地址.
用于附加段的访问. ES 中的值在 左移四位 得到 附加段起始地址,再加上 BX 中的偏移量,
得到一个存储单元的 物理地址.
32 位 PC 的内存管理仍然采用 " 分段 " 的管理模式,逻辑地址同样由段地址和偏移量两部分组成,
32 位 PC 的内存管理和 16 位 PC 的内存管理有 相同 和不同之处,因为 32位 PC 采用了两种不同的工作模式:
实模式 和 保护模式.
在实模式下, 32 位 CPU 的内存管理 与 16 位 CPU 是一致的.
段基地址长达 32 位,每个 段的最大容量可达 4G ,段寄存器的值是段地址的 “ 选择器 ” ( Selector ),
用该 “选择器” 从内存中得到一个 32 位的段地址,存储单元的物理地址就是 该段地址加上 段内偏移量,
这与 32 位 CPU 的物理地址计算方式完全不同.
其值在不同的模式下具有不同的意义:
1,在实模式下:
段寄存器的值 * 16 就是 段地址.
2,在保护模式下:
段寄存器的值是一个选择器,间接指出一个 32 位 的段地址.
从管理 和效率 的角度出发,线性地址 被分为固定长度的组,称为 页 ( page ),
例如: 32 位的机器,线性地址最大可为 4G ,如果用 4K 为一个页 来划分,
这样整个线性地址就被划分为 2 的 20 次方个页.
另一类 “ 页 ” ,称之为 物理页,或者是 页框、页帧.
分页单元把所有的 物理内存 也划分为固定的长度的管理单位,
它的长度一般与线性地址页是相同的.
Linux 内核 的设计并没有全部采用 Intel 所提供的段机制,
仅仅是 有限度的 使用了分段机制,这不仅简化了 Linux 内核的设计,
而且 为把 Linux 移植到其他平台创造了条件,因为 很多的 RISC 处理器并不支持段机制.
所有段的基地址均为0
由此可以得出,每个段的逻辑地址空间范围 为 0 - 4 GB,
因为每个段的基地址为 0 ,因此,逻辑地址 与 线性地址保持一致 ( 即 逻辑地址 的偏移量 字段的值 与 线性地址的值 总是 相同的 ),
在 Linux 中所提到的 逻辑地址 和线性地址 ( 虚拟地址 ),可以认为是 一致的.
看来 , Linux 巧妙的把 段机制给绕过去了,而 完全利用了分页机制.