目录


VMALLOC 分配器原理

VMALLOC 内存分配器称为 “Virtual Memory Allocator”, 从定义可以知道 VMALLOC 内存的主要任务就是分配虚拟内存。在 Linux 内核中,划分了一块虚拟内存区域给 VMALLOC 内存分配器进行管理, VMALLOC 分配器提供相应的函数将这段虚拟内存分配 给内核其他子系统. 在不同的体系结构中 VMALLOC 内存管理器管理的虚拟内存区域 可能不同,但可以通过 VMALLOC_START 和 VMALLOC_END 进行确认.

VMALLOC 内存分配器分配虚拟内存之后,虚拟内存还没有与物理内存进行映射,因此 VMALLOC 内存需要和物理页进行映射之后才算真正的从 VMALLOC 分配器中分配可用 的虚拟内存。物理页可用通过 Buddy 内存管理器分配,也可以通过 PCP 内存管理器 进行分配,物理内存可以来自 ZONE_HIGHMEM,也可以来自非 ZONE_HIGHMEM 的区间。 由于 VMALLOC 分配器可以分配连续的虚拟地址,但与其映射的物理地址可能不是连续 的物理页块,也可能是连续的物理页块,因此通俗称 VMALLOC 内存分配器分配的内存 特点是 “虚拟地址连续,物理地址不一定连续的内存”。

VMALLOC 分配器的历史上采用过多种数据结构管理 VMALLOC 虚拟区虚拟地址的分配 和回收,比如最新版本采用红黑树管理 VMALLOC 虚拟区域的分配和回收,而在较早 的内核中,只是采用简单的遍历来查找 VMALLOC 虚拟区域内可用的虚拟内存块和回收 虚拟内存区块. 当无论采用什么方法管理 VMALLOc 虚拟区域,VMALLOC 内存管理器 总要保证能从 VMALLOC 区域内进行虚拟地址的分配和释放。

当 VMALLOC 内存分配器从 VMALLOC 虚拟区域内找到一块可用的虚拟区域之后,都会 在指定虚拟内存之后加上一个 PAGE_SIZE 的虚拟内存,这块虚拟内存称为 VMALLOC 的 GUARD 虚拟内存块,主要是用于隔离其他已经分配的虚拟内存区块. 虚拟地址分配 完毕之后,VMALLOC 内存管理器就从 Buddy 或者 PCP 内存管理器中分配对应数量的 物理页,然后将虚拟内存和物理内存建立页表。VMALLOC 分配器建立的页表可以是三级 页表,也可以高达五级页表。以上操作完毕之后,VMALLOC 分配器将虚拟地址传递给 调用者,调用者就可以使用这段虚拟内存区域. 由于 VMALLOC 按 PAGE_SIZE 为单位 从 Buddy/PCP 内存管理器中分配物理内存,并且 VMALLOC 分配虚拟地址时按 PAGE_SIZE 大小进行对齐,因此 VMALLOC 内存分配器分配以 PAGE_SIZE 为单位的虚拟内存块。 VMALLOC 内存分配器提供的分配接口如下:

void *vmalloc(unsigned long size)
void *vmalloc_32(unsigned long size)
void *vmalloc_node(unsigned long size, int node)
void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot, int node)

当使用者使用完 VMALLOC 内存分配器分配的内存之后,通过 VMALLOC 释放函数将虚拟 地址传递给 VMALLOC 分配器,VMALLOC 分配器执行与分配相反的动作,首先将虚拟地址 与物理地址关联的页表清除,然后释放对应的物理页块给 Buddy 内存管理器或者 PCP 内存管理器, 执行完毕之后 VMALLOC 释放才完成. VMALLOC 内存分配器提供的释放 接口如下:

void vfree(void *addr)
VMALLOC 页表

当从 VMALLOC 分配器获得一段 VMALLOc 虚拟内存块之后,接下来是分配一段物理内存, 并建立 VMALLOC 虚拟地址与物理地址之间的映射关系,即建立页表. VMALLOC 内存 分配器可以支持多级页表,例如 linux 2.6 版本的 VMALLOC 分配器支持如下页表:

