什么数据结构使用了1GB Linux内核空间中的128MB?

5
在我读过的有关Linux内核中HIGHMEM的书籍和文章中,它们都说在使用3:1分割时,并非所有1GB的空间都可用于内核映射。通常只有约896MB可以用于内核数据结构、内存映射、页表等。
我的问题是,这些数据结构究竟是什么?页表通常通过页表地址寄存器访问,对吗?而页表的基址通常被存储为物理地址。那么为什么需要为整个表保留一个虚拟地址空间呢?
同样地,我也了解到内核代码本身占据空间。这与虚拟地址空间有什么关系?难道不是物理内存用于存储代码吗?
最后,为什么这些数据结构必须要保留128MB的空间呢?为什么不能像内核中的其他正常数据结构一样,根据需要从整个1GB地址空间中使用?
我已经阅读了《Linux设备驱动程序》第三版、《专业Linux内核架构》以及这里的几篇文章(例如:为什么Linux内核ZONE_NORMAL限制为896 MB?)和旧版LWN文章,但没有找到具体的信息。
2个回答

4
关于页表,如果页面表本身没有映射到虚拟地址空间,MMU并不关心。对于地址转换来说,这是可以的。但是当内核需要修改页面表时,它们必须映射到虚拟地址空间中,而内核不能“即时”地映射它们,因为它需要修改页面表本身才能这样做。这是一个鸡生蛋的问题,这意味着页面表需要始终保持映射状态。
内核代码也存在类似的问题。为了使代码执行,必须将其映射到虚拟地址空间中。如果用于页面表修改的代码本身不存在,我们将面临类似的鸡生蛋问题。因此,更容易的方法是始终保留整个内核代码的映射,以及内核模式堆栈和任何由代码访问的内核数据结构,您不希望可能出现页面故障的情况。其中一种大型数据结构的示例是表示每个物理内存页面的struct page结构数组。

1

128MB 的保留空间并不是为了特定的数据结构,而是用于虚拟内存,为可能使用它的各种用户保留。通常情况下,并不会全部使用。

关于物理内存和虚拟内存:每个分配都需要三个东西——一个物理页、一个虚拟页和连接两者的映射。Linux 几乎从不直接使用物理地址,它总是通过虚拟地址转换来实现。对于大多数内核内存分配(称为 lowmem),这种转换非常简单——从虚拟地址中减去一些常量即可得到物理地址。但仍然使用虚拟地址。

Linux 的内存管理是在虚拟内存空间(4GB)比物理内存大得多的时候编写的,即使在最大的机器上也是如此。在这种情况下,浪费虚拟地址并不是问题。但今天,当物理内存很大时,这会导致低效和问题。

vmalloc虚拟地址范围可被任何调用vmalloc的用户使用。例如:
1. 加载内核驱动程序(使用modprobeinsmod)。
2. 内核模块通常使用vmalloc进行分配。曾经使用的另一函数kmalloc仅限于128K,并将大小舍入为2的幂,因此对于大型分配,通常更喜欢使用vmalloc


如果没有为特定数据结构保留128MB,那么为什么我们不能将整个1G内核虚拟空间映射到某个虚拟地址?这样,如果系统恰好有1GB物理内存,则无需使用HIGHMEM。在基于896MB的设置中,需要使用HIGHMEM才能利用任何大于896MB的物理内存。同样,当我们为某些内容保留了128MB(甚至不使用它),那么这么多的虚拟地址范围就无法用于vmalloc。正如你所说,所有内容都通过虚拟地址访问。那么为什么要浪费任何虚拟地址呢? - codetwiddler
@codetwiddler,这很复杂...浪费虚拟地址是不好的,但也不是毫无意义。基本点是有两种方法将虚拟地址连接到物理地址——直接映射(lowmem)和任意动态映射(vmalloc)。每个方法都必须使用单独的虚拟地址范围,以避免冲突。 - ugoren
在所有方面都达成一致,我知道有两种方法及其用途。现在,具体来说,如果我有1GB的物理内存,我需要使用HIGHMEM来访问超过896MB的内存,因为1GB-896MB的块被保留了。那个保留区域的目的是什么,无论多么复杂?该保留区域应该与某些结构或指向该空间的指针相关联,并且我认为如果该保留是静态的,则它应该是相当重要的实体。 - codetwiddler
另外,在您之前的回答中,您说“通常情况下,并不是全部都被使用”,而在这里您又说“但也不是毫无意义”。不幸的是,这对我来说有点令人困惑。 - codetwiddler
在这里详细解释lowmem/vmalloc对我来说太多了。抱歉。关于你的困惑-紧急出口通常不会使用,但它们不是无用的。128M的vmalloc空间通常也不会被使用。然而,为所有意外配置保留它非常重要。 - ugoren

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接