Linux 6.6内核内存管理深度解析(十七):vmalloc — 内核要“虚拟连续“就够:物理页逐个凑,页表来连

引言:内核自己要 1MB 连续内存,buddy 却凑不出来

前面 #2 讲过 buddy 按 2 的幂给物理页,系统跑久了 4KB 空闲页东一块西一块,高阶连续块凑不出来(外部碎片)。用户态 mmap(#16)好办------按需分配 ,虚拟连续、物理页缺页时逐个给。但内核自己(模块加载、BPF 程序、per-CPU 大缓冲)有时也要 1MB 甚至更大的虚拟连续内存,却不想被"物理也必须连续"卡死。

vmalloc 的思路:只要虚拟地址连续,物理页可以逐个从 buddy 拿(order-0 单页也行),再用页表把不连续的物理页"连"成一段连续的虚拟地址。它解耦了"虚拟连续"和"物理连续"------这正是它和 kmalloc(#13,物理连续)的根本区别。

这条链路:vmalloc → __vmalloc_node_range 分配虚拟空间 → __vmalloc_area_node 逐个拿物理页 + 建页表映射 → 返回虚拟连续的内核地址。


全景图

#mermaid-svg-Fn2Omu9ncsjiuQVF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Fn2Omu9ncsjiuQVF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Fn2Omu9ncsjiuQVF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Fn2Omu9ncsjiuQVF .error-icon{fill:#552222;}#mermaid-svg-Fn2Omu9ncsjiuQVF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Fn2Omu9ncsjiuQVF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Fn2Omu9ncsjiuQVF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Fn2Omu9ncsjiuQVF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Fn2Omu9ncsjiuQVF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Fn2Omu9ncsjiuQVF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Fn2Omu9ncsjiuQVF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Fn2Omu9ncsjiuQVF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Fn2Omu9ncsjiuQVF .marker.cross{stroke:#333333;}#mermaid-svg-Fn2Omu9ncsjiuQVF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Fn2Omu9ncsjiuQVF p{margin:0;}#mermaid-svg-Fn2Omu9ncsjiuQVF .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Fn2Omu9ncsjiuQVF .cluster-label text{fill:#333;}#mermaid-svg-Fn2Omu9ncsjiuQVF .cluster-label span{color:#333;}#mermaid-svg-Fn2Omu9ncsjiuQVF .cluster-label span p{background-color:transparent;}#mermaid-svg-Fn2Omu9ncsjiuQVF .label text,#mermaid-svg-Fn2Omu9ncsjiuQVF span{fill:#333;color:#333;}#mermaid-svg-Fn2Omu9ncsjiuQVF .node rect,#mermaid-svg-Fn2Omu9ncsjiuQVF .node circle,#mermaid-svg-Fn2Omu9ncsjiuQVF .node ellipse,#mermaid-svg-Fn2Omu9ncsjiuQVF .node polygon,#mermaid-svg-Fn2Omu9ncsjiuQVF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Fn2Omu9ncsjiuQVF .rough-node .label text,#mermaid-svg-Fn2Omu9ncsjiuQVF .node .label text,#mermaid-svg-Fn2Omu9ncsjiuQVF .image-shape .label,#mermaid-svg-Fn2Omu9ncsjiuQVF .icon-shape .label{text-anchor:middle;}#mermaid-svg-Fn2Omu9ncsjiuQVF .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Fn2Omu9ncsjiuQVF .rough-node .label,#mermaid-svg-Fn2Omu9ncsjiuQVF .node .label,#mermaid-svg-Fn2Omu9ncsjiuQVF .image-shape .label,#mermaid-svg-Fn2Omu9ncsjiuQVF .icon-shape .label{text-align:center;}#mermaid-svg-Fn2Omu9ncsjiuQVF .node.clickable{cursor:pointer;}#mermaid-svg-Fn2Omu9ncsjiuQVF .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Fn2Omu9ncsjiuQVF .arrowheadPath{fill:#333333;}#mermaid-svg-Fn2Omu9ncsjiuQVF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Fn2Omu9ncsjiuQVF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Fn2Omu9ncsjiuQVF .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Fn2Omu9ncsjiuQVF .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Fn2Omu9ncsjiuQVF .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Fn2Omu9ncsjiuQVF .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Fn2Omu9ncsjiuQVF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Fn2Omu9ncsjiuQVF .cluster text{fill:#333;}#mermaid-svg-Fn2Omu9ncsjiuQVF .cluster span{color:#333;}#mermaid-svg-Fn2Omu9ncsjiuQVF div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Fn2Omu9ncsjiuQVF .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Fn2Omu9ncsjiuQVF rect.text{fill:none;stroke-width:0;}#mermaid-svg-Fn2Omu9ncsjiuQVF .icon-shape,#mermaid-svg-Fn2Omu9ncsjiuQVF .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Fn2Omu9ncsjiuQVF .icon-shape p,#mermaid-svg-Fn2Omu9ncsjiuQVF .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Fn2Omu9ncsjiuQVF .icon-shape .label rect,#mermaid-svg-Fn2Omu9ncsjiuQVF .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Fn2Omu9ncsjiuQVF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Fn2Omu9ncsjiuQVF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Fn2Omu9ncsjiuQVF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} vmalloc vmalloc.c:3416
__vmalloc_node_range :3235

先试大页 VM_ALLOW_HUGE_VMAP
__get_vm_area_node :2570

分配虚拟空间(红黑树找空闲区间)
alloc_vmap_area :1582

free_vmap_area_root 红黑树 first-fit
__vmalloc_area_node :3101

拿物理页 + 建页表
vm_area_alloc_pages :2992

逐个从 buddy 拿物理页
vmap_pages_range :628

vmap_range_noflush :278 建页表
pgd→p4d→pud→pmd→pte

逐级把物理页填进页表
返回虚拟连续的内核地址

物理页不连续也能寻址
vfree :2807

remove_vm_area 拆页表 + 逐页还 buddy

一句话:vmalloc → __vmalloc_node_range 分配虚拟空间(红黑树找空闲区间)→ __vmalloc_area_node 逐个从 buddy 拿物理页 + vmap_pages_range 建页表把不连续物理页映射成连续虚拟地址 → 返回虚拟连续内核内存。


一、vmalloc 是什么:虚拟连续 ≠ 物理连续

1.1 三个分配器,三种"连续"

物理连续? 虚拟连续? 怎么拿到 快慢 能 DMA?
kmalloc / slab(#13) ✅ ✅(直接映射区,虚拟=物理+偏移) buddy 直接给连续块 快(无页表开销) ✅
vmalloc ❌(逐个 order-0) ✅ 逐个拿物理页 + 建页表 慢(逐页 + 页表遍历) ❌(无 IOMMU 时)
mmap(#16,用户态) ❌(按需) ✅ 只登记 vma,缺页才给 首次访问慢(缺页) 靠 GUP pin(#6)

vmalloc 的核心:把"连续"这件事拆成虚拟和物理两个维度 。kmalloc 要求两个都连续(所以受外部碎片限制,拿不到大块);vmalloc 只要求虚拟连续,物理页零散无所谓------虚拟地址连续,指针就能连续寻址,物理页靠页表翻译。

1.2 为什么内核需要 vmalloc

  • 内核模块加载 :insmod 要把模块的代码/数据放进内核地址空间,大小不定,用 vmalloc。
  • BPF 程序 / kprobe:JIT 出的代码、perf 缓冲区,要大块内核内存。
  • per-CPU 大缓冲、DRM 驱动:某些驱动要一大块虚拟连续的工作区。

这些场景的共同点:要"大块连续的虚拟地址",但物理页可以零散。这正是 vmalloc 的用武之地。


二、两个数据结构:vm_struct + vmap_area

2.1 struct vm_struct:一块 vmalloc 区的描述符

c 复制代码
// struct vm_struct(include/linux/vmalloc.h:49)
struct vm_struct {
	struct vm_struct *next;        // 链表串起所有 vm_struct
	void *addr;                    // 虚拟起始地址
	unsigned long size;            // 大小
	unsigned long flags;           // VM_ALLOC / VM_IOREMAP / VM_ALLOW_HUGE_VMAP...
	struct page **pages;           // 指向物理页指针数组
	unsigned int nr_pages;         // 物理页数量
	phys_addr_t phys_addr;         // ioremap 时的物理基址
	const void *caller;            // 谁分配的(调试)
};

它对应一块已经分出去的虚拟区间 ,记录虚拟地址、大小、以及背后散落的物理页数组 pages。

2.2 struct vmap_area:虚拟空间的"区间节点"

管理 vmalloc 虚拟地址空间用的是两棵红黑树 ,节点是 vmap_area:

c 复制代码
// struct vmap_area(include/linux/vmalloc.h:63)
struct vmap_area {
	unsigned long va_start;        // 区间起点
	unsigned long va_end;          // 区间终点

	struct rb_node rb_node;        // 红黑树节点(按地址排序)
	struct list_head list;         // 链表节点(按地址排序)

	union {
		unsigned long subtree_max_size;  // 在"空闲树"里:子树最大空闲块
		struct vm_struct *vm;            // 在"已用树"里:指向对应的 vm_struct
	};
	unsigned long flags;
};

关键设计------两棵树 + union 复用:

  • 空闲树 free_vmap_area_root(vmalloc.c:766)+ 空闲链表 free_vmap_area_list(:754):存所有空闲 虚拟区间。alloc_vmap_area(:1582)在这棵树上 first-fit 找一块够大的空闲区间。subtree_max_size 记录子树里最大的连续空闲块,让"找够大的块"变成 O(log n)------不用挨个遍历。
  • 已用树 vmap_area_root:存所有已分配 的区间。释放时 remove_vm_area 按地址查这棵树找到要回收的区间。

同一个 vmap_area 对象,空闲时挂在空闲树(union 里是 subtree_max_size),分出去后挂到已用树(union 里是 vm 指针)。一个 union 省下一个字段,两棵树各取所需。

Why 虚拟空间也要"分配器" :vmalloc 区(x86-64 是 VMALLOC_START~VMALLOC_END,vmalloc_base 起的 32TB,pgtable_64_types.h:134/127)是一段连续的内核虚拟地址区间,跟 buddy 管理物理页一样,虚拟地址区间也得有"谁占着、哪段空着"的记账。所以 vmalloc 复用了"红黑树 + 链表"这套经典区间管理(和用户态 vma 的 maple tree #16 是同一个问题,只是 vmalloc 用红黑树)。


三、分配:vmalloc → 分配虚拟空间 → 拿物理页 → 建页表

3.1 入口:vmalloc 是薄包装

c 复制代码
// vmalloc(mm/vmalloc.c:3416)
void *vmalloc(unsigned long size)
{
	return __vmalloc_node(size, 1, GFP_KERNEL, NUMA_NO_NODE,
				__builtin_return_address(0));
}

真正干活的是 __vmalloc_node_range(vmalloc.c:3235)。它先试大页:

c 复制代码
// __vmalloc_node_range(vmalloc.c:3235)------ 关键片段
if (vmap_allow_huge && (vm_flags & VM_ALLOW_HUGE_VMAP)) {
	...
	if (arch_vmap_pmd_supported(prot) && size_per_node >= PMD_SIZE)
		shift = PMD_SHIFT;                 // 大页: 2MB 页
	...
}

area = __get_vm_area_node(real_size, align, shift, VM_ALLOC | ...);   // ① 分虚拟空间(:3280)
...
ret = __vmalloc_area_node(area, gfp_mask, prot, shift, node);          // ② 拿物理页 + 建页表(:3320)

vmap_allow_huge + VM_ALLOW_HUGE_VMAP(如 vmalloc_huge :3435 传的)时,用 2MB 大页(arch_vmap_pmd_supported),减少页表层级和 TLB miss------接 #4 大页。

3.2 第一步:分配虚拟空间

__get_vm_area_node(:2570)→ alloc_vmap_area(:1582):

c 复制代码
// alloc_vmap_area(vmalloc.c:1582)------ 关键片段
va = kmem_cache_alloc_node(vmap_area_cachep, gfp_mask, node);   // 分配 vmap_area 节点
...
addr = __alloc_vmap_area(&free_vmap_area_root, &free_vmap_area_list,   // :1615
		size, align, vstart, vend);

__alloc_vmap_area 在空闲红黑树上 first-fit 找一块对齐的、够大的空闲虚拟区间,摘下来。这一步只动虚拟地址,不碰物理页。

3.3 第二步:拿物理页 + 建页表

__vmalloc_area_node(vmalloc.c:3101):

c 复制代码
static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask, ...)
{
	/* ① 分配"物理页指针数组" */
	array_size = nr_small_pages * sizeof(struct page *);
	if (array_size > PAGE_SIZE)
		area->pages = __vmalloc_node(array_size, 1, ...);   // 数组大,自己也 vmalloc(:3122)
	else
		area->pages = kmalloc_node(array_size, ...);         // 数组小,kmalloc(:3125)

	/* ② 逐个从 buddy 拿物理页(order-0 或大页) */
	area->nr_pages = vm_area_alloc_pages(gfp_mask | __GFP_NOWARN,      // :3139
			node, page_order, nr_small_pages, area->pages);

	/* ③ 建页表:把不连续的物理页映射进连续的虚拟空间 */
	ret = vmap_pages_range(addr, addr + size, prot, area->pages, page_shift);  // :3182
	...
	return area->addr;
}

三步拆开:

  1. 分配页指针数组 area->pages :struct page * 数组记录所有物理页。数组本身可能很大,超过一页就递归用 vmalloc(:3122)。
  2. vm_area_alloc_pages(:2992)逐个拿物理页 :循环 alloc_pages(:3063)从 buddy 拿 order-0 单页(或大页 order),不要求它们物理连续------这是 vmalloc 的灵魂。
  3. vmap_pages_range(:628)→ vmap_range_noflush(:278)建页表 :从 pgd_offset_k 开始逐级(pgd → p4d → pud → pmd → pte)把每个物理页的 PFN 填进页表项,把不连续的物理地址"连"成连续的虚拟地址。
c 复制代码
// vmap_range_noflush(vmalloc.c:278)------ 关键片段
pgd = pgd_offset_k(addr);
do {
	next = pgd_addr_end(addr, end);
	err = vmap_p4d_range(pgd, addr, next, phys_addr, prot, ...);   // 逐级填页表
} while (pgd++, phys_addr += (next - addr), addr = next, addr != end);

核心成本在这里:物理不连续 → 必须走页表翻译 → 每次访问都有 TLB miss 风险;且建页表本身要分配各级页表页、填 PTE。

Why vmalloc 物理不连续就慢、且不能 DMA :① 慢 ------物理连续(kmalloc 的直接映射区)虚拟=物理,一条大页映射覆盖整个内核段,TLB 几乎不 miss;vmalloc 的页是零散的,每页一个 PTE,访问大块时 TLB miss 多。② 不能 DMA ------DMA 设备没有 MMU(无 IOMMU 时),只认物理连续 地址;vmalloc 的页物理零散,设备没法一次搬。有 IOMMU 时(IOMMU 篇)dma_map_single 能把零散物理页重映射成连续 IOVA,vmalloc 内存才能做 DMA------这就是 IOMMU 的价值之一。


四、三兄弟:vmalloc / vmap / ioremap

三者共用同一套"分配虚拟空间 + 建页表"的引擎 (__get_vm_area_node + vmap_range_noflush),区别只在物理页从哪来:

物理页来源 是否分配物理页 典型用途
vmalloc 自己从 buddy 逐个拿 ✅ 分配 内核要一大块匿名内核内存
vmap(:2894) 调用者传入的 struct page **pages 数组 ❌ 只映射已存在的页 把一组已拿到的页映射成连续虚拟(如 struct page 数组)
ioremap(:307) 设备的物理地址(MMIO 寄存器) ❌ 映射已存在的物理地址 把设备 MMIO 映射进内核地址空间
c 复制代码
// vmap(vmalloc.c:2894)------ 映射调用者给的 page 数组
void *vmap(struct page **pages, unsigned int count, ...);

// ioremap_page_range(vmalloc.c:307)------ 映射设备物理地址
int ioremap_page_range(unsigned long addr, unsigned long end,
		phys_addr_t phys_addr, pgprot_t prot);   // 内部也是 vmap_range_noflush(:312)

一句话:vmalloc = 分配虚拟空间 + 分配物理页 + 建页表 ;vmap = 分配虚拟空间 + 建页表(物理页已有) ;ioremap = 分配虚拟空间 + 建页表(物理地址是设备的,不属于 buddy)。物理页来源是唯一分水岭。


五、释放:vfree

vfree(vmalloc.c:2807)是分配的逆过程:

c 复制代码
// vfree(vmalloc.c:2807)------ 关键片段
void vfree(const void *addr)
{
	...
	vm = remove_vm_area(addr);          // ① 拆页表 + 从已用树摘回空闲树(:2824)
	...
	for (i = 0; i < vm->nr_pages; i++) {
		__free_page(vm->pages[i]);      // ② 逐个物理页还回 buddy(:2842)
		cond_resched();
	}
	kvfree(vm->pages);                  // ③ 释放页指针数组
	kfree(vm);                          // ④ 释放 vm_struct
}

remove_vm_area(:2684)找到 vmap_area,拆掉页表(__vunmap_range_noflush :414 清 PTE + 刷 TLB),把虚拟区间还回空闲树。然后逐个 __free_page 把物理页还 buddy、释放页数组和描述符。物理页回到 buddy 后,虚拟区间也回到空闲树,两者独立回收。


六、与前面各篇的关系 + 记住三点

6.1 与前面各篇的关系

篇 关系
#2 buddy vmalloc 的物理页逐个从 buddy 拿(alloc_pages :3063),order-0 就行,不要求连续
#13 slab/kmalloc kmalloc 物理连续(直接映射,可 DMA),vmalloc 虚拟连续(页表,不可 DMA)------"连续"两维度的对照
#16 mmap 用户态 mmap 虚拟连续 + 按需分配;vmalloc 内核态虚拟连续 + 立即分配(当场拿页建表),都不要求物理连续
#4 大页 vmalloc_huge + VM_ALLOW_HUGE_VMAP + arch_vmap_pmd_supported 用 2MB 大页
#18 KASLR vmalloc 区起点 vmalloc_base 被 KASLR 随机化(pgtable_64_types.h:134)
IOMMU 篇 vmalloc 物理不连续不能直接 DMA,有 IOMMU 才 dma_map_single 重映射成连续 IOVA

6.2 与 AI Infra 的关系

  • BPF / 网络:eBPF 程序、XDP 元数据、perf 环形缓冲都用 vmalloc(大块虚拟连续、物理零散没问题)。
  • RDMA / 网卡驱动 :驱动里要大块工作区时用 vmalloc,但做 DMA 前必须注意------无 IOMMU 时 vmalloc 内存不能直接给设备,要么换 kmalloc(物理连续),要么靠 IOMMU。
  • CUDA / GPU:GPU 显存管理(cudaMalloc)是另一套(设备内存),但内核态驱动模块加载、GDR(GPU Direct RDMA)的内核缓冲区会碰到 vmalloc。
  • 为什么训练框架关心:DMA 缓冲(网络收发包、checkpoint 落盘)选 kmalloc 还是 vmalloc,取决于有没有 IOMMU------这决定了能不能零拷贝。

6.3 记住三点

  1. vmalloc = 虚拟连续、物理不连续 :把"连续"拆成虚拟/物理两维。vmalloc(vmalloc.c:3416)→ __vmalloc_node_range(:3235)先试大页,然后 __get_vm_area_node(:2570)→ alloc_vmap_area(:1582)在 free_vmap_area_root 红黑树上 first-fit 分虚拟空间,再 __vmalloc_area_node(:3101)逐个 vm_area_alloc_pages(:2992)拿物理页 + vmap_pages_range(:628)建页表。

  2. 两棵树管虚拟空间 :struct vmap_area(vmalloc.h:63)空闲时挂 free_vmap_area_root(union 存 subtree_max_size 加速 first-fit),分出去挂已用树(union 存 vm 指针)。struct vm_struct(:49)记录一块分出去的区间 + 背后的页数组。

  3. 三兄弟同一引擎、区别在物理页来源 :vmalloc(自己从 buddy 拿)、vmap(映射调用者的 page 数组,:2894)、ioremap(映射设备物理地址,:307)共用"分虚拟空间 + vmap_range_noflush(:278)建页表";物理不连续 → 页表翻译慢 + 无 IOMMU 不能 DMA。释放 vfree(:2807)拆页表 + 逐页还 buddy。


附:本篇关键源码索引

符号 位置
vmalloc / vmalloc_huge(入口) mm/vmalloc.c:3416 / :3435
__vmalloc_node_range(分虚拟空间 + 拿页建表) mm/vmalloc.c:3235
__get_vm_area_node(分配虚拟空间) mm/vmalloc.c:2570
alloc_vmap_area(红黑树 first-fit 分虚拟空间) mm/vmalloc.c:1582
__vmalloc_area_node(拿物理页 + 建页表) mm/vmalloc.c:3101
vm_area_alloc_pages(逐个从 buddy 拿物理页) mm/vmalloc.c:2992
vmap_pages_range(建页表入口) mm/vmalloc.c:628
vmap_range_noflush(pgd→p4d→pud→pmd→pte 填页表) mm/vmalloc.c:278
vmap(映射调用者的 page 数组) mm/vmalloc.c:2894
ioremap_page_range(映射设备物理地址) mm/vmalloc.c:307
vfree(释放) mm/vmalloc.c:2807
remove_vm_area(拆页表 + 摘回空闲树) mm/vmalloc.c:2684
struct vm_struct(addr/pages/nr_pages) include/linux/vmalloc.h:49
struct vmap_area(rb_node + union subtree_max_size/vm) include/linux/vmalloc.h:63
free_vmap_area_root / free_vmap_area_list mm/vmalloc.c:766 / :754
VMALLOC_START / VMALLOC_END(vmalloc_base 起 32TB) arch/x86/include/asm/pgtable_64_types.h:134 / :152
相关推荐
pt10432 小时前
Cisco设备网络安全系列-19:网络安全事件驱动与调整
运维·网络
Escalating_xu2 小时前
【Linux 网络编程】ARP 的具体过程与 ARP 欺骗:一条 IP → MAC 映射,怎样改变数据的去向?
linux·网络·tcp/ip
sxgzzn2 小时前
如何利用无人机提升光伏巡检效率?
运维·重构·无人机·开关柜局放在线监测·光伏运维管理系统
R²AIN SUITE2 小时前
企业智能体平台私有化部署需要什么服务器?CPU、内存、显卡与项目管理集成的7个评估项
运维·服务器
坚定的共产主义生产设备永不宕机2 小时前
服务器从裸机开始搭建ESXI虚拟机
linux·服务器·网络
刚入门的大一新生2 小时前
Linux-线程封装
linux·运维·服务器
god_cvz2 小时前
实践:Linux环境构建CTB工具(cesium-terrain-builder)
linux·gis·ctb
n112123 小时前
Docker 容器时区错 8 小时:3 个坑
运维·docker·容器·故障排查·服务器运维·容器时区
Android系统攻城狮3 小时前
Linux FFmpeg深度解析之av_dump_format调用流程与实战(十一)
linux·运维·服务器·ffmpeg·1024程序员节·音视频进阶