VMALLOC 分配器通过建立 4 级页表最终将物理内存和 VMALLOC 虚拟内存建立映射 关系. Linux 5.x 版本的 VMALLOC 分配器支持如下页表:

Linux 2.6 版本的 VMALLOC

在 Linux 2.6 版本上,VMALLOC 使用 struct vm_struct 结构维护一段 VMALLOC 虚拟内存块,定义如下:

struct vm_struct {
        void                    *addr;
        unsigned long           size;
        unsigned long           flags;
        struct page             **pages;
        unsigned int            nr_pages;
        unsigned long           phys_addr;
        struct vm_struct        *next;
};

VMALLOC 内存管理器通过建立一个 struct vm_struct 结构的 vmlist 链表。当 VMALLOC 从 VMALLOC 虚拟内存区域找一块可用内存时,遍历 vmlist 链表,以此确认找到 的虚拟内存区块未使用,然后将其加入到 vmlist 里。当释放 VMALLOC 虚拟块的 时候,VMALLOC 分配器将其对应的 struct vm_struct 从 vmlist 链表中移除.

Linux 5.x 版本的 VMALLOC

在 Linux 5.x 版本上,VMALLOC 使用一颗红黑树管理着所有的 VMALLOC 虚拟内存。 当 VMALLOC 管理器需要分配虚拟内存时,VMALLOc 从红黑树上查找一块可用的虚拟 内存。当释放时,VMALLOC 分配器再将虚拟地址插入到红黑树中.


VMALLOC 的优点

当系统运行一段时间后,线性映射区已经很难找到连续的虚拟内存区域了,这时可以 从 VMALLOC 区域获得连续的物理内存。

VMALLOC 的缺点

由于要动态建立页表,分配虚拟内存的效率远低于 Slab/Slub 等线性区分配器.


VMALLOC 分配器使用


基础用法介绍

VMALLOC 分配器提供了用于分配和释放虚拟内存的相关函数接口:

VMALLOC 分配
void *vmalloc(unsigned long size)
void *vmalloc_32(unsigned long size)
void *vmalloc_node(unsigned long size, int node)
void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot, int node)
VMALLOC 释放
void vfree(void *addr)

具体函数解析说明,请查看:


VMALLOC 分配虚拟内存

从 VMALLOC 区域内存分配、使用和释放虚拟内存,开发者可以参考如下代码:

#include <linux/mm.h>
#include <linux/vmalloc.h>

static int TestCase_vmalloc(void)
{
        void *data;

        /* Alloc */
        data = vmalloc(PAGE_SIZE);
        sprintf((char *)data, "BiscuitOS-%s", __func__);
        printk("[%#lx] %s\n", (unsigned long)data, (char *)data);

        /* free */
        vfree(data);
        return 0;
}


VMALLOC 分配器实践


实践准备

本实践是基于 BiscuitOS Linux 5.0 ARM32 环境进行搭建,因此开发者首先 准备实践环境,请查看如下文档进行搭建:


实践部署

准备好基础开发环境之后,开发者接下来部署项目所需的开发环境。由于项目 支持多个版本的 VMALLOC,开发者可以根据需求进行选择,本文以 linux 2.6.12 版本的 VMALLOC 进行讲解。开发者使用如下命令:

cd BiscuitOS/
make linux-5.0-arm32_defconfig
make menuconfig

选择并进入 “[*] Package —>” 目录。

选择并进入 “[*] Memory Development History —>” 目录。

选择并进入 “[*] VMALLOC Allocator —>” 目录。

选择 “[*] VMALLOC on linux 2.6.12 —>” 目录,保存并退出。接着执行如下命令:

make

成功之后将出现上图的内容,接下来开发者执行如下命令以便切换到项目的路径:

cd BiscuitOS/output/linux-5.0-arm32/package/BiscuitOS_VMALLOC-2.6.12
make download

至此源码已经下载完成,开发者可以使用 tree 等工具查看源码:

