我所理解的high memory

来源:互联网 发布:淘宝实名认证 编辑:程序博客网 时间:2024/06/05 18:39

关于Linux 中high memory,网上已经有好多人讲解,在这里把我收集到的资料归纳总结一下,同时加入一些自己的理解,如有不正确之处欢迎指正。

一: 为什么需要high memory

32位的CPU,最大寻址范围2^32-1, 也就是虚拟内存空间的范围为0~4G。 Linux一般吧0~3G划分为用户地址空间,3G到4G为内核地址空间。


图1: 用户和内核地址空间分布

最高的1GB虚拟空间由所有进程和内核共享,因此内核最多寻址1G的虚拟地址空间。如果物理内存超过1G,内核该如何映射呢? 

Linux采取的策略内核空间的前“896M”(注:896M是一个笼统的概念,详细参照第二节)采用固定映射(也可以称为静态映射,直接映射),这部分页表是固定的,虚拟地址 - PAGE_OFFSET = 物理地址(PAGE_OFFSET 一般为3G,0xC0000000),这部分的映射是系统刚刚初始化的时候就建立的,这段物理内存被称为Low Memory。而虚拟地址空间的后“128M”,也就是3G+“896M” ~ 4G-1部分采用动态映射。这样如果物理内存大于1G,通过更新页表就可以被映射到了。(试想如果1G的虚拟地址空间全部采用固定映射,那超过1G的物理内存将无法被内核访问,对不对?)所以超过“896M”的物理内存就称为High Memory。

那如果内存只有1G,是不是可以全部采用固定映射了呢?答案还是No!这是因为内核除了访问内存,还需要访问很多IO设备比如硬件寄存器,片上内存等,所以1G的虚拟空间不可以全部映射到1G的物理内存,仍然需要预留部分空间给这些IO资源(一般ioremap就是用来做这种 映射的)。内核提供了动态映射High Memory的方法,比如kmap, vmalloc。由此大家可以看到High Memory对于系统的物理内存比较大时是必要的。

由此我们可以看出high memory的一些结论如下:

1. High Memory 是指物理内存,可以使内核地址空间映射到高端物理内存,比如大于1G的物理内存这个时候就可以被映射到, 或者硬件寄存器的地址,like 0xdxxxxxxxx,0xcxxxxxxx);

2. High Memory 跟Low Memory一样都被内核管理,都有对应的page结果,都可以被kernel/user 分配,他们的区别是High Memory没有固定的内核虚拟空间与之对应。 High Memory 是内核区间固定映射和动态映射的分界。

3. 如果物理内存较小比如小于“896M”,High Memory是可以没有的;64位系统下不会有High Memory,因为64的虚拟地址空间非常大,物理内存可以被全部固定映射。

 

二: High Memory VS 最高端的“128M”内核虚拟地址:

以i386中一个典型的内存分布为例(物理内存大于“896M”):内核所能自由访问的物理内存为MAXMEM(有固定映射的内存区) 如下定义:



图2: Linux 虚拟内存空间分布

#define VMALLOC_OFFSET        (8 * 1024 * 1024)

#define VMALLOC_START          ((unsigned long)high_memory +VMALLOC_OFFSET)

#ifdef CONFIG_X86_PAE

#define LAST_PKMAP 512

#else

#define LAST_PKMAP 1024

#endif

#define PKMAP_BASE((FIXADDR_BOOT_START - PAGE_SIZE * (LAST_PKMAP + 1))   & PMD_MASK)

#ifdef CONFIG_HIGHMEM

# define VMALLOC_END   (PKMAP_BASE - 2 * PAGE_SIZE)

#else

# define VMALLOC_END   (FIXADDR_START - 2 * PAGE_SIZE)

#endif

#define MAXMEM    (VMALLOC_END - PAGE_OFFSET -__VMALLOC_RESERVE)

可以看到MAXMEM是由VMALLOC_END,PAGE_OFFSET和__VMALLOC_RESERVE决定的。PAGE_OFFSET是0xC0000000。

从以上的定义可以看到,从0xFFFFFFFF的最高地址往下,有FIXADDR固定映射区(主要用在boot阶段用来永久性映射一些物理地址固定的数据结构或者硬件地址(比如ACPI表,APIC地址,等等),PKMAP区(kernel用来临时建立映射来访问物理页用的,可用的地址空间也比较小,4M)和VMALLOC区(128M -8M,8M用来cache kernel 指针错误)。在这三个区域以下是kernel可以使用自由访问的物理内存空间。可以通过下图看到大体的内核1G虚拟地址空间的分布概况:

由此可以看到我们之前所说896M/128M 其实是一个比较笼统的概念,笔者猜测因为vmalloc area 占据了这部分区间的绝大部分,而__VMALLOC_RESERVEdefault 为128M,由此得来这个划分。

现在再以ARM 为例,default vmalloc area 被定义为240M:

#define VMALLOC_END         0xff000000UL

static void * __initdata vmalloc_min=

 (void *)(VMALLOC_END - (240 << 20) - VMALLOC_OFFSET);

而vmalloc_reserve可以通过cmd line 在early_param("vmalloc", early_vmalloc)来配置,如下vmalloc_reserve 最小为16M,最大为VMALLOC_END - (PAGE_OFFSET + 32M). 其中vmalloc_reserve= vmalloc_size + VMALLOC_OFFSET(8M) 。Kernel 将那些与vmalloc area 范围有重叠的memory bank分隔开来,Highmemory的start即被定义为vmalloc_min所对应的物理地址。

ARM 体系架构中PKMAP_BASE 不同于x86,

#define PKMAP_BASE       (PAGE_OFFSET - PMD_SIZE)

从VMALLOC_END 向上到0xFFFFFFFF的区间:

Fixmap 区间的范围是0xfff00000~0xffffdffff    (Addresses provided by fix_to_virt() will be located here)。

其余的区间要不是特殊用途,要不是reserve的区间。具体分布可以参照Memory.txt @Documentation\arm\

由此我们可以看到ARM中的highmemory 的起始地址可以更加自由的配置,因为“896M”的分界对ARM而言就更笼统了。

再来说一下vmalloc区间,这段连续的虚拟地址空间,不论是否有highmemory都是存在的,它的存在使得连续的虚拟地址空间可以映射到非连续的物理内存,只要通过更新页表就可以做到,这和用户态的虚拟内存映射采用了同样这种方法。这在没有大段连续的空闲物理地址时,是非常重要的。

原创粉丝点击