Linux内核中kmalloc()功能

6
我在LDD书籍中找到了一个方法,可以使用kmalloc函数从高内存中分配空间。我有一个基本问题: 1)据我所知,除非通过kmap()将高内存映射到内核空间,否则无法直接从内核访问高内存。但是我没有看到为kmalloc()保留任何映射区域,而对于vmalloc()来说,存在这样的映射区域。那么如果从高内存中分配,kmalloc()将映射到内核地址的哪个部分? 此问题针对 x86 架构下的32位系统。

如果我理解正确的话,“kmalloc”不会从所有内存中分配,而是从已经为内核分配的内存中分配,而“vmalloc”则与用户空间应用程序相同地从虚拟内存中分配内存。 - Shahbaz
kmalloc() 通过 slab/slub 等方式从物理连续的范围进行分配。物理到虚拟映射是 一对一 的。对于 vmalloc(),为每个页面分配 MMU/PTE 值;物理到虚拟映射不连续。高内存是通常与 Intel x86 架构相关联的概念。如果您的查询明确与 [tag:x86] 相关,请指定。Linux 可在大约30种不同的架构上运行。 - artless noise
架构应将所有物理内存映射到虚拟范围;在最新的ARM Linux中,这被称为lowmem。分配给用户进程或vmalloc等的所有内存都别名为这些物理块。宏virt_to_phys()phys_to_virt()可以转换此范围内的地址(kmalloc()或内核代码空间,即lowmem)。底层MMU可能会使用与4k页面不同的东西来分配此范围;例如,在ARM上使用1MB节。 - artless noise
1个回答

2

我的知识可能已经过时,但堆栈大致如下:

kmalloc通过调用get_free_pages(这就是首字母缩写GFP的含义)来分配物理连续内存。传递给kmallocGFP_*标志最终会传递给页分配器get_free_pages

由于高内存页面需要特殊处理,因此除非在请求中添加GFP_HIGHMEM标志,否则您将无法获得它们。

Linux中的所有内存都是虚拟的(这是一个不完全正确且与体系结构有关的概括,但让我们继续使用它,直到本段落中的下一个括号陈述为止)。然而,有一段内存不受虚拟化的影响,即不会重新映射页面:它只是虚拟地址和物理地址之间的线性映射。由get_free_pages分配的内存是线性映射的,除了高内存。(在某些体系结构上,线性映射支持没有使用MMU的内存范围:这只是逻辑地址到物理地址的简单算术转换:添加一个位移。在其他体系结构上,使用MMU进行线性映射。)

无论如何,如果您调用get_free_pages(直接或通过kmalloc)来分配两个或多个页面,则必须找到物理连续的页面。

现在,虚拟内存也是基于get_free_pages实现的,因为我们可以采取以这种方式分配的页面,并将其安装到虚拟地址空间中。

这就是mmap和用户空间中的所有其他内容的工作原理。当提交一块虚拟内存(由页面错误或其他原因支持物理页面)时,页面来自get_free_pages。除非该页面是高内存,否则它具有线性映射,以使其在内核中可见。此外,它被连接到正在进行请求的虚拟地址空间中。一些内核数据结构跟踪此情况,当然,它被插入到页表中,因此MMU会使其发生。

vmalloc在原理上类似于mmap,但要简单得多,因为它不涉及多个后端(具有mmap虚拟函数的文件系统中的设备)并且不处理像mmap允许的映射的合并和拆分等问题。 vmalloc区域由一段保留的虚拟地址范围组成,仅对内核可见(其基址取决于体系结构,可以在内核编译时进行调整)。vmalloc分配器划分此虚拟空间,并使用来自get_free_pages的页面填充它。这些页面不需要连续,因此可以逐个获取,并连接到分配的虚拟空间中。

高内存页面是指物理内存,无法在内核的线性映射中表示。高内存存在的原因是内核的线性“窗口”不能覆盖所有内存。例如,假设您有1GB的窗口,但有4GB的RAM。因此,为了覆盖所有内存,除了线性映射外,还有一些较小的“非线性”映射,其中页面可以使用和临时地选择性地显示。将页面放入此视图被认为是获取宝贵资源的行为,必须节俭使用并尽快释放。
高内存页面可以像其他页面一样安装到虚拟内存映射中,并且不需要特殊的“高内存”处理。可以用于任何映射:进程的映射或vmalloc范围的映射。
如果您正在处理一些可能混合了高内存和非高内存页面的虚拟内存,必须准备好使用映射函数来查看它们。

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