arch 目录下包含内存初始化早期,与体系结构相关的处理部分。mm 目录下面包含 了与各个内存分配器和内存管理行为相关的代码。init 目录下是整个模块的初始化 入口流程。modules 目录下包含了内存分配器的使用例程和测试代码. fs 目录下 包含了内存管理信息输出到文件系统的相关实现。入口函数是 init/main.c 的 start_kernel()。

如果你是第一次使用这个项目,需要修改 DTS 的内容。如果不是可以跳到下一节。 开发者参考源码目录里面的 “BiscuitOS.dts” 文件,将文件中描述的内容添加 到系统的 DTS 里面,”BiscuitOS.dts” 里的内容用来从系统中预留 100MB 的物理 内存供项目使用,具体如下:

开发者将 “BiscuitOS.dts” 的内容添加到:

BiscuitOS/output/linux-5.0-arm32/linux/linux/arch/arm/boot/dts/vexpress-v2p-ca9.dts

添加完毕之后,使用如下命令更新 DTS:

cd BiscuitOS/output/linux-5.0-arm32/package/BiscuitOS_VMALLOC-2.6.12
make kernel


实践执行

环境部署完毕之后,开发者可以向通用模块一样对源码进行编译和安装使用,使用 如下命令:

cd BiscuitOS/output/linux-5.0-arm32/package/BiscuitOS_VMALLOC-2.6.12
make

以上就是模块成功编译,接下来将 ko 模块安装到 BiscuitOS 中,使用如下命令:

cd BiscuitOS/output/linux-5.0-arm32/package/BiscuitOS_VMALLOC-2.6.12
make install
make pack

以上准备完毕之后,最后就是在 BiscuitOS 运行这个模块了,使用如下命令:

cd BiscuitOS/output/linux-5.0-arm32/package/BiscuitOS_VMALLOC-2.6.12
make run

在 BiscuitOS 中插入了模块 “BiscuitOS_VMALLOC-2.6.12.ko”,打印如上信息,那么 BiscuitOS Memory Manager Unit History 项目的内存管理子系统已经可以使用。


测试建议

BiscuitOS Memory Manager Unit History 项目提供了大量的测试用例用于测试 不同内存分配器的功能。结合项目提供的 initcall 机制,项目将测试用例分作 两类,第一类类似于内核源码树内编译,也就是同 MMU 子系统一同编译的源码。 第二类类似于模块编译,是在 MMU 模块加载之后独立加载的模块。以上两种方案 皆在最大程度的测试内存管理器的功能。

要在项目中使用以上两种测试代码,开发者可以通过项目提供的 Makefile 进行 配置。以 linux 2.6.12 为例, Makefile 的位置如下:

/xspace/OpenSource/BiscuitOS/BiscuitOS/output/linux-5.0-arm32/package/BiscuitOS_VMALLOC-2.6.12/BiscuitOS_VMALLOC-2.6.12/Makefile

Makefile 内提供了两种方案的编译开关,例如需要使用打开 buddy 内存管理器的 源码树内部调试功能,需要保证 Makefile 内下面语句不被注释:

$(MODULE_NAME)-m                += modules/buddy/main.o

如果要关闭 buddy 内存管理器的源码树内部调试功能,可以将其注释:

# $(MODULE_NAME)-m                += modules/buddy/main.o

同理,需要打开 buddy 模块测试功能,可以参照下面的代码:

obj-m                             += $(MODULE_NAME)-buddy.o
$(MODULE_NAME)-buddy-m            := modules/buddy/module.o

如果不需要 buddy 模块测试功能,可以参考下面代码, 将其注释:

# obj-m                             += $(MODULE_NAME)-buddy.o
# $(MODULE_NAME)-buddy-m            := modules/buddy/module.o

在上面的例子中,例如打开了 buddy 的模块调试功能,重新编译模块并在 BiscuitOS 上运行,如下图,可以在 “lib/module/5.0.0/extra/” 目录下看到两个模块:

然后先向 BiscuitOS 中插入 “BiscuitOS_VMALLOC-2.6.12.ko” 模块,然后再插入 “BiscuitOS_VMALLOC-2.6.12-buddy.ko” 模块。如下:

