分类: LINUX
2012-05-27 18:48:46
在上一篇博文里,我们已经看到Linux如何有效地利用80x86的分段和分页硬件单元把逻辑地址转换为线性地址,在由线性地址转换到物理地址。那
么我们的应用程序如何使用这些逻辑地址,整个内存的地址布局又是怎样的?打一个比方,内存就像一座城市,而居住在这个城市里的市民就像是各个进程,一个市
民吃喝拉撒睡,当然就得用于“房子”、“车子”、“票子”等各种各样的资源。有些资源是固定的,如“房子”,我们称之为静态数据;有些资源是动态的,如
“车子”,我们称之为动态数据;有些资源是用来购买(产生)数据的,如“票子”,我们称之为代码。
现在,我们就来看看内存这座巨大的城市史如何布局的。在系统初始化阶段,内核首先在实模式下建立一个物理地址映射来指定哪些物理地址范围对内核可用而哪些不可用(主要是根据映射硬件设备I/O的共享内存,或者根据相应的页框含有的BIOS数据)。
内存的某些部分将永久地分配给BOIS或内核,用来存放BIOS信息、内核代码以及静态内核数据结构。所以内核将下列页框记为保留:
• 在不可用的物理地址范围内的页框,一般用来存放BIOS信息。
• 含有内核代码和已初始化的数据结构的页框。
标记为保留页框中的页,绝不能被动态分配或交换到磁盘上。
一般来说,Linux内核安装在RAM中从物理地址0x00100000开始的地方,也就是说,从第二个MB开始。所需页框总数依赖于内核的配置方案:典型的配置所得到的内核可以完全被安装在小于3MB的RAM中。
为什么内核没有安装在RAM第一个MB开始的地方?主要是为具体的PC体系结构所考虑。例如:
• 页框0由BIOS使用,存放加电自检(Power-On Self-Test,POST)期间检查到的硬件配置。因此,很多膝上型电脑的BIOS甚至在系统初始化后还将数据写到该页框。
• 物理地址从0x000a0000 到 0x000fffff的范围通常留给BIOS例程,并且映射ISA图形卡上的内部存储器。这个区域就是所有IBM兼容PC上从640KB到1MB之间著名的洞:物理地址存在但被保留,对应的页框不能由操作系统使用。
• 前1MB内的其他页框可能由特定计算机模型保留。例如,IBM 笔记本电脑把0x0a页框映射到0x9f页框。
在启动过程的早期阶段,内核询问BIOS并了解物理内存的大小,并调用machine_specific_memory_setup()函数建立物理地址映射。假设我们的内存是128MB,那么,第一个MB就给BIOS了。整个128MB的内存被物理映射成以下布局:
0x00000000 - 0x0009ffff 除第一个页框外的640K空间可用
0x000a0000 - 0x000effff 保留
0x000f0000 - 0x000fffff 保留给BIOS例程
0x00100000 - 0x07feffff 126.9MB可用空间
0x07ff0000 - 0x07ff2fff ACPI data
0x07ff3000 - 0x07ffffff ACPI NVS
0xffff0000 - 0xffffffff 保留
这
里简单介绍一下128MB内存的末尾,从0x07ff0000
到0x07ff2fff的物理地址范围中存有加电自测(POST)阶段由BIOS写入的系统硬件设备信息;在初始化阶段,内核把这些信息拷贝到一个合适的
内核数据结构中,然后认为这些页框是可用的。相反,从0x07ff3000到0x07ffffff的物理地址范围被映射到硬件设备的ROM芯片。从
0xffff0000开始的物理地址范围标记为保留,因为它由硬件映射到了BIOS的ROM芯片。注意BIOS也许并不提供一些物理地址范围的信息(在上
述图中,范围是0x000a0000到 0x000effff)。为安全可靠起见,Linux假定这样的范围是不可用的。
虽然,我们看到第一个MB里,BIOS并没有用完,但是为了避免把内核装入一组不连续的页框里,影响性能,Linux便跳过第1MB的RAM,之间从第2个MB开始加载。其实一般来说,对于两个MB,也就是512个页框,对初始化时的内核代码及一些静态数据,已经足够了。
内
核可能不会见到BIOS报告的所有物理内存:例如,如果未使用PAE支持来编译,即使有更大的物理内存可供使用,内核也只能寻址4GB大小的RAM。
setup_memory()函数在machine_specific_memory_setup()执行后被调用:它分析物理内存区域表并初始化一些变
量来描述内核的物理内存布局,这些变量如下表所示:
变量名称 |
说明 |
num_physpages |
最高可用页框的页框号 |
totalram_pages |
可用页框的总数量 |
min_low_pfn |
RAM 中在内核映像后第一个可用页框的页框号 |
max_pfn |
最后一个可用页框的页框号 |
max_low_pfn |
被内核直接映射的最后一个页框的页框号(低地址内存) |
totalhigh_pages |
内核非直接映射的页框的总数(高地址内存) |
highstart_pfn |
内核非直接映射的第一个页框的页框号 |
highend_pfn |
内核非直接映射的最后一个页框的页框号 |