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>
This commit is contained in:
Executable
+147
@@ -0,0 +1,147 @@
|
||||
# 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 无锁分配
|
||||
|
||||
```c
|
||||
// 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 并解读字段
|
||||
Reference in New Issue
Block a user