以上便是测试代码的使用办法。开发者如果想在源码中启用或关闭某些宏,可以 修改 Makefile 中内容:

从上图可以知道,如果要启用某些宏,可以在 ccflags-y 中添加 “-D” 选项进行 启用,源码的编译参数也可以添加到 ccflags-y 中去。开发者除了使用上面的办法 进行测试之外,也可以使用项目提供的 initcall 机制进行调试,具体请参考:

Initcall 机制提供了以下函数用于 VMALLOC 调试:

vmalloc_initcall_bs()

从项目的 Initcall 机制可以知道,vmalloc_initcall_bs() 调用的函数将 在 VMALLOC 分配器初始化完毕之后自动调用。VMALLOC 相关的测试代码位于:

BiscuitOS/output/linux-5.0-arm32/package/BiscuitOS_VMALLOC-2.6.12/BiscuitOS_VMALLOC-2.6.12/module/vmalloc/

在 Makefile 中打开调试开关:

$(MODULE_NAME)-m                += modules/vmalloc/main.o

VMALLOC 测试代码也包含模块测试,在 Makefile 中打开调试开关:

obj-m                           += $(MODULE_NAME)-vmalloc.o
$(MODULE_NAME)-vmalloc-m        := modules/vmalloc/module.o

VMALLOC 模块测试结果如下:


VMALLOC 历史补丁


VMALLOC Linux 2.6.12

Linux 2.6.12 采用 VMALLOC 分配器管理 VMALLOC 虚拟内存区域。

VMALLOC 分配
void *vmalloc(unsigned long size)
void *vmalloc_32(unsigned long size)
void *vmalloc_node(unsigned long size, int node)
void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot, int node)
VMALLOC 释放
void vfree(void *addr)

具体函数解析说明,请查看:

与项目相关

VMALLOC 内存分配器与本项目相关的 vmalloc() 调用顺序如下:

VMALLOC 内存分配器与本项目相关的 vfree() 调用顺序如下:

项目中虚拟内存布局如下:

在项目中,VMALLOC 虚拟内存的管理的范围是: 0x9440A000 到 0x95E0A000.

补丁

对于 Linux 2.6.12 的补丁,Linus 将 Linux 内核源码树加入到 git 中来,因此 这个版本的代码均不产生补丁。更多补丁的使用请参考:


VMALLOC Linux 2.6.12.1

Linux 2.6.12.1 采用 VMALLOC 分配器管理 VMALLOC 虚拟内存区域。

VMALLOC 分配
void *vmalloc(unsigned long size)
void *vmalloc_32(unsigned long size)
void *vmalloc_node(unsigned long size, int node)
void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot, int node)
VMALLOC 释放
void vfree(void *addr)

具体函数解析说明,请查看:

与项目相关

VMALLOC 内存分配器与本项目相关的 vmalloc() 调用顺序如下:

VMALLOC 内存分配器与本项目相关的 vfree() 调用顺序如下:

项目中虚拟内存布局如下:

在项目中,VMALLOC 虚拟内存的管理的范围是: 0x9440A000 到 0x95E0A000.

补丁

与 Linux 2.6.12 的相比,VMALLOC 并为产生补丁。更多补丁的使用请参考:


VMALLOC Linux 2.6.12.2

Linux 2.6.12.2 采用 VMALLOC 分配器管理 VMALLOC 虚拟内存区域。

VMALLOC 分配
void *vmalloc(unsigned long size)
void *vmalloc_32(unsigned long size)
void *vmalloc_node(unsigned long size, int node)
void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot, int node)
VMALLOC 释放
void vfree(void *addr)

具体函数解析说明,请查看:

与项目相关

VMALLOC 内存分配器与本项目相关的 vmalloc() 调用顺序如下:

VMALLOC 内存分配器与本项目相关的 vfree() 调用顺序如下:

项目中虚拟内存布局如下:

在项目中,VMALLOC 虚拟内存的管理的范围是: 0x9440A000 到 0x95E0A000.

补丁

