Files
dpdk-blog/notes/phase0/day03-kernel-memory.md
T
newbieandClaude 6d8c994dde init: DPDK → DPU learning blog (VitePress)
- VitePress static site for rendering all markdown documents
- Auto-discovers .md files from dpdk-roadmap-v3 and dpdk-study-kb
- Features: full-text search, dark mode, responsive design
- Pages: roadmap, notes (Phase 0-3), product docs, knowledge base

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-09 15:42:47 +00:00

5.5 KiB
Executable File

Phase 0 · Day 3: 内核内存管理 — buddy → slab → slub


一、你的系统数据

Buddy (/proc/buddyinfo, Node 0 DMA32):
  order 0(4KB)=2275  1(8KB)=2107  2(16KB)=2287  3(32KB)=2270
  4(64KB)=1540  5(128KB)=992  6(256KB)=558  7(512KB)=215
  8(1MB)=35  9(2MB)=7  10(4MB)=0  ← 连续大块已严重碎片化

Slab (/proc/slabinfo):
  skbuff_head_cache:    4411 active  256B/obj  16 objs/slab
  skbuff_small_head:    345  active  704B/obj  23 objs/slab
  sock_inode_cache:     896  active  832B/obj
  kmalloc-cg-2k:        627  active  2048B/obj
  kmalloc-cg-4k:        222  active  4096B/obj

二、三层架构

kmalloc(1536)
    ↓
┌─ Slub (对象分配器) ─────────────────────┐
│ per-CPU cache (kmem_cache_cpu)           │
│   freelist: obj0→obj3→obj7→∅            │
│   分配 = 弹 head, O(1), 无锁 (fastpath) │
│   释放 = 插 head, O(1), 无锁            │
│         ↓ freelist 空                   │
│ partial slab → 切对象 → 填 freelist     │
│         ↓ partial 也空                  │
└────────────┬────────────────────────────┘
             ↓ 需要新 slab page
┌─ Buddy System (页分配器) ────────────────┐
│ order 0(4KB) → order 10(4MB)            │
│ 分配: 找 order n → 没有? 分裂 n+1       │
│ 释放: 合井相邻 buddy                    │
│ 碎片: 长期运行 order 高位全 0           │
└──────────────────────────────────────────┘

三、Buddy System

两个页块互为 buddy: 大小相同 + 物理相邻 + 来自同一父块分裂。

16KB 块 (order 2, page 0-3):
  [p0][p1][p2][p3]
   └buddy┘ └buddy┘
   
释放 p0 时: p1 空闲? → 合并成 8KB (order 1)
           [p0,p1] 和 [p2,p3] 都空闲? → 再合并成 16KB (order 2)

/proc/buddyinfo 就是各级 order 的空闲块计数。你的系统 order 9 只剩 7 个 2MB 块 → 碎片严重。

四、Slub: per-CPU 无锁分配

// include/linux/slub_def.h:50
struct kmem_cache_cpu {
    void **freelist;       // 本 CPU 空闲对象链表
    unsigned long tid;     // 事务 ID
    struct slab *slab;     // 当前 slab
    struct slab *partial;  // 部分使用的 slabs
};

分配路径

slab_alloc(s, gfp, node)
  → c = per_cpu_ptr(s->cpu_slab, cpu)  ← 本 CPU 的 cache
  → object = c->freelist                ← 取 freelist head
  → if (object) {                       ← FAST PATH (>99%)
        c->freelist = *(void**)object;   ← 更新 freelist
        return object;                  ← done, 无锁!
    }
  → ___slab_alloc(s, gfp, node, c)      ← SLOW PATH
    → 1. 取 partial slab → 切对象
    → 2. partial 空 → buddy 申请新页 → 切 16 个对象
    → 3. 1 个返回, 15 个填 freelist

freelist 嵌入空闲对象内部

一个 slab page (4KB):
┌──────┬──────┬──────┬...┬──────┐
│ obj0 │ obj1 │ obj2 │   │obj15 │  16个 256B 对象
└──────┴──────┴──────┴...┴──────┘

空闲对象 obj0 的前 8 字节 = &obj3
空闲对象 obj3 的前 8 字节 = &obj7
obj7 的前 8 字节 = NULL

分配: head = freelist; freelist = *(void**)head; return head;
释放: *(void**)obj = freelist; freelist = obj;

五、内核 slub vs DPDK mempool

内核 slub DPDK mempool
L1 cache per-CPU freelist per-lcore cache
L1 操作 弹 freelist head, O(1) 弹 cache->objs[--len], O(1)
L2 补货 partial slab → buddy 新页 rte_ring → driver dequeue
L3 兜底 buddy alloc → OOM 驱动层
锁 无锁 (per-CPU) 无锁 (per-lcore + ring CAS)
预分配 按需从 buddy 取 hugepage 一次性分配
碎片 长期运行 buddy 碎片 不会,固定大小,一次分配

六、实战踩坑

坑 1: 跨 NUMA 分配延迟 3x

  • 现象: 双 socket 服务器上性能波动严重
  • 根因: CPU0 分配了 socket1 的内存 → 远程访问
  • 修复: rte_mempool_create(socket_id=rte_socket_id())

坑 2: buddy 碎片 → 大块分配失败

  • 现象: dmesg "page allocation failure: order:4"
  • 根因: 频繁 alloc/free skb 拆散所有连续大块
  • 修复: DPDK 用 hugepage 预分配

坑 3: skb 泄漏

  • 现象: /proc/slabinfo skb active_objs 只增不减
  • 根因: netfilter drop 未调 kfree_skb
  • 定位: bpftrace -e 'k:alloc_skb { @a=kstack } k:kfree_skb { @f=kstack }'

坑 4: GFP_ATOMIC 在中断上下文耗光

  • 现象: 高吞吐下 alloc_skb 返回 NULL
  • 根因: GFP_ATOMIC 不触发页面回收,分配池浅
  • 修复: 预分配内存池,或在进程上下文用 GFP_KERNEL

坑 5: per-CPU cache false sharing

  • 现象: 多核并发分配性能不线性增长
  • 根因: s->cpu_slab[0] 和 s->cpu_slab[1] 在同一 cache line
  • 修复: ____cacheline_aligned_in_smp 或 DPDK __rte_cache_aligned

七、检验标准

  • 能解释 buddy 分裂/合并 + 会读 /proc/buddyinfo
  • 能画 slub 三级分配: freelist → partial slab → buddy
  • 能讲 kmem_cache_cpu 如何无锁 + 和 DPDK mempool 对照
  • 5 个坑都知道怎么定位
  • 能在 /proc/slabinfo 找到 skb cache 并解读字段