土法炼钢兴趣小组的算法知识备份

【操作系统百科】vmalloc/kmap/ioremap

文章导航

分类入口
os
标签入口
#vmalloc#ioremap#kmap#vmap#pat

目录

buddy 分配的是物理连续页——高 order 容易碎片失败。vmalloc 反过来:虚拟连续、物理随意——用内核页表映射散落的物理页到连续 VA。

一、先看图

flowchart LR
    VMALLOC[vmalloc 64KB] --> ALLOC[alloc 16 × order-0 页<br/>物理不连续]
    ALLOC --> MAP[在 vmalloc 区间<br/>建页表映射<br/>VA 连续]
    MAP --> VA[返回虚拟地址<br/>内核可用]
    IOREMAP[ioremap 寄存器] --> MMIO[建页表映射<br/>物理=设备 BAR]
    MMIO --> UNCACHE[UC/WC 缓存策略]
    classDef sw fill:#388bfd22,stroke:#388bfd,color:#adbac7;
    classDef hw fill:#3fb95022,stroke:#3fb950,color:#adbac7;
    class VMALLOC,ALLOC,MAP,VA sw
    class IOREMAP,MMIO,UNCACHE hw

二、vmalloc

2.1 用法

void *p = vmalloc(size);     // 可睡眠
vfree(p);

void *p = vzalloc(size);    // 零填充

2.2 内部

  1. 在 vmalloc VA 区间找一段空闲
  2. 为每页调 alloc_page(GFP_KERNEL) 分配 order-0 物理页
  3. 逐页建内核页表映射
  4. flush TLB

2.3 vmalloc 区域

x86_64 内核地址空间里:

线性映射 (PAGE_OFFSET ~ VMALLOC_START)
VMALLOC_START ~ VMALLOC_END    vmalloc / vmap / ioremap

约 32TB 的虚拟空间给 vmalloc。

2.4 性能

vmalloc 比 kmalloc 慢:

不适合频繁分配小对象。适合:大缓冲区、BPF 程序加载、module 加载。

2.5 vmap

vmap 是底层接口——给已有的 page 数组建映射:

struct page *pages[N];
void *va = vmap(pages, N, VM_MAP, PAGE_KERNEL);
// 用完
vunmap(va);

vmalloc = alloc_pages + vmap。

三、ioremap

3.1 用途

映射设备 MMIO 寄存器到内核虚拟地址:

void __iomem *base = ioremap(phys_addr, size);
u32 val = readl(base + OFFSET);
writel(0x1234, base + OFFSET);
iounmap(base);

3.2 缓存策略

设备寄存器不能用 CPU 缓存——必须 UC(Uncacheable):

ioremap(addr, size);              // 默认 UC-
ioremap_wc(addr, size);           // Write-Combining(帧缓冲等)
ioremap_cache(addr, size);        // WB(少见,只特定 RAM 区域)

x86 用 PAT(Page Attribute Table)控制每页的缓存策略:PCD/PWT/PAT bit 组合。

3.3 devm_ioremap

设备模型自动管理:驱动 remove 时自动 iounmap:

base = devm_ioremap_resource(dev, res);

四、kmap 与 HIGHMEM

4.1 32 位的问题

32 位内核只有 ~1GB 内核空间。物理内存 >1GB 的部分在 ZONE_HIGHMEM,不能直接线性映射。

kmap 把 HIGHMEM 页临时映射到一小块窗口:

void *va = kmap(page);        // 可能睡眠
kunmap(page);

void *va = kmap_atomic(page); // 不可睡眠
kunmap_atomic(va);

4.2 64 位 = HIGHMEM 退场

64 位内核空间足够直接线性映射所有 RAM → ZONE_HIGHMEM 不存在 → kmap 退化为 page_address(直接取线性映射地址)。

4.3 kmap_local(替代 kmap_atomic)

5.11+ kmap_local_page 替代 kmap_atomic——不禁止抢占,更灵活。

void *va = kmap_local_page(page);
// ... 可以 schedule
kunmap_local(va);

五、percpu 的 vmalloc 路径

per-CPU 变量的动态部分(pcpu_alloc)底层用 vmalloc 区域映射——每 CPU 一组页,VA 按 CPU 偏移排布。

六、BPF 与 vmalloc

BPF 程序加载后存在 vmalloc 区域——因为 JIT 代码需要可执行页,大小不定。6.x 有 bpf_prog_pack 优化:把多个小 BPF 程序打包到一个 2M huge page。

七、常见问题

A:vmalloc 空间耗尽 /proc/meminfo 的 VmallocUsed。极端情况(大量 BPF / iptables 规则)可能接近上限。

B:ioremap 返回 NULL 物理地址冲突或 PAT 策略不兼容。检查 /proc/iomem

C:kmap_atomic 中 schedule 旧代码 bug——会 corrupt 映射窗口。改用 kmap_local_page

八、观察

cat /proc/meminfo | grep Vmalloc
# VmallocTotal:  34359738367 kB
# VmallocUsed:   123456 kB

cat /proc/vmallocinfo | head
# 0xffff...  size=16384  caller=bpf_prog_alloc

cat /proc/iomem                # 物理地址布局

九、小结


参考文献

工具


上一篇Slab/SLUB 分配器 下一篇per-CPU 变量

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-05-06 · os

【操作系统百科】内核内存调试

内核内存 bug 是最难追的:UAF、OOB、double free、leak 都可能沉默数月。本文讲 KASAN 三种模式、KFENCE 生产采样、kmemleak、SLUB_DEBUG、UBSAN/KCSAN 联动。

2026-05-08 · os

【操作系统百科】VFS 四层抽象

Linux 的一切皆文件靠 VFS 实现——superblock、inode、dentry、file 四层抽象加 ops 表。本文讲 VFS 核心数据结构、dcache、inode cache、RCU lookup,以及文件系统如何插入 VFS。

2026-04-22 · os

操作系统百科

Linux 6.x 视角下的操作系统系列索引:110 篇覆盖调度、虚拟内存、文件系统与 I/O、并发、隔离、可观测性,按主题、阅读路径与关键问题三种入口组织。

2026-05-27 · os

【操作系统百科】用户态分配器:jemalloc vs tcmalloc

jemalloc 与 tcmalloc 都想解决多线程分配器的老问题:锁争抢、碎片、RSS 膨胀与回收抖动。但两者把优化重点放在了不同位置:tcmalloc 更激进地把热路径推到 per-CPU,jemalloc 则把 arena、extent、decay 和 profiling 做成了一套更完整的内存治理工具箱。


By .