首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

通俗易懂!高手带你理解嵌入式Linux中高端内存与低端内存

通俗易懂!高手带你理解嵌入式Linux中高端内存与低端内存

通俗易懂!高手带你理解嵌入式Linux中高端内存与低端内存

高端内存是Linux中一个重要的概念,初涉Linux时曾经对这个概念非常迷惑。实际上这个概念比较简单,理解这个概念,需要追溯一下Linux的内存管理。

从前,CPU的地址总线只有32位,再早的就不再追溯了。32的地址总线无论是从逻辑上还是从物理上都只能描述4G的地址空间,在物理上理论上最多拥有4G内存(除了IO地址空间,实际内存容量小于4G),逻辑空间也只能描述4G的线性地址空间。为了合理的利用4G空间,Linux采用了3:1的策略,即内核占用1G的线性地址空间,用户占用3G的线性地址空间。所以用户进程的地址范围从0~3G,内核地址范围从3G~4G,也就是说,内核空间只能管理1G的内存。

对于如此紧张的线性地址资源,内核空间与用户空间的肆意瓜分,导致了内存管理上的问题:当物理内存大于1G时,内核线性地址空间小于实际的物理内存容量,内核如何实现对大于1G内存的管理呢?说到这里,需要提一下内核空间对内存的管理方法。一方面为了提高内核空间对内存的管理效率;另一方面,为了简化内核空间对内存的管理方法,内核采用线性映射的方法实现对内存的管理,从Linux实现的方法来看,物理地址与内核的虚拟地址只差一个偏移量。所以,当物理内存大于1G时,物理内存无法全部映射到内核线性地址空间,这就产生了上述问题。

从上述描述可以看出,地址空间大于1G的内存区域称之为高端内存,同理,小于1G的内存区域称之为低端内存。高端内存的管理需要进行非线性映射,为此,在内核线性地址空间预留了128M的空间,位于线性地址空间的高端。如今,CPU的地址总线都扩大到64位了,线性地址资源非常丰富,所以,可以给内核空间预留足够的线性地址资源,在最近一段时间内,内核线性地址资源与物理内存容量之间的矛盾将不再突出,高端内存的概念也就在64位CPU上消失了。

原先一直都对Linux高端内存的管理认识模模糊糊的,可能主要是初次接触linux kernel 是0.11版吧,当初的内存设计是16M,Linus对拥有32M的内存都是觊觎万分,1G内存恐怕是天方夜谭了。16M内存哪里还顾得上高端内存,脑子就一直没有这种概念。先我们还是来看看为什么要有高端内存?

80386的线性寻址空间是4G,内核空间从3G开始,如果全部采用"线性映射"(物理地址和逻辑地址只差一个常量 PAGE_OFFSET ),最多管理1G物理内存,也就是1G的物理内存挨着挨着对应的是虚拟地址的3G到4G的位置。你想想如果多于1G的内存,我们用什么线性地址来装下这些多出的地址呢? 我朋友的机器有2G,据说玩游戏巨爽(AMD64300+).显然如果线性映射我的朋友就会浪费1G内存.为了使内核能够访问这些"高端内存",内核使用HighMem.做法是不将内核1G的虚拟地址空间全部映射成物理内存,而是预留一部分给高端内存做临时映射使用.

其实内核不仅仅预留了highmem的地址空间,还给fixmap,vmalloc预留了虚存空间.实际上,系统初始化的时候预留128M虚存,896M用于"直接"映射物理内存。下面我们先贴上一幅图,摘自《understand linux kernel 》



毕竟意淫是很考大家的空间想象能力的,所以我们还是依照图片来看图说话:

高端内存映射有三种方式:

1、映射到“内核动态映射空间”
这种方式很简单,因为通过 vmalloc() ,在"内核动态映射空间"(上图的VMALLOC_START到VMALLOC_END)申请内存的时候,就可能从高端内存获得页面(参看 vmalloc 的实现),因此说高端内存有可能映射到"内核动态映射空间" 中。

2、永久内核映射
如果是通过 alloc_page() 获得了高端内存对应的 page,如何给它找个线性空间?
内核专门为此留出一块线性空间,从 PKMAP_BASE 到 FIXADDR_START (上图的倒数第二块区域),用于映射高端内存。在 2.4 内核上,这个地址范围是 4G-8M 到 4G-4M 之间。这个空间起叫“内核永久映射空间”或者“永久内核映射空间”。

这个空间和其它空间使用同样的页目录表,对于内核来说,就是 swapper_pg_dir,对普通进程来说,通过 CR3 寄存器指向。

通常情况下,这个空间是 4M 大小,因此仅仅需要一个页表即可,内核通过来 pkmap_page_table 寻找这个页表。通过 kmap(), 可以把一个 page 映射到这个空间来。由于这个空间是 4M 大小,最多能同时映射 1024 个 page。因此,对于不使用的的 page,及应该时从这个空间释放掉(也就是解除映射关系),通过 kunmap() ,可以把一个 page 对应的线性地址从这个空间释放出来。

3、临时映射
内核在 FIXADDR_START 到 FIXADDR_TOP 之间保留了一些线性空间用于特殊需求。这个空间称为“固定映射空间”
在这个空间中,有一部分用于高端内存的临时映射。

这块空间具有如下特点:
1、每个 CPU 占用一块空间
2、在每个 CPU 占用的那块空间中,又分为多个小空间,每个小空间大小是 1 个 page,每个小空间用于一个目的,这些目的定义在 kmap_types.h 中的 km_type 中。

当要进行一次临时映射的时候,需要指定映射的目的,根据映射目的,可以找到对应的小空间,然后把这个空间的地址作为映射地址。这意味着一次临时映射会导致以前的映射被覆盖.通过 kmap_atomic() 可实现临时映射。


想学习的你和我联系预约就可以免费听课了。
以下课程可免费试听C语言、电子、PCB、STM32、Linux、FPGA、JAVA、安卓等。
宋工企鹅号:3524-6590-88   Tel/WX:173--1795--1908
返回列表