我们在学习Linux内存管理时了解到:
Linux内核将 0MB ~ 896MB 物理内存,映射为 0xC0000000 ~ 0xF7FFFFFF 虚拟地址,并且可以直接对其寻址。
每个进程的页表(page table)中的 0xC0000000 ~ 0xF7FFFFFF,线性映射在 0x00000000 ~ 0x37FFFFFF 的物理地址上。
对上面这个结论如果还有疑问的,那就需要回顾下一些基础知识了。
虚拟内存部分
在4GB的虚拟内存中,分为2个部分:0x00000000 ~ 0xBFFFFFFF 是进程虚拟内存,而 0xC0000000 ~ 0xFFFFFFFF 是内核虚拟内存。
而划分这两个空间的重要位置就是 PAGE_OFFSET,对不同的体系结构不同。
如下图,在 32 位系统中 0G ~ 3G 属于用户使用的内存空间,3G ~ 4G 属于内核使用的内存空间,所以此时 PAGE_OFFSET 就是 3G 所在的地址位置:0xC0000000。

[图片上传中...(image.png-adb393-1615865131477-0)]
物理内存部分
在物理内存中,分为3个区域:
- ZONE_DMA:包含16MB以下的内存页框(page frames),该区域的物理页面专门供I/O设备的DMA使用。之所以需要单独管理DMA的物理页面,是因为DMA使用物理地址访问内存,不经过MMU,并且需要连续的缓冲区,所以为了能够提供物理上连续的缓冲区,必须从物理地址空间专门划分一段区域用于DMA。
- ZONE_NORMAL:包含16MB以上且896MB以下的内存页框,该区域的物理页面是内核能够直接使用的。
- ZONE_HIGHMEM:包含896MB及以上的内存页框,该区域即为高端内存,内核不能直接使用。
映射关系
针对上面这个问题,我们关注点在kernel memory这部分内存的映射,可以参考 ARM虚拟内存布局 在 59 行的描述:
Start | End | Use |
---|---|---|
PAGE_OFFSET | high_memory-1 | 内核直接映射 RAM 区域,它映射平台 RAM,并且通常以1:1关系映射所有平台RAM |
上面所述的 PAGE_OFFSET ~ high_memory-1 所指的内存地址就是 0xC0000000 ~ 0xF7FFFFFF,也就是下图黄色区域。

虽然内核占据了虚拟内存中最高位的1GB,但映射到的物理内存,却是从最低地址(0x00000000)开始。从下图可以看到,左侧虚拟地址空间绿色区域,线性的映射到右侧 0 ~ 896MB 的物理地址上。

所以在 0xC0000000 ~ 0xF7FFFFFF 区间的映射关系是:虚拟地址 = 物理地址 + PAGE_OFFSET。
需要注意的是,Linux内核并没有把整个1G空间用于线性映射,而只映射了896M物理内存,预留了最高端的128M虚拟地址空间,用来临时映射内存的任意部分,以此完成了对所有高于896M物理内存的访问。
这也就是开篇说映射关系:虚拟内存 0xC0000000 ~ 0xF7FFFFFF,线性映射在 0x00000000 ~ 0x37FFFFFF 的物理地址上。
物理内存不足 1GB
如果系统上只安装了512MB的物理内存,那么上述结论就会明显不满足。
512MB的物理地址范围仅为 0x00000000 ~ 0x1FFFFFFF。如何将虚拟地址从 0xC0000000 ~ 0xF7FFFFFF 映射到物理地址 0x00000000 ~ 0x37FFFFFF 上?
实际上,并非所有虚拟地址都必须映射到物理地址上, 如果代码访问未映射的虚拟地址,则出现缺页异常(page fault)。
因此,如果物理内存只有 512MB,那么ZONE_HIGHMEM将为空,并且ZONE_NORMAL只映射 496MB的物理内存。此时,就没有高端内存一说了,因为所有的内存都被 kernel 直接映射了。
512MB的物理内存(0x00000000 ~ 0x1FFFFFFF),将被线性映射为虚拟地址 0xC0000000 ~ 0xDFFFFFFF。
参考资料:
How does the linux kernel manage less than 1GB physical memory?
Documentation/arm/memory.txt
网友评论