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

【操作系统百科】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-04-27 · os

【操作系统百科】内存回收

Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。

2026-04-28 · os

【操作系统百科】交换

swap 还值得开吗?本文讲 swap area 基础、swap cache、zram 压缩内存、zswap 前端压缩池、swappiness 的真实含义、容器里的 swap 策略,以及为什么现代 Android 全靠 zram 不靠磁盘。

2026-05-03 · os

【操作系统百科】Slab/SLUB 分配器

buddy 只管页粒度(4K+),内核大多数对象只有几十到几百字节。slab/SLUB 在 buddy 之上做对象级缓存。本文讲 slab 历史、SLUB 接手、SLOB 退场、kmem_cache、per-CPU cache、KASAN 集成。

2026-05-07 · os

【操作系统百科】用户态分配器

glibc malloc、tcmalloc、jemalloc、mimalloc 各有哲学。本文讲 arena、thread cache、size class、madvise 返还策略、碎片与 RSS 膨胀、如何根据负载选分配器。


By .