目录
高端内存处理
By: Peter Zijlstra a.p.zijlstra@chello.nl
什么是高端内存?
当物理地址大小接近或者达到虚拟内存的最大值时,会使用高端内存( highmem
)。那时对于内核来说不可能维护所有可用的物理内存映射。这意味着内核开始对要被访问的物理内存片段使用临时映射。
这部分没有被永久映射的(物理)内存就是我们所提及的”高端内存”。高端内存的边界具体位于哪里,有各种各样的体系结构相关的限制。
举个例子,在 i386
体系结构中,我们选择将内核映射到每个进程的 VM
空间,这样我们不需要在进入内核态或者从内核态退出时无效所有的 TLB
。这意味可用的虚拟内存空间( i386
是 4G
)需要在用户地址空间和内核地址空间分割。
多个体系结构传统划分方法是 3:1
, 3GiB
属于用户空间,高端的 1GiB
是内核空间:
+--------+ 0xffffffff
| Kernel |
+--------+ 0xc0000000
| |
| User |
| |
+--------+ 0x00000000
这意味着内核任意一次最多只能映射 1GiB
物理内存,但因为我们需要虚拟地址空间做其他的
事情-包括使用临时映射去访问剩余的物理内存-直接映射实际会更少(通常约 896MiB
)。
其他用 TLB
来标记内存管理的体系结构有独立的内核空间和用户空间映射。然而一些硬件
(比如 ARM
系列),当它们使用内存管理上下文标记时有有限的虚拟空间。
临时虚拟映射
内核包括以下几种创建临时映射的方法:
vmap()
. 该函数用来将多个物理页面长久映射到一个连续的虚拟地址空间。unmap
时需要
做全局的同步。kmap()
. 这个函数允许做一个独立页面的短时映射。它需要全局同步,但是有所摊销。kmap()
以嵌入方式使用时易于死锁,所以新写的代码并不推荐使用。kmap_atomic()
. 这个函数允许对一个独立页面做非常短的映射。因为这种映射局限于
所影响的CPU
,所以这个函数工作的很好,但也因此所涉及的任务要求一直在原来的CPU
上
执行直到任务结束, 以免其他任务替换该映射。kmap_atomic()
也可能被中断上下文所使用,因为该函数不会休眠并且引用者也是直到kunmap_atomic()
被调用时才会休眠.这可能被认为
k[un]map_atomic()
不会失败。
使用 KMAP_ATOMIC
何时何地使用 kmap_atomic()
是简单明了的。当代码想访问从高端内存分配所分配页面的内
容(看 __GFP_HIGHMEM
),比如当一个页面”在页缓冲“。有两个相关的API函数,可以像如下方式使用:
/* 寻找“感兴趣”的页面 */
struct page *page = find_get_page(mapping, offset);
/* 获得访问那个页面内容的地址 */
memset(vaddr, 0, PAGE_SIZE);
/* 解除映射那个页面 */
kunmap_atomic(vaddr);
注意 kunmap_atomic()
使用的是 kmap_atomic()
的结果,而不是 kmap_atomic()
的参数.
如果需要映射两个页面因为向从一个页面复制到另一个页面,必须保证 kmap_aotmic
调用严格
嵌套,比如:
vaddr1 = kmap_atomic(page1);
vaddr2 = kmap_atomic(page2);
memcpy(vaddr1, vaddr2, PAGE_SIZE);
kunmap_atomic(vaddr2);
kunmap_atomic(vaddr1);
临时映射的代价
创建临时映射的代价可谓是很大的。框架必须管理内核的页表,数据 TLB
,也许或者必须处理
MMU
的寄存器。
如果 CONFIG_HIGHMEM
没有选中,这是内核将尝试并且简单创建一个算术位的映射,这个映射
将转换页面结构体地址为一个指向页面内容的指针,”而不是篡改的映射”。在这种情况下,
unmap
操作也许可能是一个空操作。
如果 CONFIG_MMU
没有被选中,就不可能有临时映射和高端内存。这种情况下,”算术方法”也将被
使用。
i386 PAE
i386
体系结构在一些情况下允许 32
位机器访问到 64GiB RAM
。这会带来很多后果:
Linux
系统里对每个页要求有一个页帧结构,并且这些页帧要以永久映射方式存在,这
意味着:最多有
896M/sizeof(struce page)
个页帧项; 一个页结构体大小为32
字节, 那将
可存储大约112G
的页面`; 然而kernel在内存中不仅仅需要保存页表。。。PAE
可以使得页表变的更大一些-
这会使系统变慢,因为更多的数据要被访问需要TLB
来回
转载等等. 一个优势是PAE
有更多的PTE
位并且能提供更高级的功能如NX
和PAT
。
通常建议在 32
位机器上不要使用超过 8GiB
的内存-尽管更大的内存对你或者你的工作负载有用,
你得自己聪明一点-不要期待内核开发者当系统崩溃时真的会有多在意。