Skip to the content.

madvise

总体分析

下面覆盖 Linux v7.0.1 通用 UAPI 中的全部 27 个 MADV_*。定义位于 include/uapi/asm-generic/mman-common.h,主要实现位于 mm/madvise.c

最初的五个传统 advice 随 Linux madvise() 在 2.4.0 出现。Torvalds 主线 Git 的完整历史从 2.6.12-rc2 才开始,因此没有可信的原始 Git commit;这里 不能用后来的头文件整理 commit 冒充功能引入 commit。

功能 添加理由 作用 Commit 首次版本
MADV_NORMAL 提供默认访问策略 清除随机/顺序访问提示,恢复默认 readahead 行为 早于 Git 历史 Linux 2.4.0
MADV_RANDOM 随机访问时普通预读会浪费 I/O 和 page cache 标记 VMA 为随机访问,尽量减少预读 早于 Git 历史 Linux 2.4.0
MADV_SEQUENTIAL 优化顺序扫描的吞吐和缓存占用 标记 VMA 为顺序访问,增强顺序预读,并更积极处理已经越过的页面 早于 Git 历史 Linux 2.4.0
MADV_WILLNEED 应用预告即将访问一段映射 对文件映射执行 best-effort 预读;它不保证完成 prefault 早于 Git 历史 Linux 2.4.0
MADV_DONTNEED 应用已经不需要当前页面内容 立即丢弃或解除页面映射;匿名内存再次访问得到零页,私有文件映射重新从文件读取 早于 Git 历史 Linux 2.4.0
MADV_REMOVE 数据库需要丢弃共享 buffer pool 的一部分;释放 tmpfs 页面和 swap 后备存储;支持 UML 内存热插拔 对共享可写映射执行 hole punch,释放页面及文件后备空间;以后读取返回零 f6b3ec238d12 Linux 2.6.16
MADV_DONTFORK fork() 后 COW 会改变 pinned page 的物理地址,破坏 RDMA/InfiniBand DMA;也可减少 fork 开销 子进程不继承指定 VMA,访问对应地址会触发 SIGSEGV f822566165dd Linux 2.6.16
MADV_DOFORK 撤销 MADV_DONTFORK 恢复默认的 fork 继承行为 同上 Linux 2.6.16
MADV_HWPOISON 测试内核硬件内存错误处理路径 人工将页面标记为硬件损坏,后续访问按真实 memory failure 处理,通常产生 SIGBUS 9893e49d64a4 Linux 2.6.32
MADV_MERGEABLE KSM 原先使用 /dev/ksm ioctl,社区希望采用按地址范围的接口 将私有匿名 VMA 注册为 KSM 候选,允许合并内容相同的页面 d19f35248446 Linux 2.6.32
MADV_UNMERGEABLE 撤销 KSM 注册 停止合并并拆分已经合并的 KSM 页面 同上 Linux 2.6.32
MADV_SOFT_OFFLINE 测试程序需要先构造特定页面状态,再测试软下线逻辑 迁移页面内容并将原物理页隔离,通常不破坏进程数据 afcf938ee0aa Linux 2.6.33
MADV_HUGEPAGE THP 需要应用按 VMA 指定适合使用大页的关键内存 持久标记 VMA 为 THP 候选,但不保证立即产生 THP a826e422420b Linux 2.6.38
MADV_NOHUGEPAGE 稀疏或延迟敏感映射可能不适合 THP 禁止指定 VMA 使用 THP 1ddd6db43a08 Linux 2.6.38
MADV_DONTDUMP 避免 QEMU core dump 包含全部 guest RAM;保护密钥等敏感内存 将 VMA 排除在 core dump 之外,优先级高于 coredump_filter accb61fe7bb0 Linux 3.4
MADV_DODUMP 撤销 MADV_DONTDUMP 恢复该 VMA 的默认 core dump 策略 同上 Linux 3.4
MADV_FREE jemalloc、tcmalloc 等 allocator 需要低成本释放,避免把无用数据换出,同时允许页面在真正回收前快速复用 将匿名页面标记为 lazy-free;有内存压力才丢弃,期间若再次写入则取消释放 854e9ed09ded Linux 4.5
MADV_WIPEONFORK PRNG、OpenSSL、PKCS#11 等库需要在 fork 后自动重新初始化状态,同时不能让地址失效 子进程保留 VMA,但内容全部变为零;适用于私有匿名映射 d2cd9ede6e19 Linux 4.14
MADV_KEEPONFORK 撤销 MADV_WIPEONFORK 恢复 fork 时复制或继承原内容 同上 Linux 4.14
MADV_COLD Android 比内核 LRU 更清楚哪些后台应用已变冷,希望优先回收它们,同时保留数据 将页面从 active LRU 移到 inactive LRU;不立即回收、不丢数据 9c276cc65a58 Linux 5.4
MADV_PAGEOUT 用户态希望主动回收已知长期不用的页面,而不是等待内存压力或杀进程 立即尝试回收页面;匿名页换出、脏文件页回写,内容仍被保留 1a4e58cce84e Linux 5.4
MADV_POPULATE_READ QEMU、数据库和稀疏映射需要可靠 prefault;MAP_POPULATE 只能在 mmap 时使用且不能可靠报告错误,逐页 touch 又慢 同步建立可读页表;类似逐页读取,不打破 COW,成功表示整个范围当时可读 4ca9b3859dac Linux 5.14
MADV_POPULATE_WRITE 同时需要预分配后备内存并建立可写页表 同步建立可写页表,可能分配后备存储并打破 COW;成功表示整个范围当时可写 同上 Linux 5.14
MADV_DONTNEED_LOCKED 安全内存 allocator 和 MLOCK_ONFAULT 用户需要释放 locked VMA 的物理页;munlockmadvisemlock 会产生额外 syscall、VMA 分裂和锁竞争 MADV_DONTNEED 相同,但显式允许处理 mlock 区域 9457056ac426 Linux 5.18
MADV_COLLAPSE khugepaged 的时机不可预测;TCMalloc、VM 迁移等希望主动恢复 THP,并让请求进程承担 CPU 和回收成本 同步把范围折叠成 THP;这是一次性操作,不给 VMA 添加持久策略,可绕过全局 THP never,但不能绕过 MADV_NOHUGEPAGE 7d8faaf15545 Linux 6.1
MADV_GUARD_INSTALL 使用大量独立 PROT_NONE VMA 作为 guard page 会消耗 VMA、触发 mmap syscall 和 mmap_lock 竞争 使用 PTE marker 安装轻量 guard;丢弃该位置的现有页面,随后访问产生致命 SIGSEGV 662df3e5c376 Linux 6.13
MADV_GUARD_REMOVE 移除轻量 guard 只清除范围内的 guard marker,其他页面保持不变 同上 Linux 6.13

版本依据来自对应的主线 commit、主线 release tag 和 madvise(2)

回收类 advice 的区别

接口 是否立即行动 是否丢失内容
MADV_DONTNEED
MADV_FREE 内存压力时 是,但再次写入可取消
MADV_COLD 只调整 LRU
MADV_PAGEOUT 立即尝试回收或换出

MADV_WILLNEEDMADV_POPULATE_* 也不同:前者只是 best-effort 预读提示;后者是同步 prefault,并向调用者报告失败。

相关讨论: https://news.ycombinator.com/item?id=23216590

MADV_POPULATE_WRITE

QEMU 中的代码,在做 MADV_POPULATE_WRITE 的时候需要持有 mmap_sem 的 read lock

    /*
     * On Linux, the page faults from the loop below can cause mmap_sem
     * contention with allocation of the thread stacks.  Do not start
     * clearing until all threads have been created.
     */
    qemu_mutex_lock(&page_mutex);
    while (!memset_args->context->all_threads_created) {
        qemu_cond_wait(&page_cond, &page_mutex);
    }
    qemu_mutex_unlock(&page_mutex);

本站所有文章转发 CSDN 将按侵权追究法律责任,其它情况随意。