与 Linux 2.6.12.1 的相比,VMALLOC 并为产生补丁。更多补丁的使用请参考:


VMALLOC Linux 2.6.12.3

Linux 2.6.12.3 采用 VMALLOC 分配器管理 VMALLOC 虚拟内存区域。

VMALLOC 分配
void *vmalloc(unsigned long size)
void *vmalloc_32(unsigned long size)
void *vmalloc_node(unsigned long size, int node)
void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot, int node)
VMALLOC 释放
void vfree(void *addr)

具体函数解析说明,请查看:

与项目相关

VMALLOC 内存分配器与本项目相关的 vmalloc() 调用顺序如下:

VMALLOC 内存分配器与本项目相关的 vfree() 调用顺序如下:

项目中虚拟内存布局如下:

在项目中,VMALLOC 虚拟内存的管理的范围是: 0x9440A000 到 0x95E0A000.

补丁

与 Linux 2.6.12.2 的相比,VMALLOC 并为产生补丁。更多补丁的使用请参考:


VMALLOC Linux 2.6.12.4

Linux 2.6.12.4 采用 VMALLOC 分配器管理 VMALLOC 虚拟内存区域。

VMALLOC 分配
void *vmalloc(unsigned long size)
void *vmalloc_32(unsigned long size)
void *vmalloc_node(unsigned long size, int node)
void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot, int node)
VMALLOC 释放
void vfree(void *addr)

具体函数解析说明,请查看:

与项目相关

VMALLOC 内存分配器与本项目相关的 vmalloc() 调用顺序如下:

VMALLOC 内存分配器与本项目相关的 vfree() 调用顺序如下:

项目中虚拟内存布局如下:

在项目中,VMALLOC 虚拟内存的管理的范围是: 0x9440A000 到 0x95E0A000.

补丁

与 Linux 2.6.12.3 的相比,VMALLOC 并为产生补丁。更多补丁的使用请参考:


VMALLOC Linux 2.6.12.5

Linux 2.6.12.5 采用 VMALLOC 分配器管理 VMALLOC 虚拟内存区域。

VMALLOC 分配
void *vmalloc(unsigned long size)
void *vmalloc_32(unsigned long size)
void *vmalloc_node(unsigned long size, int node)
void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot, int node)
VMALLOC 释放
void vfree(void *addr)

具体函数解析说明,请查看:

与项目相关

VMALLOC 内存分配器与本项目相关的 vmalloc() 调用顺序如下:

VMALLOC 内存分配器与本项目相关的 vfree() 调用顺序如下:

项目中虚拟内存布局如下:

在项目中,VMALLOC 虚拟内存的管理的范围是: 0x9440A000 到 0x95E0A000.

补丁

与 Linux 2.6.12.4 的相比,VMALLOC 并为产生补丁。更多补丁的使用请参考:


VMALLOC Linux 2.6.12.6

Linux 2.6.12.6 采用 VMALLOC 分配器管理 VMALLOC 虚拟内存区域。

VMALLOC 分配
void *vmalloc(unsigned long size)
void *vmalloc_32(unsigned long size)
void *vmalloc_node(unsigned long size, int node)
void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot, int node)
VMALLOC 释放
void vfree(void *addr)

具体函数解析说明,请查看:

与项目相关

VMALLOC 内存分配器与本项目相关的 vmalloc() 调用顺序如下:

VMALLOC 内存分配器与本项目相关的 vfree() 调用顺序如下:

项目中虚拟内存布局如下:

在项目中,VMALLOC 虚拟内存的管理的范围是: 0x9440A000 到 0x95E0A000.

补丁

与 Linux 2.6.12.5 的相比,VMALLOC 并为产生补丁。更多补丁的使用请参考:


VMALLOC Linux 2.6.13

Linux 2.6.13 采用 VMALLOC 分配器管理 VMALLOC 虚拟内存区域。

VMALLOC 分配
void *vmalloc(unsigned long size)
void *vmalloc_32(unsigned long size)
void *vmalloc_node(unsigned long size, int node)
void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot, int node)
VMALLOC 释放
void vfree(void *addr)

