summaryrefslogtreecommitdiff
path: root/Documentation/translations/zh_CN/vm/highmem.rst
blob: 018838e58c3e797420f26489a9ab22c6128f47f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/vm/highmem.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

:校译:

==========
高内存处理
==========

作者: Peter Zijlstra <a.p.zijlstra@chello.nl>

.. contents:: :local:

高内存是什么?
==============

当物理内存的大小接近或超过虚拟内存的最大大小时,就会使用高内存(highmem)。在这一点上,内
核不可能在任何时候都保持所有可用的物理内存的映射。这意味着内核需要开始使用它想访问的物理内
存的临时映射。

没有被永久映射覆盖的那部分(物理)内存就是我们所说的 "高内存"。对于这个边界的确切位置,有
各种架构上的限制。

例如,在i386架构中,我们选择将内核映射到每个进程的虚拟空间,这样我们就不必为内核的进入/退
出付出全部的TLB作废代价。这意味着可用的虚拟内存空间(i386上为4GiB)必须在用户和内核空间之
间进行划分。

使用这种方法的架构的传统分配方式是3:1,3GiB用于用户空间,顶部的1GiB用于内核空间。::

		+--------+ 0xffffffff
		| Kernel |
		+--------+ 0xc0000000
		|        |
		| User   |
		|        |
		+--------+ 0x00000000

这意味着内核在任何时候最多可以映射1GiB的物理内存,但是由于我们需要虚拟地址空间来做其他事
情--包括访问其余物理内存的临时映射--实际的直接映射通常会更少(通常在~896MiB左右)。

其他有mm上下文标签的TLB的架构可以有独立的内核和用户映射。然而,一些硬件(如一些ARM)在使
用mm上下文标签时,其虚拟空间有限。


临时虚拟映射
============

内核包含几种创建临时映射的方法。:

* vmap().  这可以用来将多个物理页长期映射到一个连续的虚拟空间。它需要synchronization
  来解除映射。

* kmap().  这允许对单个页面进行短期映射。它需要synchronization,但在一定程度上被摊销。
  当以嵌套方式使用时,它也很容易出现死锁,因此不建议在新代码中使用它。

* 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);

	/* 获得对该页内容的访问权。 */
	void *vaddr = kmap_atomic(page);

	/* 对该页的内容做一些处理。 */
	memset(vaddr, 0, PAGE_SIZE);

	/* 解除该页面的映射。 */
	kunmap_atomic(vaddr);

注意,kunmap_atomic()调用的是kmap_atomic()调用的结果而不是参数。

如果你需要映射两个页面,因为你想从一个页面复制到另一个页面,你需要保持kmap_atomic调用严
格嵌套,如::

	vaddr1 = kmap_atomic(page1);
	vaddr2 = kmap_atomic(page2);

	memcpy(vaddr1, vaddr2, PAGE_SIZE);

	kunmap_atomic(vaddr2);
	kunmap_atomic(vaddr1);


临时映射的成本
==============

创建临时映射的代价可能相当高。体系架构必须操作内核的页表、数据TLB和/或MMU的寄存器。

如果CONFIG_HIGHMEM没有被设置,那么内核会尝试用一点计算来创建映射,将页面结构地址转换成
指向页面内容的指针,而不是去捣鼓映射。在这种情况下,解映射操作可能是一个空操作。

如果CONFIG_MMU没有被设置,那么就不可能有临时映射和高内存。在这种情况下,也将使用计算方法。


i386 PAE
========

在某些情况下,i386 架构将允许你在 32 位机器上安装多达 64GiB 的内存。但这有一些后果:

* Linux需要为系统中的每个页面建立一个页帧结构,而且页帧需要驻在永久映射中,这意味着:

* 你最多可以有896M/sizeof(struct page)页帧;由于页结构体是32字节的,所以最终会有
  112G的页;然而,内核需要在内存中存储更多的页帧......

* PAE使你的页表变大--这使系统变慢,因为更多的数据需要在TLB填充等方面被访问。一个好处
  是,PAE有更多的PTE位,可以提供像NX和PAT这样的高级功能。

一般的建议是,你不要在32位机器上使用超过8GiB的空间--尽管更多的空间可能对你和你的工作
量有用,但你几乎是靠你自己--不要指望内核开发者真的会很关心事情的进展情况。