我的知识可能已经过时,但堆栈大致如下:
kmalloc
通过调用get_free_pages
(这就是首字母缩写GFP
的含义)来分配物理连续内存。传递给kmalloc
的GFP_*
标志最终会传递给页分配器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
的页面填充它。这些页面不需要连续,因此可以逐个获取,并连接到分配的虚拟空间中。
kmalloc()
通过 slab/slub 等方式从物理连续的范围进行分配。物理到虚拟映射是 一对一 的。对于vmalloc()
,为每个页面分配 MMU/PTE 值;物理到虚拟映射不连续。高内存是通常与 Intel x86 架构相关联的概念。如果您的查询明确与 [tag:x86] 相关,请指定。Linux 可在大约30种不同的架构上运行。 - artless noisevirt_to_phys()
和phys_to_virt()
可以转换此范围内的地址(kmalloc()或内核代码空间,即lowmem)。底层MMU可能会使用与4k页面不同的东西来分配此范围;例如,在ARM上使用1MB节。 - artless noise