具体函数解析说明,请查看:

与项目相关

VMALLOC 内存分配器与本项目相关的 vmalloc() 调用顺序如下:

VMALLOC 内存分配器与本项目相关的 vfree() 调用顺序如下:

项目中虚拟内存布局如下:

在项目中,VMALLOC 虚拟内存的管理的范围是: 0x9440A000 到 0x95E0A000.

补丁

相对上一个版本 linux 2.6.12.6,VMALLOC 内存分配器增加了多个补丁,如下:

tig mm/vmalloc.c include/linux/vmalloc.h

2005-05-01 08:59 Pavel Pisa     o [PATCH] DocBook: changes and extensions to the kernel documentation
                                  [main] 4dc3b16ba18c0f967ad100c52fa65b01a4f76ff0
2005-05-20 14:27 Andi Kleen     o [PATCH] x86_64: Fixed guard page handling again in iounmap
                                  [main] 7856dfeb23c16ef3d8dac8871b4d5b93c70b59b9

git format-patch -1 4dc3b16ba18c0f967ad100c52fa65b01a4f76ff0
vi 0001-PATCH-DocBook-changes-and-extensions-to-the-kernel-d.patch

该补丁新增了页表的权限 PAGE_KERNEL_EXEC, 使分配的虚拟内存拥有执行和读写权限.

git format-patch -1 7856dfeb23c16ef3d8dac8871b4d5b93c70b59b9
vi 0001-PATCH-x86_64-Fixed-guard-page-handling-again-in-ioun.patch 

该补丁新增了 __remove_vm_area() 函数并更新了 remove_vm_area() 函数实现. 更多补丁使用方法请参考下面文章:


VMALLOC Linux 2.6.13.1

Linux 2.6.13.1 采用 VMALLOC 分配器管理 VMALLOC 虚拟内存区域。

VMALLOC 分配
void *vmalloc(unsigned long size)
void *vmalloc_32(unsigned long size)
void *vmalloc_node(unsigned long size, int node)
void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot, int node)
VMALLOC 释放
void vfree(void *addr)

具体函数解析说明,请查看:

与项目相关

VMALLOC 内存分配器与本项目相关的 vmalloc() 调用顺序如下:

VMALLOC 内存分配器与本项目相关的 vfree() 调用顺序如下:

项目中虚拟内存布局如下:

在项目中,VMALLOC 虚拟内存的管理的范围是: 0x9440A000 到 0x95E0A000.

补丁

与 Linux 2.6.13 的相比,VMALLOC 并为产生补丁。更多补丁的使用请参考:


VMALLOC Linux 2.6.14

Linux 2.6.14 采用 VMALLOC 分配器管理 VMALLOC 虚拟内存区域。

VMALLOC 分配
void *vmalloc(unsigned long size)
void *vmalloc_32(unsigned long size)
void *vmalloc_node(unsigned long size, int node)
void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot, int node)
VMALLOC 释放
void vfree(void *addr)

具体函数解析说明,请查看:

与项目相关

VMALLOC 内存分配器与本项目相关的 vmalloc() 调用顺序如下:

VMALLOC 内存分配器与本项目相关的 vfree() 调用顺序如下:

项目中虚拟内存布局如下:

在项目中,VMALLOC 虚拟内存的管理的范围是: 0x9440A000 到 0x95E0A000.

补丁

相对上一个版本 linux 2.6.13.1,VMALLOC 内存分配器增加了多个补丁,如下:

tig mm/vmalloc.c include/linux/vmalloc.h

2005-09-03 15:54 Deepak Saxena  o [PATCH] arm: allow for arch-specific IOREMAP_MAX_ORDER
                                  [main] fd195c49fb17a21e232f50bddb2267150053cf34
2005-09-09 13:10 Pekka Enberg   o [PATCH] update kfree, vfree, and vunmap kerneldoc
                                  [main] 80e93effce55044c5a7fa96e8b313640a80bd4e9
2005-10-07 07:46 Al Viro        o [PATCH] gfp flags annotations - part 1
                                  [main] dd0fc66fb33cd610bc1a5db8a5e232d34879b4d7

