A12 — The Complete Journey of the Memory System: From Physical to Virtual to Application

Key Insight: The only way to understand memory is to trace the complete journey of data from hardware to application.


Five-Layer Panorama of the Memory System

Bash
内存系统的分层结构:

  [应用层]          malloc / new / mmap
      │
      ▼
  [用户态分配器]    ptmalloc / tcmalloc / jemalloc
      │
      ▼
  [内核页表]        VMA / COW / page fault
      │
      ▼
  [内核物理管理]    Buddy / SLAB / SLUB / Page Cache
      │
      ▼
  [硬件层]          DRAM / Cache / MMU / CPU

Bash
每层的作用:

硬件层(S/H):
  - DRAM:实际的存储介质
  - Cache(L1/L2/L3):减少 DRAM 访问延迟
  - MMU:VA → PA 翻译
  - 地址总线:CPU 到 DRAM 的通信

内核物理管理(K):
  - Buddy System:分配 4KB+ 的物理页
  - SLAB/SLUB:把页切成固定大小的对象
  - Page Cache:缓存文件数据
  - kswapd:回收页面(LRU)

内核页表(B):
  - VMA:进程虚拟地址空间的管理
  - COW:fork 时的延迟复制
  - page fault:按需分配物理页
  - mmap:建立文件或匿名映射

用户态分配器(A):
  - ptmalloc:glibc 的 malloc(arena + bin)
  - tcmalloc:Google 的高效分配器
  - jemalloc:Facebook 的高效分配器

应用层:
  - malloc / new:申请内存
  - free / delete:释放内存
  - mmap:映射文件或分配大块

The Complete Journey of a Single Memory Allocation

Bash
malloc(256) 的完整路径:

1. ptmalloc 接收请求
   - 大小 256 → 找对应 size class(可能是 256)
   - 检查 thread cache(tcache)
   - tcache 有 → 直接返回(无锁,O(1))

2. tcache 空 → 从 arena 的 bin 分配
   - 检查 smallbin 或 largebin
   - bin 有 → 取出 chunk(需要锁)
   - bin 空 → 从 top chunk 取

3. top chunk 不够 → 调用 brk()
   - brk() 系统调用
   - 内核分配新的虚拟地址空间(vma)
   - 物理页在第一次访问时分配(demand paging)
   - 更新 top chunk

4. 物理页分配(page fault):
   - MMU 发现 PTE 无效
   - OS 分配物理页(alloc_pages → buddy)
   - 建立页表映射
   - 填充页面(清零或读文件)
   - 返回,继续执行

5. 返回虚拟地址给用户程序

总耗时:
  - tcache 命中:~10 ns(用户态)
  - bin 分配:~50 ns(含锁)
  - brk:~500 ns(含系统调用)
  - page fault:~10000 ns(首次访问,含物理分配)

What Happens After Data Is Written

Bash
memcpy(buf, data, 256) 的过程:

1. 用户态 memcpy
   - 把 data 复制到 buf(CPU copy)

2. 如果 buf 之前没访问过(第一次 touch):
   - page fault
   - OS 分配物理页(alloc_pages → buddy)
   - 建立页表映射

3. 如果多个进程映射同一文件(MAP_SHARED):
   - 修改直接反映到 Page Cache
   - 其他进程立即看到

4. 后续访问:
   - MMU 查页表 → PA
   - L1 Cache hit → ~1 ns
   - L1 Cache miss → L2 hit → ~10 ns
   - L2 miss → L3 hit → ~30 ns
   - L3 miss → DRAM → ~60 ns

Memory Copy During fork

Bash
fork() 的内存复制路径:

1. copy_page_range() 被调用
   - 遍历父进程的每个 VMA
   - 复制页表(PTE)
   - 共享物理页面(引用计数 +1)
   - 设置只读位

2. COW 设置:
   - 父子共享同一物理页
   - 页面标记为只读
   - 写入触发 page fault

3. 写入时的 COW 处理:
   - Page fault → 检查 PTE
   - 发现只读 + 共享 → 分配新页
   - 复制旧页内容
   - 更新 PTE 为新页 + 可写
   - 继续执行写入

Complete Memory Leak Detection Chain

Bash
检测内存泄漏的工具对应关系:

1. /proc/status(运行时监控):
   - RSS 持续增长 → 可能有泄漏
   - VmPeak 持续增长 → 确认泄漏

2. mtrace(glibc 内置):
   - 记录所有 malloc/free
   - 找出未配对的分配

3. valgrind(全面检测):
   - 跟踪所有 malloc/free/realloc
   - 报告泄漏的堆块
   - 报告 use-after-free

4. ASan(运行时检测):
   - 编译时插桩
   - 检测越界访问
   - 检测 UAF
   - 比 valgrind 快 2~5x

5. massif(堆分析):
   - 跟踪堆的使用
   - 找出峰值和趋势
   - 火焰图可视化

Memory Allocation in NUMA Systems

Bash
NUMA 系统中 malloc 的路径:

1. glibc 分配请求
   - 线程缓存 → arena

2. arena 分配物理页:
   - first touch:在线程当前运行的节点分配
   - mbind:可以绑定到特定节点

3. alloc_pages_node(node, ...):
   - 在指定节点从 buddy 分配
   - 如果节点内存不足,回退到其他节点

4. 页面迁移(numa_balancing):
   - 如果页面和访问线程不在同一节点
   - kernel 自动迁移页面
   - 减少 NUMA 延迟

numa 感知的分配:
  #include <numa.h>
  numa_bind(&numa_all_nodes);
  // 后续分配在绑定的节点

Page Cache and File I/O

Bash
文件读取的 Page Cache 路径:

1. read(fd, buf, 4096)
   - 查找 Page Cache(radix tree)

2. Cache miss:
   - 从 buddy 分配物理页
   - 从磁盘读取到 Page Cache
   - 复制到用户 Buffer

3. Cache hit:
   - 直接从 Page Cache 复制
   - 无磁盘 I/O

文件写入的 Page Cache 路径:

1. write(fd, buf, 4096)
   - 写入 Page Cache(标记为 dirty)
   - 立即返回(写回异步)

2. 定期 flush(pdflush):
   - 把 dirty 页面写回磁盘
   - 更新 Page Cache 状态

Summary

  • Five-Layer Structure: Hardware (S/H) → Kernel Physical (B/K) → Kernel Page Table (B) → Userspace Allocator (A) → Application
  • malloc Path: tcache → bin → top chunk → brk → page fault → buddy
  • Data Write: User memcpy → COW → PTE update → Cache hit or DRAM access
  • NUMA: first touch → node assignment → page migration (numa_balancing)
  • Page Cache: radix tree indexing → LRU management → periodic flushing
  • Leak Detection Chain: /proc/status → mtrace → valgrind → ASan → massif

Memory Series Summary: From S-layer DRAM Cell and DDR5, to H-layer DIMM and dual channel, to B-layer boot/mmu/buddy, to K-layer SLAB/kswapd/PageCache, to A-layer malloc/brk and leak detection. 55 articles cover the complete memory lifecycle.


Last modified: 2024年1月12日

Author

Comments

Write a Reply or Comment

Your email address will not be published.