git format-patch -1 fd195c49fb17a21e232f50bddb2267150053cf34
vi 0001-PATCH-arm-allow-for-arch-specific-IOREMAP_MAX_ORDER.patch

该补丁将 IOREMAP_MAX_ORDER 宏移到 vmalloc.h.

git format-patch -1 80e93effce55044c5a7fa96e8b313640a80bd4e9
vi 0001-PATCH-update-kfree-vfree-and-vunmap-kerneldoc.patch

该补丁修改了相关的注释.

git format-patch -1 dd0fc66fb33cd610bc1a5db8a5e232d34879b4d7
vi 0001-PATCH-gfp-flags-annotations-part-1.patch

该补丁将 VMALLOC 分配器中 gfp 标志的类型替换成 gfp_t. 更多不行请参考下文:


VMALLOC Linux 2.6.15

Linux 2.6.15 采用 VMALLOC 分配器管理 VMALLOC 虚拟内存区域。

VMALLOC 分配
void *vmalloc(unsigned long size)
void *vmalloc_32(unsigned long size)
void *vmalloc_node(unsigned long size, int node)
void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot, int node)
VMALLOC 释放
void vfree(void *addr)

具体函数解析说明,请查看:

与项目相关

VMALLOC 内存分配器与本项目相关的 vmalloc() 调用顺序如下:

VMALLOC 内存分配器与本项目相关的 vfree() 调用顺序如下:

项目中虚拟内存布局如下:

在项目中,VMALLOC 虚拟内存的管理的范围是: 0x9440A000 到 0x95E0A000.

补丁

相对上一个版本 linux 2.6.14,VMALLOC 内存分配器增加了多个补丁,如下:

tig mm/vmalloc.c include/linux/vmalloc.h

2005-10-29 18:15 Christoph Lameter o [PATCH] vmalloc_node
                                     [main] 930fc45a49ddebe7555cc5c837d82b9c27e65ff4
2005-10-29 18:16 Hugh Dickins      o [PATCH] mm: init_mm without ptlock
                                     [main] 872fec16d9a0ed3b75b8893aa217e49cca575ee5
2005-11-07 01:01 Randy Dunlap      o [PATCH] kernel-doc: fix warnings in vmalloc.c
                                     [main] d44e0780bcc47c9b8851099c0dfc1dda3c9db5a9

git format-patch -1 930fc45a49ddebe7555cc5c837d82b9c27e65ff4
vi 0001-PATCH-vmalloc_node.patch

新增 vmalloc_node() 和 __vmalloc_node() 函数, 支持从指定的 NODE 上分配 VMALLOC 内存.

git format-patch -1 872fec16d9a0ed3b75b8893aa217e49cca575ee5
vi 0001-PATCH-mm-init_mm-without-ptlock.patch

该补丁用于在 VMALLOC 建立 PTE 页表时移除了 page_table_lock 锁.

git format-patch -1 d44e0780bcc47c9b8851099c0dfc1dda3c9db5a9
vi 0001-PATCH-kernel-doc-fix-warnings-in-vmalloc.c.patch

该补丁在 VMALLOC 分配器中添加了 node 相关的注释. 更多补丁使用请参考下文:


VMALLOC 历史时间轴


VMALLOC API

__get_vm_area
struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags,
                                unsigned long start, unsigned long end)
  作用: 分配一段可用的 VMALLOC 虚拟内存.
get_vm_area
struct vm_struct *get_vm_area(unsigned long size, unsigned long flags)
  作用: 分配一段可用的 VMALLOC 虚拟内存.
__get_vm_area_node
struct vm_struct *__get_vm_area_node(unsigned long size, unsigned long flags,
                                unsigned long start, unsigned long end, int node)
  作用: 从指定节点上分配一段可用的 VMALLOC 虚拟内存.
get_vm_area_node
struct vm_struct *get_vm_area_node(unsigned long size, unsigned long flags, int node)
  作用: 从指定节点上分配一段可用的 VMALLOC 虚拟内存.
map_vm_area
int map_vm_area(struct vm_struct *area, pgprot_t prot, struct page ***pages)
  作用: 映射一段 VMALLOC 虚拟内存到物理内存上.
unmap_vm_area
void unmap_vm_area(struct vm_struct *area)
  作用: 解除一段 VMALLOC 虚拟内存的映射关系.
__remove_vm_area
struct vm_struct *__remove_vm_area(void *addr)
  作用: 释放一段 VMALLOC 虚拟内存到 VMALLOC 内存分配器.
remove_vm_area
struct vm_struct *remove_vm_area(void *addr)
  作用: 释放一段 VMALLOC 虚拟内存到 VMALLOC 内存分配器.
vfree
void vfree(void *addr)
  作用: 释放一段虚拟内存到 VMALLOC 分配器.
__vmalloc
void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
  作用: 从 VMALLOC 分配器中分配指定长度的 VMALLOC 虚拟内存.
vmalloc
void *vmalloc(unsigned long size)
  作用: 从 VMALLOC 分配器中分配 VMALLOC 虚拟内存.
vmalloc_32
void *vmalloc_32(unsigned long size)
  作用: 分配一段 32 位的 VMALLOC 虚拟内存.
__vmalloc_area
void *__vmalloc_area(struct vm_struct *area, gfp_t gfp_mask, pgprot_t prot)
  作用: VMALLOC 内存分配器从 VMALLOC 区域分配一段虚拟内存.
__vmalloc_area_node
void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
                                pgprot_t prot, int node)
  作用: VMALLOC 分配器从指定节点上分配一段 VMALLOC 虚拟内存块.
vmalloc_exec
void *vmalloc_exec(unsigned long size)
  作用: 分配一段可以执行的 VMALLOC 虚拟内存.
__vmalloc_node
void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot, int node)
  作用: VMALLOC 分配器从指定节点上分配 VMALLOC 虚拟内存.
vmalloc_node
void *vmalloc_node(unsigned long size, int node)
  作用: VMALLOC 分配器从指定节点上分配 VMALLOC 虚拟内存.
vmap
void *vmap(struct page **pages, unsigned int count,
                unsigned long flags, pgprot_t prot)
  作用: 将一段 VMALLOC 虚拟内存与物理内存进行映射.
vmap_pmd_range
static inline int vmap_pmd_range(pud_t *pud, unsigned long addr,
                        unsigned long end, pgprot_t prot, struct page ***pages)
  作用: 建立一段 VMALLOC 虚拟内存的 PMD 页表.
vmap_pte_range
static int vmap_pte_range(pmd_t *pmd, unsigned long addr,
                        unsigned long end, pgprot_t prot, struct page ***pages)
  作用: 建立一段 VMALLOC 虚拟内存的 PTE 页表.
vmap_pud_range
static inline int vmap_pud_range(pgd_t *pgd, unsigned long addr,
                        unsigned long end, pgprot_t prot, struct page ***pages)
  作用: 建立一段 VMALLOC 虚拟内存的 PUD 页表.
__vunmap
void __vunmap(void *addr, int deallocate_pages)
  作用: 解除一段 VMALLOC 虚拟内存的映射关系.
vunmap
void vunmap(void *addr)
  作用: 解除一段 VMALLOC 虚拟内存的映射关系.
vunmap_pmd_range
static inline void vunmap_pmd_range(pud_t *pud, unsigned long addr,
                                                unsigned long end)
  作用: 解除一段 VMALLOC 虚拟内存的 PMD 页表.
vunmap_pte_range
static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end)
  作用: 解除一段 VMALLOC 虚拟内存的 PTE 页表.
vunmap_pud_range
static inline void vunmap_pud_range(pgd_t *pgd, unsigned long addr,
                                                unsigned long end)
  作用: 解除一段 VMALLOC 虚拟内存的 PUD 页表.


VMALLOC 进阶研究


VMALLOC 内存分配器调试


附录

BiscuitOS Home

BiscuitOS Driver

Linux Kernel

Bootlin: Elixir Cross Referencer

捐赠一下吧 🙂

MMU