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:
@@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.vitepress/cache/
|
||||
.vitepress/dist/
|
||||
@@ -0,0 +1,82 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
|
||||
export default defineConfig({
|
||||
title: 'DPDK → DPU 学习笔记',
|
||||
description: 'DPDK learning roadmap, uNOS design, home gateway product specs, and knowledge base',
|
||||
lang: 'zh-CN',
|
||||
base: '/',
|
||||
|
||||
themeConfig: {
|
||||
nav: [
|
||||
{ text: '首页', link: '/' },
|
||||
{ text: '学习路线', link: '/roadmap' },
|
||||
{ text: '学习笔记', link: '/notes/' },
|
||||
{ text: '知识库', link: '/kb' },
|
||||
{ text: '产品设计', link: '/products' },
|
||||
],
|
||||
|
||||
sidebar: {
|
||||
'/notes/': [
|
||||
{
|
||||
text: 'Phase 0 — 基础',
|
||||
items: [
|
||||
{ text: 'Day01: PCIe 拓扑', link: '/notes/phase0/day01-pcie-topology' },
|
||||
{ text: 'Day02: 内核收包路径', link: '/notes/phase0/day02-kernel-rx-path' },
|
||||
{ text: 'Day03: 内核内存管理', link: '/notes/phase0/day03-kernel-memory' },
|
||||
{ text: 'Day04: RCU & Lock-free', link: '/notes/phase0/day04-rcu-lockfree' },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Week 1 (Jul 21+)',
|
||||
items: [
|
||||
{ text: 'Day001: PCIe 拓扑深度分析', link: '/notes/week1/day001-pcie-topology' },
|
||||
]
|
||||
}
|
||||
],
|
||||
'/products/': [
|
||||
{
|
||||
text: '产品文档',
|
||||
items: [
|
||||
{ text: 'DPDK Home Gateway 产品方案', link: '/products/home-gateway' },
|
||||
{ text: 'uNOS 设计规格', link: '/products/unos-design' },
|
||||
{ text: 'uNOS 学习方法论', link: '/products/unos-method' },
|
||||
]
|
||||
}
|
||||
],
|
||||
'/kb/': [
|
||||
{
|
||||
text: '知识库',
|
||||
items: [
|
||||
{ text: '索引', link: '/kb/' },
|
||||
{ text: '2026-08 更新', link: '/kb/2026-08' },
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
|
||||
socialLinks: [
|
||||
{ icon: 'github', link: 'http://localhost:3000/wxj/dpdk-roadmap-v3' }
|
||||
],
|
||||
|
||||
search: {
|
||||
provider: 'local',
|
||||
options: {
|
||||
translations: {
|
||||
button: { buttonText: '搜索文档' },
|
||||
modal: { displayDetails: '显示详情', noResultsText: '无结果', resetButtonTitle: '重置' }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
outline: { level: [2, 3], label: '目录' },
|
||||
docFooter: { prev: '上一篇', next: '下一篇' },
|
||||
darkModeSwitchLabel: '深色模式',
|
||||
sidebarMenuLabel: '菜单',
|
||||
returnToTopLabel: '回到顶部',
|
||||
lastUpdated: { text: '最后更新' },
|
||||
},
|
||||
|
||||
markdown: {
|
||||
lineNumbers: true,
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,97 @@
|
||||
/* Custom styles for DPDK → DPU Blog */
|
||||
|
||||
:root {
|
||||
--vp-c-brand-1: #2563eb;
|
||||
--vp-c-brand-2: #3b82f6;
|
||||
--vp-c-brand-3: #1d4ed8;
|
||||
--vp-c-brand-soft: rgba(37, 99, 235, 0.14);
|
||||
|
||||
--vp-font-family-base: 'Inter', 'Noto Sans SC', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
--vp-font-family-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace;
|
||||
}
|
||||
|
||||
/* Hero section tweaks */
|
||||
.VPHero .name {
|
||||
background: linear-gradient(135deg, #2563eb, #06b6d4);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.VPHero .text {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
/* Blog-like content */
|
||||
.vp-doc h1 {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.vp-doc h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
padding-bottom: 0.3em;
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
.vp-doc div[class*='language-'] {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.vp-doc code {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
.vp-doc :not(pre) > code {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 4px;
|
||||
padding: 0.15em 0.4em;
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
.vp-doc table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
|
||||
.vp-doc th {
|
||||
background: var(--vp-c-bg-soft);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.vp-doc tr:nth-child(even) {
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
/* Timeline-style for notes */
|
||||
.vp-doc ul:has(li input[type="checkbox"]) {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
/* Print-friendly */
|
||||
@media print {
|
||||
.VPNav, .VPSidebar, .VPFooter {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark mode tweaks */
|
||||
.dark .VPHero .name {
|
||||
background: linear-gradient(135deg, #60a5fa, #22d3ee);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.dark .vp-doc div[class*='language-'] {
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import './custom.css'
|
||||
|
||||
export default DefaultTheme
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: "DPDK → DPU"
|
||||
text: "学习笔记与开发日志"
|
||||
tagline: 从零到 DPU 架构师 — 18 个月系统学习路线
|
||||
actions:
|
||||
- theme: brand
|
||||
text: 开始学习
|
||||
link: /roadmap
|
||||
- theme: alt
|
||||
text: 学习笔记
|
||||
link: /notes/
|
||||
|
||||
features:
|
||||
- title: 📚 系统学习路线
|
||||
details: 546 天 DPDK 到 DPU 完整路线图,涵盖网卡驱动、协议栈、SAI 适配、DPU 架构设计
|
||||
- title: 🏗️ 产品化实践
|
||||
details: uNOS 用户态网络操作系统 + 家庭网关网络加速器,从设计规格到硬件 BOM
|
||||
- title: ✍️ 每日笔记
|
||||
details: Phase 0-3 逐日学习记录,PCIe 拓扑、内核协议栈、RCU、DPDK 源码分析
|
||||
- title: 🔧 实战代码
|
||||
details: MPSC 无锁队列、dpdk-ws-client、libmtcp-v3、SAI-DPDK 适配器、Ring Benchmark
|
||||
---
|
||||
|
||||
## 最近更新
|
||||
|
||||
### 学习笔记
|
||||
|
||||
- [Day001: PCIe 拓扑深度分析 (Jul 21, 2026)](/notes/week1/day001-pcie-topology)
|
||||
- [Day04: RCU 与 Lock-free 编程](/notes/phase0/day04-rcu-lockfree)
|
||||
- [Day03: 内核内存管理](/notes/phase0/day03-kernel-memory)
|
||||
- [Day02: 内核收包路径分析](/notes/phase0/day02-kernel-rx-path)
|
||||
- [Day01: PCIe 拓扑 + 讲座笔记](/notes/phase0/day01-pcie-topology)
|
||||
|
||||
### 最新知识库
|
||||
|
||||
- [2026-08 知识更新](/kb/2026-08)
|
||||
|
||||
---
|
||||
|
||||
*本站基于 [VitePress](https://vitepress.dev) 构建,所有 .md 文档均可直接渲染。*
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
# 2026-08
|
||||
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
# 知识库
|
||||
|
||||
DPDK 学习与应用开发的知识积累。
|
||||
|
||||
> 工作中遇到的问题 → 5 分钟记一行 → 积累成面试素材 + uNOS 设计输入。
|
||||
|
||||
## 每月记录
|
||||
|
||||
[2026-08 更新](./2026-08.md)
|
||||
|
||||
## 知识分类
|
||||
|
||||
### DPDK 核心
|
||||
|
||||
- 环境抽象层 (EAL)
|
||||
- 内存管理 (Mbuf, Memzone, Mempool)
|
||||
- 轮询模式驱动 (PMD)
|
||||
- 无锁队列 (rte_ring)
|
||||
- 报文转发框架
|
||||
|
||||
### Linux 内核对照
|
||||
|
||||
- 网络协议栈收发包路径
|
||||
- PCIe 设备枚举与驱动
|
||||
- 内存管理 (Buddy, Slab, CMA)
|
||||
- 同步机制 (RCU, Spinlock)
|
||||
- 中断与 NAPI
|
||||
|
||||
### DPU 架构
|
||||
|
||||
- BlueField / Octeon 对比
|
||||
- SAI 交换机抽象接口
|
||||
- OVS 数据路径卸载
|
||||
- VirtIO / SR-IOV
|
||||
- DMA 引擎
|
||||
|
||||
### 性能优化
|
||||
|
||||
- Cache 友好设计
|
||||
- NUMA 亲和性
|
||||
- Huge Pages
|
||||
- 批处理与向量化
|
||||
- 零拷贝技术
|
||||
@@ -0,0 +1,39 @@
|
||||
# 学习笔记
|
||||
|
||||
> DPDK → DPU 学习路线每日记录
|
||||
|
||||
## Phase 0 — 基础夯实
|
||||
|
||||
| Day | 主题 | 日期 |
|
||||
|-----|------|------|
|
||||
| [Day 01](/notes/phase0/day01-pcie-topology) | PCIe 拓扑结构 | Jun 2026 |
|
||||
| [Day 02](/notes/phase0/day02-kernel-rx-path) | 内核收包路径分析 | Jun 2026 |
|
||||
| [Day 03](/notes/phase0/day03-kernel-memory) | 内核内存管理 | Jun 2026 |
|
||||
| [Day 04](/notes/phase0/day04-rcu-lockfree) | RCU & Lock-free 编程 | Jun 2026 |
|
||||
|
||||
## Week 1 (起算: July 21, 2026)
|
||||
|
||||
| Day | 主题 |
|
||||
|-----|------|
|
||||
| [Day 001](/notes/week1/day001-pcie-topology) | PCIe 拓扑深度分析 |
|
||||
|
||||
## Phase 1 — DPDK 核心 (待更新)
|
||||
|
||||
- [ ] EAL 初始化流程
|
||||
- [ ] Mbuf 内存池管理
|
||||
- [ ] PMD 驱动模型
|
||||
- [ ] rte_ring 无锁队列
|
||||
- [ ] Flow Bifurcation
|
||||
|
||||
## Phase 2 — 协议栈 & 加速 (待更新)
|
||||
|
||||
- [ ] 用户态 TCP/IP (libmtcp-v3)
|
||||
- [ ] 性能基准测试
|
||||
- [ ] 硬件卸载 (RSS, TSO, LRO)
|
||||
|
||||
## Phase 3 — DPU 架构 (待更新)
|
||||
|
||||
- [ ] SAI-DPDK 适配
|
||||
- [ ] OVS 卸载
|
||||
- [ ] SONiC 集成
|
||||
- [ ] DPU 架构设计文档
|
||||
Executable
+127
@@ -0,0 +1,127 @@
|
||||
# Phase 0 · Day 1: PCIe 总线拓扑与 Config Space
|
||||
|
||||
---
|
||||
|
||||
## 一、BDF 地址:每个设备的唯一身份
|
||||
|
||||
```
|
||||
0000 : 02 : 01 . 0
|
||||
│ │ │ └── Function (0~7) — 一个物理芯片可以有多个功能
|
||||
│ │ └────── Device (0~31) — 同一总线上最多 32 个设备
|
||||
│ └─────────── Bus (0~255) — 256 条总线
|
||||
└───────────────── Domain (0~65535) — 多 CPU socket 系统有多个 domain
|
||||
|
||||
单 CPU 系统 domain=0000。多路服务器每个 socket 一个 domain。
|
||||
```
|
||||
|
||||
DPDK 通过 BDF 地址接管网卡:`dpdk-devbind.py -b vfio-pci 02:01.0`
|
||||
|
||||
### 为什么需要三级?
|
||||
|
||||
PCI 是树形拓扑。每个 Bridge 引入一条新 bus。256 条 bus × 32 设备 × 8 功能 = 65536 个可寻址功能。如果没有 bus 层级,只靠 5-bit device 最多 32 个设备——一张显卡+一张网卡就满了。
|
||||
|
||||
## 二、拓扑中的四种角色
|
||||
|
||||
| 角色 | 你的实例 | 职责 |
|
||||
|------|---------|------|
|
||||
| **Root Complex** | `00:00.0` | CPU 和 PCIe 世界的桥梁,把 load/store 翻译成 TLP |
|
||||
| **PCI Bridge** | `00:11.0` → Bus 02 | 传统 PCI 桥,用 subtractive decode 转发未认领的访问 |
|
||||
| **PCIe Root Port** | `00:15.0` → Bus 03 | PCIe 原生桥,点对点链路,支持更高带宽 |
|
||||
| **Endpoint** | `02:01.0` `03:00.0` | 叶子节点,实际的网卡设备 |
|
||||
|
||||
关键差异:`02:01.0` (e1000) 是挂在传统 PCI Bridge 后面的老式设备,多条设备共享 Bus 02。`03:00.0` (VMXNET3) 是 PCIe Endpoint,独享 Bus 03 的整条链路(5GT/s × x32)。
|
||||
|
||||
## 二、PCIe 拓扑
|
||||
|
||||
```
|
||||
[0000:00] ← Root Complex (Host Bridge)
|
||||
│ CPU 访问 PCIe 设备的唯一入口
|
||||
│
|
||||
├── 11.0 → [02] ← PCI Bridge (老式总线)
|
||||
│ ├── 01.0 e1000 (Endpoint)
|
||||
│ └── 04.0 e1000 (Endpoint)
|
||||
│
|
||||
└── 15.0 → [03] ← PCIe Root Port (真正的 PCI Express)
|
||||
└── 00.0 VMXNET3 (Endpoint)
|
||||
```
|
||||
|
||||
数据流:CPU → Root Complex → TLP 封装 → PCIe 链路 → Endpoint。
|
||||
|
||||
## 三、Config Space 完整解码
|
||||
|
||||
e1000 (02:01.0) 的 256 字节 Config Space,逐字段解码:
|
||||
|
||||
| Offset | 字段 | Hex 值 | 含义 |
|
||||
|--------|------|--------|------|
|
||||
| 0x00-01 | Vendor ID | 0x8086 | Intel |
|
||||
| 0x02-03 | Device ID | 0x100f | 82545EM |
|
||||
| 0x04-05 | Command | 0x0113 | IO+Mem enable, BusMaster OFF |
|
||||
| 0x0a-0b | Class Code | 0x020000 | Network Controller |
|
||||
| 0x0e | Header Type | 0x00 | Endpoint |
|
||||
| 0x10-13 | BAR0 | 0xfd5c0004 | Memory@0xfd5c0000, 64bit, 128KB, Non-Prefetchable |
|
||||
| 0x18-1b | BAR2 | 0xfdff0004 | Memory@0xfdff0000, 64KB |
|
||||
| 0x20-23 | BAR4 | 0x00002001 | IO Port@0x2000 |
|
||||
| 0x34 | Capability Ptr | 0xdc | 链表起点 |
|
||||
| 0x3c | Interrupt Line | 0x07 | IRQ 7 |
|
||||
| 0x3d | Interrupt Pin | 0x01 | INTA# |
|
||||
|
||||
BAR flag bit 解码: bit0=Memory/IO, bit[2:1]=32b/64b, bit3=Prefetchable。`0xfd5c0004` 中的 `4` = 0100₂ → 64-bit Memory, Non-Prefetchable。真实基址 = 0xfd5c0004 & ~0xF = 0xfd5c0000。
|
||||
|
||||
Capability 链表: 0xdc→PM(ID=1)→0xe4→PCI-X(ID=7)→0x00(End)。
|
||||
|
||||
## 四、VMXNET3 对比 (真正的 PCIe 设备)
|
||||
|
||||
| | e1000 | VMXNET3 |
|
||||
|---|---|---|
|
||||
| 总线 | PCI-X (老) | PCIe v2.0 |
|
||||
| Capability | PM + PCI-X | PM + PCIe Express + MSI + MSI-X + Serial# |
|
||||
| MSI-X | 无 | 25 向量, BAR2 offset 0, PBA offset 0x1000 |
|
||||
|
||||
## 五、实战踩坑
|
||||
|
||||
### 坑 1: Bus Master 没开
|
||||
- **现象**: DPDK 启动后一个包都收不到
|
||||
- **根因**: Command bit2=0 → 网卡不能 DMA
|
||||
- **定位**: `lspci -vvv | grep "BusMaster"`
|
||||
- **修复**: `pci_set_master(pdev)` 或 `setpci -s XX:XX.X COMMAND=0x117`
|
||||
|
||||
### 坑 2: 64-bit BAR 误读
|
||||
- **现象**: 物理地址 >4GB 时 MMIO 地址错误
|
||||
- **根因**: 只用 uint32_t 读 BAR0 低 32 位
|
||||
- **修复**: 用 `resource_size_t pci_resource_start(pdev, 0)`
|
||||
|
||||
### 坑 3: MSI-X 向量不够
|
||||
- **现象**: 多队列网卡性能不增长
|
||||
- **根因**: pci_alloc_irq_vectors 返回数 < RX 队列数
|
||||
- **定位**: `cat /proc/interrupts | grep PCI-MSI | wc -l`
|
||||
- **修复**: 增大内核 MSI-X 限制或 BIOS 调整
|
||||
|
||||
### 坑 4: PCIe 链路降速
|
||||
- **现象**: 100Gbps 网卡只能跑 25Gbps
|
||||
- **定位**: `lspci -vvv | grep -E "LnkCap:|LnkSta:"` → Speed 2.5GT/s
|
||||
- **修复**: 检查物理槽位宽度、PCIe 延长线、BIOS 设置
|
||||
|
||||
### 坑 5: AER 静默数据损坏
|
||||
- **现象**: 偶尔出现 mbuf 数据乱码但不 crash
|
||||
- **定位**: `dmesg | grep AER` → Uncorrectable Error
|
||||
- **修复**: 必须换硬件,链路已不可靠
|
||||
|
||||
## 六、核心认知
|
||||
|
||||
CPU 操作网卡的全路径:
|
||||
|
||||
```
|
||||
读 Config Space → 匹配 Vendor/Device ID → 确定驱动
|
||||
→ pci_enable_device → ioremap(BAR0) → 拿到虚拟地址
|
||||
→ ioread32/iowrite32 操作寄存器 → 网卡开始工作
|
||||
```
|
||||
|
||||
BAR 是 MMIO 的入口,Config Space 是身份证,Capability 链表是扩展机制。
|
||||
|
||||
## 七、检验标准
|
||||
- [ ] 能解释 BDF 地址含义
|
||||
- [ ] 能手写 Config Space 前 16 字节字段
|
||||
- [ ] 能从 hex 值解码 BAR 的基址和大小
|
||||
- [ ] 能画 Capability 链表结构
|
||||
- [ ] 能对比 e1000(PCI-X) vs VMXNET3(PCIe v2)
|
||||
- [ ] 能解释为什么 Bus Master bit 不置位就没 DMA
|
||||
Executable
+104
@@ -0,0 +1,104 @@
|
||||
# Phase 0 · Day 2: 内核中断 + 网络收包路径
|
||||
|
||||
---
|
||||
|
||||
## 一、你的系统数据
|
||||
|
||||
```
|
||||
IRQ 56 (ens160-rxtx-0): CPU0:1329万 CPU1:191万 CPU2:112万 CPU3:676万
|
||||
IRQ 57 (ens160-rxtx-1): CPU0:11万 CPU1:33万 CPU2:36万 CPU3:38万
|
||||
IRQ 58 (ens160-rxtx-2): CPU0:19万 CPU1:37万 CPU2:43万 CPU3:42万
|
||||
IRQ 59 (ens160-rxtx-3): CPU0:30万 CPU1:39万 CPU2:52万 CPU3:52万
|
||||
IRQ 60 (ens160-event): 全 0 (事件队列, 未使用)
|
||||
|
||||
NET_RX 软中断: CPU0:1339万 CPU1:436万 CPU2:381万 CPU3:791万
|
||||
ksoftirqd: 每个 CPU 一个 (PID 16/24/30/36)
|
||||
```
|
||||
|
||||
- 4 个 MSI-X 向量 → 4 个 RX 队列 → 4 个 CPU 并行收包
|
||||
- IRQ 56 在 CPU0 上触发了 1329 万次(主流量),CPU1-3 只触发了少量——IRQ affinity 配置不均
|
||||
|
||||
## 二、收包 6 道关卡
|
||||
|
||||
```
|
||||
关卡1: 硬件中断
|
||||
网卡 DMA 写 RX ring → MSI-X 中断 → APIC → CPU → IDT → irq_handler
|
||||
此时在硬中断上下文,所有中断被屏蔽
|
||||
|
||||
关卡2: NAPI 调度
|
||||
irq_handler → napi_schedule → 关网卡中断 + 触发 NET_RX_SOFTIRQ → 返回
|
||||
源码: net/core/dev.c ____napi_schedule (line 4551)
|
||||
|
||||
关卡3: ksoftirqd
|
||||
ksoftirqd 被唤醒 → __do_softirq → net_rx_action → 遍历 poll_list
|
||||
源码: kernel/softirq.c + net/core/dev.c
|
||||
|
||||
关卡4: 驱动 poll → netif_receive_skb
|
||||
从 RX descriptor ring 取包 → alloc_skb → memcpy → netif_receive_skb(skb)
|
||||
源码: net/core/dev.c netif_receive_skb (line 5899)
|
||||
|
||||
关卡5: 协议分发 (L2→L3)
|
||||
__netif_receive_skb_core → skb->protocol → 查 ptype_base 哈希表 → ip_rcv()
|
||||
源码: net/core/dev.c __netif_receive_skb_core (line 5434)
|
||||
net/ipv4/ip_input.c ip_rcv (line 560)
|
||||
协议注册: net/ipv4/af_inet.c dev_add_pack (line 2065)
|
||||
|
||||
关卡6: TCP 收包 (L3→L4→socket)
|
||||
ip_rcv → NF_HOOK → ip_local_deliver → tcp_v4_rcv
|
||||
→ __inet_lookup_established → tcp_rcv_established (fast path!)
|
||||
→ tcp_queue_rcv → sk_data_ready → wake_up → epoll 返回
|
||||
源码: net/ipv4/tcp_ipv4.c (line 1999)
|
||||
net/ipv4/tcp_input.c tcp_rcv_established
|
||||
```
|
||||
|
||||
## 三、DPDK 为什么快
|
||||
|
||||
| 内核步骤 | 耗时 | DPDK |
|
||||
|---------|------|------|
|
||||
| MSI-X → IDT → handler | ~3us | PMD 轮询, 无中断 |
|
||||
| napi_schedule + ksoftirqd 调度 | ~5us | PMD 线程永不睡眠 |
|
||||
| alloc_skb (slab 锁) | ~1us | mbuf 从 per-lcore cache 弹出 |
|
||||
| memcpy DMA→skb | ~2us | DMA 直接写 mbuf, 零拷贝 |
|
||||
| ptype_base 查找 | ~0.3us | 应用自己解析协议 |
|
||||
| Netfilter HOOK | ~5us | 不需要 |
|
||||
| ehash 查 socket | ~1us | rte_hash_lookup |
|
||||
| copy_to_user | ~3us | 零拷贝 |
|
||||
| **总计** | **~20us** | **~1us** |
|
||||
|
||||
## 四、实战踩坑
|
||||
|
||||
### 坑 1: 中断全绑 CPU0
|
||||
- **现象**: top 看 CPU0 si=99%, 其他 CPU 空闲
|
||||
- **根因**: smp_affinity 默认只开 CPU0
|
||||
- **定位**: `cat /proc/irq/56/smp_affinity`
|
||||
- **修复**: `echo ff > /proc/irq/56/smp_affinity`
|
||||
|
||||
### 坑 2: NAPI budget 耗尽
|
||||
- **现象**: 100G 线速达不到,`ethtool -S` 看 rx_missed 增长
|
||||
- **根因**: net_rx_action 默认一次最多 300 包,收不够就返回
|
||||
- **定位**: `cat /proc/net/softnet_stat`, 第三列 = "times queue full"
|
||||
- **修复**: 增大 net.core.netdev_budget,或换 DPDK
|
||||
|
||||
### 坑 3: ksoftirqd 被抢占
|
||||
- **现象**: 收包延迟 P99 抖动严重
|
||||
- **根因**: ksoftirqd 不是实时线程,被磁盘 I/O 抢占
|
||||
- **定位**: `perf sched latency`
|
||||
- **修复**: `chrt -f 1 $(pidof ksoftirqd/0)` 或换 DPDK PMD
|
||||
|
||||
### 坑 4: Netfilter 静默丢包
|
||||
- **现象**: tcpdump 能看到包,应用收不到
|
||||
- **根因**: Docker/k8s 自动注入 iptables drop 规则
|
||||
- **定位**: `iptables -L -v -n`, 看每条规则的计数器
|
||||
|
||||
### 坑 5: TCP backlog 满
|
||||
- **现象**: 客户端 connect 超时
|
||||
- **根因**: accept queue=128, 第 129 个 SYN 被内核静默丢弃
|
||||
- **定位**: `netstat -s | grep overflow`
|
||||
- **修复**: `echo 4096 > /proc/sys/net/core/somaxconn`
|
||||
|
||||
## 五、检验标准
|
||||
- [ ] 能画中断→NAPI→ksoftirqd→poll→netif_receive_skb→ip_rcv→tcp_v4_rcv 完整调用链
|
||||
- [ ] 能解释 NAPI 为什么要关中断、用软中断
|
||||
- [ ] 能说 ptype_base 的作用
|
||||
- [ ] 能列 DPDK 比内核快 ≥5 个原因
|
||||
- [ ] 能在 /proc/interrupts 和 /proc/softirqs 找到你的网卡
|
||||
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 并解读字段
|
||||
Executable
+135
@@ -0,0 +1,135 @@
|
||||
# Phase 0 · Day 4: RCU 无锁机制 + 手写 MPSC 队列
|
||||
|
||||
---
|
||||
|
||||
## 一、RCU 核心思想
|
||||
|
||||
### 1.1 rwlock 为什么不行
|
||||
|
||||
在多核系统中,传统 rwlock 的问题是:即使 100% 都是读操作,每个 CPU 的 `read_lock` 都要原子修改同一个 cache line → 32 核时该 cache line 在 32 个核之间乒乓(cache line bouncing)→ 性能随核数退化。
|
||||
|
||||
### 1.2 RCU 的做法
|
||||
|
||||
```
|
||||
读者 (数据面): 写者 (控制面):
|
||||
rcu_read_lock() copy data → modify copy
|
||||
→ preempt_disable() rcu_assign_pointer(ptr, new)
|
||||
(非 PREEMPT_RCU 内核) synchronize_rcu() ← 等所有老读者退出
|
||||
ptr = rcu_dereference(gp) kfree(old)
|
||||
→ READ_ONCE + lockdep
|
||||
use(ptr) ← 无锁! 纯读!
|
||||
rcu_read_unlock()
|
||||
→ preempt_enable()
|
||||
```
|
||||
|
||||
核心权衡: **用延迟释放换取读者零开销**。
|
||||
|
||||
### 1.3 核心 API
|
||||
|
||||
| API | 作用 | 场景 |
|
||||
|-----|------|------|
|
||||
| `rcu_read_lock/unlock` | 标记读临界区 (非PREEMPT下=preempt_disable) | 读者遍历 |
|
||||
| `rcu_dereference(p)` | READ_ONCE + lockdep检查 | **必须用!** 裸读=bug |
|
||||
| `rcu_assign_pointer(p,v)` | smp_store_release 发布新指针 | 写者更新 |
|
||||
| `synchronize_rcu()` | 阻塞等 grace period 结束 | 模块卸载 (不能中断上下文) |
|
||||
| `call_rcu(head, func)` | 异步等 GP → 调回调 | 热路径释放 (中断安全) |
|
||||
|
||||
### 1.4 Grace Period 机制
|
||||
|
||||
每个 CPU 经历一次 quiescent state (上下文切换或退出 rcu_read_lock) → 所有 CPU 都经历 → GP 结束 → 旧数据安全释放。
|
||||
|
||||
---
|
||||
|
||||
## 二、RCU 在 DPDK 中的体现
|
||||
|
||||
| RCU 概念 | DPDK rte_ring 对应 |
|
||||
|----------|-------------------|
|
||||
| 读者不阻塞 | consumer 读 `prod.tail` 纯读, 无锁 |
|
||||
| rcu_dereference (READ_ONCE) | `*old_head = r->prod.head` + smp_rmb() |
|
||||
| rcu_assign_pointer (release) | CAS release/acquire 语义 |
|
||||
| Grace Period | head/tail 追赶: tail 更新前不覆盖旧数据 |
|
||||
| 延迟回收 | per-lcore cache flush → bulk 操作分摊开销 |
|
||||
|
||||
---
|
||||
|
||||
## 三、实战踩坑 (RCU)
|
||||
|
||||
### 坑 1: Grace Period 过长 → OOM
|
||||
- **现象**: kmalloc + free 循环中 call_rcu 积压, 内存耗尽
|
||||
- **根因**: 长期 rcu_read_lock / NO_HZ idle CPU / CONFIG_RCU_LAZY
|
||||
- **定位**: `cat /sys/kernel/debug/rcu/rcu_preempt | grep cblist`
|
||||
- **修复**: `call_rcu_hurry()` 或 `echo 1 > /sys/kernel/rcu_expedited`
|
||||
|
||||
### 坑 2: 漏掉 rcu_dereference → use-after-free
|
||||
- **现象**: 偶发性 crash, KASAN 报告 use-after-free
|
||||
- **根因**: 编译器优化导致指针被多次读取, 第二次读时已被释放
|
||||
- **定位**: sparse: `make C=2`, KASAN: `dmesg | grep KASAN`
|
||||
- **修复**: 永远用 `rcu_dereference(p)` 而不是裸读 `p`
|
||||
|
||||
### 坑 3: rcu_read_lock 内睡眠
|
||||
- **现象**: 内核 splat: "scheduling while atomic"
|
||||
- **根因**: 非 PREEMPT_RCU 下 rcu_read_lock 禁止抢占, 不能睡眠
|
||||
- **修复**: 用 SRCU (sleepable RCU) 或把睡眠移到临界区外
|
||||
|
||||
### 坑 4: synchronize_rcu 在中断上下文调用
|
||||
- **现象**: 内核 panic
|
||||
- **根因**: synchronize_rcu 会睡眠, 中断上下文不可
|
||||
- **修复**: 用 call_rcu 异步
|
||||
|
||||
### 坑 5: list_for_each_entry_rcu 遍历时写者并发删除
|
||||
- **现象**: 读者遍历到已删除节点 → crash
|
||||
- **根因**: 写者 list_del_rcu 后立即 kfree (没等 GP)
|
||||
- **修复**: 写者必须在 synchronize_rcu/call_rcu 之后才能释放
|
||||
|
||||
---
|
||||
|
||||
## 四、编码题: MPSC 无锁队列
|
||||
|
||||
### 需求
|
||||
|
||||
```
|
||||
题目: 用 C11 atomic 实现 MPSC 无锁队列
|
||||
|
||||
硬性要求:
|
||||
1. 固定大小环形 buffer (大小 2 的幂)
|
||||
2. enqueue: 多线程安全 (CAS 抢 head)
|
||||
3. dequeue: 单线程 (不需要原子操作)
|
||||
4. 2 producer + 1 consumer, 各入队 1000000 次, 总数 = 2000000
|
||||
|
||||
核心算法 (两阶段提交):
|
||||
enqueue:
|
||||
① CAS 抢 head slot
|
||||
② 写入数据
|
||||
③ 通知 consumer (方案A: spin等tail→更新; 方案B: consumer自己检查head)
|
||||
|
||||
推荐方案B (consumer更新tail): producer只CAS head+写数据→结束, 无spin
|
||||
|
||||
参考源码:
|
||||
- rte_ring CAS: source/dpdk-stable-24.11.6/lib/ring/rte_ring_c11_pvt.h:60-128
|
||||
- rte_ring 结构体: source/dpdk-stable-24.11.6/lib/ring/rte_ring_core.h:116-145
|
||||
```
|
||||
|
||||
### 三个陷阱
|
||||
|
||||
1. **ABA**: head 只增不减 (靠32-bit溢出) → 不会有 ABA, 但如果你让 head 回退就会
|
||||
2. **内存屏障位置**: 数据写入必须在 CAS 之前 (CAS release 保证可见)
|
||||
3. **False sharing**: head 和 tail 放不同 cache line (`alignas(64)`)
|
||||
|
||||
### 代码框架
|
||||
|
||||
文件: `code/Phase0/mpsc-queue/mpsc_queue.c`
|
||||
|
||||
### 检验
|
||||
|
||||
```bash
|
||||
gcc -std=c11 -pthread -O2 -Wall -Wextra -o mpsc_test mpsc_queue.c
|
||||
./mpsc_test
|
||||
# PASS: consumed = 2000000 (expected 2000000)
|
||||
```
|
||||
|
||||
## 五、检验标准
|
||||
- [ ] 能解释 RCU 的核心思想 (读者零开销)
|
||||
- [ ] 能说出 rcu_dereference 为什么必须用 (READ_ONCE + lockdep)
|
||||
- [ ] 能解释 grace period 机制
|
||||
- [ ] 能对比 RCU 和 DPDK rte_ring 的设计共同点
|
||||
- [ ] MPSC 队列: 2 producer + 1 consumer 跑通, 2000000 个数据无丢失
|
||||
Executable
+251
@@ -0,0 +1,251 @@
|
||||
# Day 001 (2026-07-21, 周一) · 主题: PCIe总线拓扑
|
||||
|
||||
## 📖 学习 (30min)
|
||||
|
||||
### PCIe体系结构
|
||||
- PCIe是点对点串行总线,替代了旧的PCI/PCI-X并行总线
|
||||
- 分层结构:Root Complex → PCI Bridge → Endpoint
|
||||
- Root Complex集成在CPU中,是CPU和PCIe的桥梁
|
||||
- 每个PCIe设备有唯一的BDF地址:Bus:Device.Function
|
||||
|
||||
### 关键概念
|
||||
1. **Root Complex (RC)**: 连接CPU和PCIe总线,负责:
|
||||
- CPU地址 ↔ PCIe地址转换
|
||||
- 生成PCIe配置请求
|
||||
- 管理中断
|
||||
|
||||
2. **PCI Bridge**: 扩展PCIe端口,像网络交换机
|
||||
- 上游口(Upstream)连RC或其他Bridge
|
||||
- 下游口(Downstream)连设备或其他Bridge
|
||||
|
||||
3. **Endpoint**: 最终设备
|
||||
- 网卡(NIC)、显卡(GPU)、SSD(NVMe)
|
||||
- 通过BAR(Base Address Register)暴露寄存器
|
||||
|
||||
4. **BDF地址**: Bus:Device.Function
|
||||
- Bus: 8位,最多256条总线
|
||||
- Device: 5位,每条总线最多32个设备
|
||||
- Function: 3位,每个设备最多8个功能
|
||||
|
||||
## 🔧 动手 (60min)
|
||||
|
||||
### 1. 获取本机PCIe拓扑
|
||||
|
||||
```bash
|
||||
root@ubuntu:~# lspci -tv
|
||||
-[0000:00]-+-00.0 Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge
|
||||
+-01.0-[01]--
|
||||
+-07.0 Intel Corporation 82371AB/EB/MB PIIX4 ISA
|
||||
+-07.1 Intel Corporation 82371AB/EB/MB PIIX4 IDE
|
||||
+-07.3 Intel Corporation 82371AB/EB/MB PIIX4 ACPI
|
||||
+-07.7 VMware Virtual Machine Communication Interface
|
||||
+-0f.0 VMware SVGA II Adapter
|
||||
+-10.0 Broadcom / LSI 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI
|
||||
+-11.0-[02]--+-00.0 VMware USB1.1 UHCI Controller
|
||||
| +-01.0 Intel Corporation 82545EM Gigabit Ethernet Controller (Copper)
|
||||
| +-02.0 VMware USB2 EHCI Controller
|
||||
| +-03.0 VMware SATA AHCI controller
|
||||
| \-04.0 Intel Corporation 82545EM Gigabit Ethernet Controller (Copper)
|
||||
+-15.0-[03]----00.0 VMware VMXNET3 Ethernet Controller
|
||||
+-15.1-[04]--
|
||||
...
|
||||
```
|
||||
|
||||
**拓扑分析**:
|
||||
- 这是VMware虚拟机环境
|
||||
- 主桥:Intel 440BX/ZX/DX (很老的芯片组,1998年)
|
||||
- 总线00: Root Complex + 传统PCI设备
|
||||
- 总线01: 空桥(可能是PCIe扩展槽)
|
||||
- 总线02: PCI Bridge 00:11.0 下游,连接4个设备
|
||||
- 总线03: PCI Bridge 00:15.0 下游,连接VMXNET3网卡
|
||||
|
||||
### 2. 读取网卡详细信息 (02:01.0)
|
||||
|
||||
```bash
|
||||
root@ubuntu:~# lspci -vvv -s 02:01.0
|
||||
02:01.0 Ethernet controller: Intel Corporation 82545EM Gigabit Ethernet Controller (Copper) (rev 01)
|
||||
DeviceName: Ethernet2
|
||||
Subsystem: VMware PRO/1000 MT Single Port Adapter
|
||||
Physical Slot: 33
|
||||
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV+ VGASnoop- ParErr- Stepping- SERR+ FastB2B- DisINTx-
|
||||
Status: Cap+ 66MHz+ UDF- FastB2B- ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
|
||||
Latency: 0 (63750ns min), Cache Line Size: 64 bytes
|
||||
Interrupt: pin A routed to IRQ 19
|
||||
Region 0: Memory at fd5c0000 (64-bit, non-prefetchable) [size=128K]
|
||||
Region 2: Memory at fdff0000 (64-bit, non-prefetchable) [size=64K]
|
||||
Region 4: I/O ports at 2000 [size=64]
|
||||
Expansion ROM at fd500000 [virtual] [disabled] [size=64K]
|
||||
Capabilities: [dc] Power Management version 2
|
||||
Capabilities: [e4] PCI-X non-bridge device
|
||||
Kernel driver in use: e1000
|
||||
Kernel modules: e1000
|
||||
```
|
||||
|
||||
**关键信息解码**:
|
||||
- **Vendor ID**: 0x8086 (Intel)
|
||||
- **Device ID**: 0x100F (82545EM)
|
||||
- **Class**: 0x020000 (Network Controller)
|
||||
- **BAR0**: Memory at fd5c0000, 128KB (MMIO寄存器)
|
||||
- **BAR2**: Memory at fdff0000, 64KB (可能是Flash或额外寄存器)
|
||||
- **BAR4**: I/O ports at 2000, 64B (传统I/O端口)
|
||||
- **IRQ**: 19 (传统INTx中断)
|
||||
- **Driver**: e1000 (内核驱动)
|
||||
|
||||
### 3. 手动解码Config Space前16字节
|
||||
|
||||
Config Space前64字节结构:
|
||||
```
|
||||
Offset 0x00: Vendor ID (2B) + Device ID (2B)
|
||||
Offset 0x04: Command (2B) + Status (2B)
|
||||
Offset 0x08: Revision ID (1B) + Class Code (3B)
|
||||
Offset 0x0C: Cache Line Size (1B) + Latency Timer (1B) + Header Type (1B) + BIST (1B)
|
||||
Offset 0x10: BAR0 (4B/8B)
|
||||
Offset 0x14: BAR1 (4B)
|
||||
Offset 0x18: BAR2 (4B/8B)
|
||||
Offset 0x1C: BAR3 (4B)
|
||||
Offset 0x20: BAR4 (4B)
|
||||
Offset 0x24: BAR5 (4B)
|
||||
Offset 0x28: CardBus CIS Pointer (4B)
|
||||
Offset 0x2C: Subsystem Vendor ID (2B) + Subsystem ID (2B)
|
||||
Offset 0x30: Expansion ROM Base Address (4B)
|
||||
Offset 0x34: Reserved (2B) + Capabilities Pointer (1B) + Reserved (1B)
|
||||
Offset 0x38: Reserved (4B)
|
||||
Offset 0x3C: Max Latency (1B) + Min Gnt (1B) + Interrupt Pin (1B) + Interrupt Line (1B)
|
||||
```
|
||||
|
||||
**从lspci -vvv输出解码**:
|
||||
- Vendor ID: 0x8086 (Intel Corporation)
|
||||
- Device ID: 0x100F (82545EM Gigabit Ethernet)
|
||||
- Revision ID: 0x01
|
||||
- Class Code: 0x020000 (02=Network, 00=Ethernet, 00=generic)
|
||||
- Cache Line Size: 64 bytes
|
||||
- Latency Timer: 0
|
||||
- Header Type: 0x00 (普通Endpoint)
|
||||
- Interrupt Pin: INTA# (pin A)
|
||||
- Interrupt Line: IRQ 19
|
||||
|
||||
## 📤 产出
|
||||
|
||||
- 笔记: `notes/2026-07-week1/Day001-2026-07-21-pcie-topology.md`
|
||||
- 拓扑图: 见下方ASCII图
|
||||
|
||||
## ✅ 掌握检验
|
||||
|
||||
1. **它是什么?**
|
||||
PCIe是CPU连接外设的高速串行总线,采用分层结构(Root Complex → Bridge → Endpoint)。本机是VMware虚拟机,模拟Intel 440BX芯片组。
|
||||
|
||||
2. **为什么需要它?**
|
||||
- 替代PCI/PCI-X,提供更高带宽(PCIe 4.0 x16可达64GB/s)
|
||||
- 点对点连接,避免总线竞争
|
||||
- 支持热插拔和电源管理
|
||||
- 本机虽然是传统PCI(66MHz),但VMware模拟了PCIe桥接
|
||||
|
||||
3. **DPDK vs 内核对比?**
|
||||
- **内核**: 通过PCIe枚举发现设备,e1000驱动运行在内核态,应用通过系统调用(socket)访问网络
|
||||
- **DPDK**: 同样枚举PCIe设备,但用VFIO/UIO把设备映射到用户态,应用直接操作BAR0(fd5c0000)的MMIO寄存器,绕过内核协议栈
|
||||
- **关键差异**: DPDK避免了系统调用和内存拷贝,延迟从微秒级降到纳秒级。本机的82545EM是DPDK e1000 PMD支持的网卡。
|
||||
|
||||
## 🐧 内核对照
|
||||
|
||||
- **内核PCIe枚举**: `drivers/pci/probe.c` 的 `pci_scan_bus()`
|
||||
- 读取ACPI MCFG表获取PCIe配置空间基地址
|
||||
- 遍历所有Bus/Device/Function,读取Vendor ID
|
||||
- 发现有效设备后,分配 `struct pci_dev`
|
||||
|
||||
- **DPDK对应**: `drivers/bus/pci/linux/pci.c` 的 `rte_pci_scan()`
|
||||
- 同样读取 `/sys/bus/pci/devices/` 或 MCFG
|
||||
- 但把结果存在用户态数组,不调用内核驱动
|
||||
- 最终通过 `rte_pci_probe()` 绑定到PMD
|
||||
|
||||
- **两者差异**:
|
||||
- 内核:枚举 → 绑定内核驱动(e1000) → 创建net_device(eth0) → 注册网络协议栈
|
||||
- DPDK:枚举 → 绑定VFIO/UIO → 用户态直接访问BAR0(fd5c0000) → 绕过内核
|
||||
|
||||
## 📊 PCIe拓扑图 (ASCII)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ VMware Virtual Machine │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ Intel 440BX/ZX/DX Host Bridge │ │
|
||||
│ │ (00:00.0) │ │
|
||||
│ │ Root Complex │ │
|
||||
│ └──────────────────────────┬──────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌────────────────────┼────────────────────┐ │
|
||||
│ │ │ │ │
|
||||
│ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ │
|
||||
│ │ 00:07.0 │ │ 00:11.0 │ │ 00:15.0 │ │
|
||||
│ │ PIIX4 │ │ PCI │ │ PCI │ │
|
||||
│ │ ISA │ │ Bridge │ │ Bridge │ │
|
||||
│ └────┬────┘ └────┬────┘ └────┬────┘ │
|
||||
│ │ │ │ │
|
||||
│ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ │
|
||||
│ │ 00:07.1 │ │ Bus 02 │ │ Bus 03 │ │
|
||||
│ │ PIIX4 │ │ │ │ │ │
|
||||
│ │ IDE │ │ ┌───────┴───┐ │ ┌───────┴───┐ │
|
||||
│ └─────────┘ │ │ 02:00.0 │ │ │ 03:00.0 │ │
|
||||
│ │ │ USB1.1 │ │ │ VMXNET3 │ │
|
||||
│ ┌─────────┐ │ └───────────┘ │ │ Ethernet │ │
|
||||
│ │ 00:07.3 │ │ ┌───────────┐ │ └───────────┘ │
|
||||
│ │ PIIX4 │ │ │ 02:01.0 │ │ │
|
||||
│ │ ACPI │ │ │ 82545EM │◄───┤ DPDK目标网卡 │
|
||||
│ └─────────┘ │ │ e1000 │ │ (10Gb虚拟) │
|
||||
│ │ │ BAR0: │ │ │
|
||||
│ ┌─────────┐ │ │ fd5c0000 │ │ │
|
||||
│ │ 00:07.7 │ │ └───────────┘ │ │
|
||||
│ │ VMware │ │ ┌───────────┐ │ │
|
||||
│ │ VMCI │ │ │ 02:02.0 │ │ │
|
||||
│ └─────────┘ │ │ USB2.0 │ │ │
|
||||
│ │ └───────────┘ │ │
|
||||
│ ┌─────────┐ │ ┌───────────┐ │ │
|
||||
│ │ 00:0f.0 │ │ │ 02:03.0 │ │ │
|
||||
│ │ SVGA II │ │ │ SATA │ │ │
|
||||
│ └─────────┘ │ └───────────┘ │ │
|
||||
│ │ ┌───────────┐ │ │
|
||||
│ ┌─────────┐ │ │ 02:04.0 │ │ │
|
||||
│ │ 00:10.0 │ │ │ 82545EM │◄───┤ 第二块e1000 │
|
||||
│ │ LSI │ │ │ e1000 │ │ │
|
||||
│ │ SCSI │ │ └───────────┘ │ │
|
||||
│ └─────────┘ └──────────────────┘ │
|
||||
│ │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
|
||||
数据流路径:
|
||||
┌─────┐ ┌─────────────┐ ┌─────────┐ ┌─────────────┐ ┌─────┐
|
||||
│ CPU │───►│ Root Complex│───►│ PCI │───►│ 82545EM │───►│网线 │
|
||||
│ │ │ (440BX) │ │ Bridge │ │ (02:01.0) │ │ │
|
||||
└─────┘ └─────────────┘ │ 00:11.0 │ │ BAR0: │ └─────┘
|
||||
└─────────┘ │ fd5c0000 │
|
||||
│ 128KB MMIO │
|
||||
└─────────────┘
|
||||
|
||||
DPDK数据面路径 (绕过内核):
|
||||
┌─────────┐ ┌─────────────┐ ┌─────────────────────────────┐
|
||||
│ DPDK App│───►│ VFIO/UIO │───►│ 直接读写BAR0 (fd5c0000) │
|
||||
│ (用户态)│ │ (设备绑定) │ │ 控制网卡收发 │
|
||||
└─────────┘ └─────────────┘ └─────────────────────────────┘
|
||||
▲ │
|
||||
│ ┌────────────────────────────────────┘
|
||||
│ │ DMA (直接内存访问)
|
||||
│ ┌────┴────┐
|
||||
└───│ Hugepage │
|
||||
│ 内存 │
|
||||
└─────────┘
|
||||
```
|
||||
|
||||
## 🔍 关键发现
|
||||
|
||||
1. **这是VMware虚拟机**: 440BX是1998年的芯片组,但VMware用PCIe桥接模拟了现代PCIe结构
|
||||
2. **两块e1000网卡**: 02:01.0和02:04.0,都是Intel 82545EM,DPDK完美支持
|
||||
3. **一块VMXNET3**: 03:00.0,VMware准虚拟化网卡,性能更好,DPDK也支持
|
||||
4. **BAR0地址**: fd5c0000,这是物理地址,DPDK通过VFIO映射到用户态虚拟地址
|
||||
5. **Cache Line Size**: 64字节,DPDK的mbuf(2048B)是32个cache line,对齐优化很重要
|
||||
|
||||
## 📚 参考资料
|
||||
|
||||
- 《PCIe体系结构导读》Ch.1-2
|
||||
- 内核: `drivers/pci/probe.c`, `include/linux/pci.h`
|
||||
- DPDK: `drivers/bus/pci/linux/pci.c`, `drivers/net/e1000/`
|
||||
- Intel 82545EM datasheet: https://www.intel.com/content/www/us/en/products/sku/76774/intel-82545em-gigabit-ethernet-controller/specifications.html
|
||||
Generated
+2538
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "blog-site",
|
||||
"version": "1.0.0",
|
||||
"description": "DPDK → DPU Learning Blog — powered by VitePress",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vitepress dev",
|
||||
"build": "vitepress build",
|
||||
"preview": "vitepress preview"
|
||||
},
|
||||
"keywords": ["dpdk", "dpu", "network", "learning", "blog"],
|
||||
"author": "wxj",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"vitepress": "^1.6.4"
|
||||
}
|
||||
}
|
||||
Executable
+349
@@ -0,0 +1,349 @@
|
||||
# 家用 DPDK 网络加速器 — 产品规划文档
|
||||
|
||||
> **创建日期**: 2026-07-13
|
||||
> **关联学习路线**: `2026-06-29-dpdk-roadmap-v3-design.md`
|
||||
> **目标**: 基于 DPDK 做一个低成本家用网络加速器,可以量产销售
|
||||
|
||||
---
|
||||
|
||||
## 一、产品定义
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| 产品名 | 家用网络加速器(暂定) |
|
||||
| 定位 | 插在光猫和路由器之间,即插即用 |
|
||||
| 一句话卖点 | "让千兆宽带真正跑满,全家不卡,不用设置" |
|
||||
| 目标用户 | 家庭用户,不折腾 |
|
||||
| 参考售价 | ¥299(100 台试水,单台毛利 ~¥118) |
|
||||
|
||||
### 使用方式
|
||||
|
||||
```
|
||||
原来: 光猫 ─── 路由器 ─── 全家设备
|
||||
↑ 光猫 NAT 性能差,有人下载全家卡
|
||||
|
||||
加盒子: 光猫 ─── [你的盒子] ─── 路由器 ─── 全家设备
|
||||
↑ ↑
|
||||
桥模式/无感透传 DPDK 线速 + 智能 QoS
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、三大核心功能
|
||||
|
||||
| # | 功能 | 痛点 | 实现原理 |
|
||||
|---|------|------|---------|
|
||||
| 1 | **提速** | 运营商光猫 NAT 差,千兆宽带只能跑 600M | 光猫改桥接,盒子 DPDK 线速 NAT/转发,跑满 940M |
|
||||
| 2 | **不卡** | 有人下载/BT/看视频,其他人游戏延迟爆炸 | DPDK rte_flow 按流特征分类:小包优先、大包限速、视频保底 |
|
||||
| 3 | **零配置** | 用户不想进后台、不想学网络知识 | 开机自动 DHCP 探测 WAN/LAN 方向,默认桥模式透传 |
|
||||
|
||||
### 功能 2 详细:智能流控
|
||||
|
||||
```
|
||||
收包 → rte_flow 五元组分类
|
||||
│
|
||||
├── 小包持续流 (<256B, >100pps)
|
||||
│ → 标记 "游戏/通话/视频会议"
|
||||
│ → 高优先级队列,绝对优先
|
||||
│
|
||||
├── 大包持续流 (>1024B, >50Mbps)
|
||||
│ → 标记 "下载/BT/视频缓冲"
|
||||
│ → 低优先级队列,带宽上限
|
||||
│
|
||||
└── 其他
|
||||
→ 普通优先级,尽力转发
|
||||
```
|
||||
|
||||
> 关键设计:不识别具体应用(不需要 DPI),只靠流特征判断。简单、可靠、私密。
|
||||
|
||||
---
|
||||
|
||||
## 三、硬件 BOM(最低成本,支持 VFIO)
|
||||
|
||||
### 选型理由
|
||||
|
||||
- **J3355B-ITX**: Apollo Lake 起全系支持 VT-d,这是 Intel 平台最便宜的 VT-d 方案
|
||||
- **Intel 82576**: DPDK igb PMD 原生驱动,最老最稳定的网卡之一,二手极便宜
|
||||
- **VFIO > UIO**: VFIO 是 DPDK 官方推荐驱动方式,内核主线永久支持,DMA 安全隔离
|
||||
|
||||
### 逐项 BOM
|
||||
|
||||
| # | 零件 | 规格 | 来源 | 单价 |
|
||||
|---|------|------|------|------|
|
||||
| 1 | 主板 | ASRock J3355B-ITX (双核 2.0GHz, 10W, VT-d) | 闲鱼 | ¥100 |
|
||||
| 2 | 内存 | DDR3L 2GB SODIMM | 闲鱼拆机 | ¥12 |
|
||||
| 3 | 存储 | 8GB mSATA SSD | 闲鱼拆机 | ¥10 |
|
||||
| 4 | 网卡 | Intel 82576 双口千兆 (PCIe 2.0 x4) | 闲鱼拆机 | ¥30 |
|
||||
| 5 | 电源 | 12V 3A 适配器 + DC 5.5×2.5mm 线 | 1688 批发 | ¥8 |
|
||||
| 6 | 外壳 | 3mm 亚克力激光切割 (17×17cm ITX) | 淘宝定制 | ¥10 |
|
||||
| 7 | 铜柱+螺丝 | M3×6 一套 4 颗 | 1688 | ¥2 |
|
||||
| 8 | 导热垫 | 40×40×1mm 硅胶 | — | ¥1 |
|
||||
| | | | **合计** | **¥173** |
|
||||
|
||||
### 备选主板
|
||||
|
||||
| 型号 | CPU | 核数 | VT-d | 价格 | 备注 |
|
||||
|------|-----|------|------|------|------|
|
||||
| ASRock J3355B-ITX | J3355 | 双核 | ✅ | ¥100 | **首选,最便宜 VT-d** |
|
||||
| ASRock J3455-ITX | J3455 | 四核 | ✅ | ¥140 | J3355 缺货时备选 |
|
||||
| ASRock J4205-ITX | J4205 | 四核 | ✅ | ¥160 | 第二备选 |
|
||||
|
||||
### 内存使用分析
|
||||
|
||||
```
|
||||
DPDK hugepage: 64MB
|
||||
Alpine Linux: ~128MB
|
||||
DPDK app: ~50MB
|
||||
空闲缓冲: ~1.7GB
|
||||
总计 (2GB): ~300MB 使用 / ~1.7GB 空闲
|
||||
|
||||
结论: 2GB 绰绰有余,不需要 4GB
|
||||
```
|
||||
|
||||
### 存储使用分析
|
||||
|
||||
```
|
||||
Alpine Linux: ~130MB ISO → 安装后 ~50MB
|
||||
DPDK 库: ~20MB
|
||||
应用二进制: ~5MB
|
||||
配置/日志: ~10MB
|
||||
总计: <100MB
|
||||
|
||||
结论: 8GB mSATA 完全够用
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、软件技术栈
|
||||
|
||||
| 层 | 组件 | 说明 |
|
||||
|----|------|------|
|
||||
| 操作系统 | Alpine Linux 3.20 | 最小 Linux 发行版,~50MB |
|
||||
| 内核 | linux-lts 6.6 | VFIO、hugepage、AF_PACKET 主线支持 |
|
||||
| 数据面框架 | DPDK 24.11 LTS | 用户态高性能包处理 |
|
||||
| PMD 驱动 | `drivers/net/igb` | Intel 82576 原生 DPDK Poll Mode Driver |
|
||||
| 设备绑定 | `vfio-pci` | VT-d + IOMMU,DPDK 官方推荐方式 |
|
||||
| 转发 | 自研 DPDK 应用 | L3 转发 + NAT + rte_flow QoS |
|
||||
| 控制面 | Unix socket + HTTP API | 配置下发、状态查询 |
|
||||
| 初始化 | 自定义 init 脚本 | DHCP 探测、自动识别 WAN/LAN |
|
||||
|
||||
### DPDK 技术点与学习路线对应
|
||||
|
||||
| DPDK 技术 | 用途 | 学习路线阶段 |
|
||||
|-----------|------|------------|
|
||||
| `rte_mempool` | 包内存池管理 | P1 |
|
||||
| `rte_mbuf` | 包数据结构 | P1 |
|
||||
| `rte_ring` | lcore 间无锁通信 | P1 |
|
||||
| `rte_eal_init` | EAL 初始化 (hugepage/lcore) | P1 |
|
||||
| `rte_ethdev` (igb PMD) | 82576 网卡收发 | P1 |
|
||||
| `rte_eth_tx_burst` / `rte_eth_rx_burst` | 批量收发包 | P1 |
|
||||
| `rte_flow` | 流分类 (小包/大包分流) | P3 |
|
||||
| `rte_hash` | NAT 连接跟踪表 | P1 |
|
||||
| `rte_lcore` | 核绑定 (控制面/数据面分离) | P1 |
|
||||
| `rte_kni` | DPDK↔内核通道 (可选) | P2 |
|
||||
|
||||
---
|
||||
|
||||
## 五、数据面架构
|
||||
|
||||
### 整体架构:快慢路径分离
|
||||
|
||||
```
|
||||
┌────────────────────┐
|
||||
│ 控制面 (lcore 0) │
|
||||
│ │
|
||||
│ DHCP Client │ ← 自动探测 WAN 口
|
||||
│ 配置管理 │
|
||||
│ HTTP API (可选) │ ← 手机配置用
|
||||
│ 日志/统计 │
|
||||
└────────┬───────────┘
|
||||
│
|
||||
Unix socket
|
||||
│
|
||||
┌────────────────────────┼────────────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
WAN 口 (port 0) 转发核 (lcore 1) LAN 口 (port 1)
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────┐ ┌───────────────┐ ┌─────────┐
|
||||
│ RX 队列 │──mbuf──►│ rte_flow 分流 │──mbuf──►│ TX 队列 │
|
||||
│ │ │ │ │ │
|
||||
│ 82576 │ │ 小包→高优先 │ │ 82576 │
|
||||
│ igb PMD │ │ 大包→低优先 │ │ igb PMD │
|
||||
│ │ │ NAT 连接跟踪 │ │ │
|
||||
└─────────┘ └───────────────┘ └─────────┘
|
||||
```
|
||||
|
||||
### 收发包路径
|
||||
|
||||
```
|
||||
收包:
|
||||
rte_eth_rx_burst(port, queue, mbufs, BURST_SIZE)
|
||||
→ 逐包处理:
|
||||
1. rte_flow 匹配(五元组 hash)
|
||||
2. 如果不是已知流 → 新建流表项
|
||||
3. 按流优先级入队
|
||||
4. NAT 转换 (SNAT/DNAT)
|
||||
5. 查路由表决定出端口
|
||||
|
||||
发包:
|
||||
高优先队列 → rte_eth_tx_burst (先发)
|
||||
低优先队列 → rte_eth_tx_burst (后发,有剩余带宽才发)
|
||||
```
|
||||
|
||||
### Lcore 分配
|
||||
|
||||
```
|
||||
lcore 0 (主核):
|
||||
- EAL 初始化
|
||||
- 控制面逻辑
|
||||
- 定时器 (统计收集)
|
||||
- Unix socket 服务
|
||||
|
||||
lcore 1 (转发核):
|
||||
- 数据面死循环
|
||||
- 收包 → 分流 → NAT → 发包
|
||||
- 独占 100% CPU,无中断干扰
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、软件模块划分
|
||||
|
||||
```
|
||||
dpdk-gateway/
|
||||
├── src/
|
||||
│ ├── main.c # EAL 初始化,启动 lcore
|
||||
│ ├── forward.c # 转发核主循环
|
||||
│ ├── forward.h
|
||||
│ ├── nat.c # NAT 连接跟踪 (SNAT/DNAT)
|
||||
│ ├── nat.h
|
||||
│ ├── classifier.c # rte_flow 流分类 + 优先级队列
|
||||
│ ├── classifier.h
|
||||
│ ├── control.c # 控制面 (DHCP 探测 + 配置)
|
||||
│ ├── control.h
|
||||
│ ├── stats.c # 统计收集
|
||||
│ └── stats.h
|
||||
├── scripts/
|
||||
│ ├── init.sh # 启动脚本 (配 hugepage/绑定网卡)
|
||||
│ └── probe.sh # WAN/LAN 自动探测脚本
|
||||
├── config/
|
||||
│ └── gateway.conf # 默认配置文件
|
||||
├── Makefile # 或 meson.build
|
||||
└── README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、量产降本路径
|
||||
|
||||
| 阶段 | 数量 | 主板来源 | 网卡来源 | 单台 BOM |
|
||||
|------|------|---------|---------|---------|
|
||||
| 样机验证 | 1 台 | 闲鱼 J3355B ¥100 | 闲鱼 82576 ¥30 | ¥173 |
|
||||
| 小批试水 | 100 台 | 闲鱼打包 ¥90 | 闲鱼打包 ¥25 | ¥146 |
|
||||
| 中批 | 500 台 | 批发商 ¥85 | 批发 ¥22 | ¥130 |
|
||||
| 大批定制 | 1000 台 | 自研板 (I226 焊板) | 芯片直焊 | ~¥120 |
|
||||
|
||||
> 1000 台定制板后,Intel I226-V 网卡芯片直接焊主板上,省去独立网卡和 PCIe 槽。
|
||||
|
||||
---
|
||||
|
||||
## 八、成本与利润测算
|
||||
|
||||
### 100 台试水
|
||||
|
||||
| 项目 | 金额 |
|
||||
|------|------|
|
||||
| 单台 BOM | ¥146 |
|
||||
| 单台组装(自己干) | ¥0 |
|
||||
| 包装盒(定制牛皮纸,100 个) | ¥3/台 |
|
||||
| 快递费(均摊) | ¥5/台 |
|
||||
| 单台全成本 | ¥154 |
|
||||
| **售价** | **¥299** |
|
||||
| **单台毛利** | **¥145** |
|
||||
| **100 台总毛利** | **¥14,500** |
|
||||
|
||||
### 1000 台(定制板)
|
||||
|
||||
| 项目 | 金额 |
|
||||
|------|------|
|
||||
| 单台 BOM | ¥120 |
|
||||
| 组装(深圳厂 ¥15/台) | ¥15 |
|
||||
| 包装盒 | ¥3 |
|
||||
| 快递 | ¥5 |
|
||||
| 单台全成本 | ¥143 |
|
||||
| **售价** | **¥299** |
|
||||
| **单台毛利** | **¥156** |
|
||||
| **1000 台总毛利** | **¥156,000** |
|
||||
|
||||
---
|
||||
|
||||
## 九、下一步行动计划
|
||||
|
||||
```
|
||||
Phase 1: 样机验证 (1-2 周)
|
||||
□ 1. 采购 J3355B-ITX + Intel 82576 + DDR3L 2GB + mSATA 8GB
|
||||
□ 2. 安装 Alpine Linux,配置 VFIO + hugepage
|
||||
□ 3. 编译 DPDK,跑通 testpmd L2 转发
|
||||
□ 4. 编写最简单的 DPDK L3 转发程序 (main.c + forward.c)
|
||||
|
||||
Phase 2: 核心功能 (2-4 周)
|
||||
□ 5. 实现 NAT (SNAT/DNAT + 连接跟踪)
|
||||
□ 6. 实现 rte_flow 流分类 (小包/大包分流)
|
||||
□ 7. 实现优先级队列调度
|
||||
□ 8. 实现 WAN/LAN 自动探测 (DHCP)
|
||||
|
||||
Phase 3: 产品化 (2-4 周)
|
||||
□ 9. 添加启动脚本 (init.sh + probe.sh)
|
||||
□ 10. 添加 LED 状态指示 (通过 GPIO 或内核接口)
|
||||
□ 11. 制作 Alpine 固件镜像 (dd 可刷)
|
||||
□ 12. 设计外壳图纸 (DXF for 亚克力切割)
|
||||
|
||||
Phase 4: 自用验证 (4 周)
|
||||
□ 13. 替换家中路由器,自己用 1 个月
|
||||
□ 14. 记录所有问题和改进点
|
||||
□ 15. 性能压测 (iperf3 小包/大包)
|
||||
|
||||
Phase 5: 开源 + 销售
|
||||
□ 16. GitHub 开源基础版
|
||||
□ 17. 恩山论坛 / chiphell 发帖
|
||||
□ 18. 淘宝/闲鱼上架
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十、风险评估
|
||||
|
||||
| 风险 | 严重度 | 概率 | 应对措施 |
|
||||
|------|--------|------|---------|
|
||||
| 二手主板货源不稳定 | 🟡 中 | 中 | 初期囤 100 片;J3455-ITX 备选;1000 台后走定制 |
|
||||
| 用户不会改光猫桥接 | 🔴 高 | 高 | 默认纯桥模式透传,不改任何配置也能工作(仅提速效果打折) |
|
||||
| 2 口不够替代路由器 | 🟡 中 | 高 | 就定位加速器,不定位路由器;明确告诉用户"插在光猫和路由器之间" |
|
||||
| GPL 合规问题 | 🟢 低 | 低 | DPDK 用户态程序 BSD 协议,Alpine Linux 用 GPL 但无需修改内核 |
|
||||
| 售后工作量大 | 🔴 高 | 中 | 初期配详细 LED 故障灯说明、FAQ 文档;逐步建立知识库 |
|
||||
| 被友商抄袭 | 🟡 中 | 中 | 开源 + 社区粘性 + 持续更新;品牌信任是壁垒 |
|
||||
| 3C 认证 (做硬件后) | 🔴 高 | 中 | 初期只卖固件/散件/DIY 套件;量大了再走认证 |
|
||||
|
||||
---
|
||||
|
||||
## 十一、竞品对比
|
||||
|
||||
| 对比维度 | 运营商光猫 | 小米路由器 | 软路由 (iStoreOS) | **你的盒子** |
|
||||
|---------|-----------|-----------|-----------------|------------|
|
||||
| NAT 性能 (千兆) | ~600M | ~940M | ~940M | **~940M (线速)** |
|
||||
| 满载延迟 (小包) | 200ms+ | 80ms+ | 50ms+ | **<20ms** |
|
||||
| 智能流控 | ❌ | 基础 QoS | 需手动配置 | **自动,零配置** |
|
||||
| 配置难度 | 不用 (默认烂) | 中等 | 高 | **不用配** |
|
||||
| 售价 | ¥0 (送的) | ¥300 | ¥400+ | **¥299** |
|
||||
| 开源 | ❌ | ❌ | 部分 | **✅ 核心开源** |
|
||||
|
||||
---
|
||||
|
||||
## 十二、相关资源
|
||||
|
||||
- **学习路线文档**: `2026-06-29-dpdk-roadmap-v3-design.md`
|
||||
- **加速路径文档**: `DPDK_ACCELERATED_PATH.md`
|
||||
- **DPDK 源码**: `~/myshare/dpdk-stable-24.11.6`
|
||||
- **内核对照**: `~/myshare/linux-stable` (v6.6)
|
||||
- **性能压测工具**: `~/myshare/dperf-main`
|
||||
@@ -0,0 +1,23 @@
|
||||
# 产品设计文档
|
||||
|
||||
## DPDK Home Gateway 网络加速器
|
||||
|
||||
→ [完整产品方案](/products/home-gateway)
|
||||
|
||||
基于 DPDK 的家庭网关网络加速方案,包括:
|
||||
|
||||
- 硬件选型与 BOM 清单
|
||||
- 定价策略与市场分析
|
||||
- 分阶段研发计划
|
||||
- 竞品对比分析
|
||||
|
||||
## uNOS — 用户态网络操作系统
|
||||
|
||||
→ [设计规格书](/products/unos-design) | [学习方法论](/products/unos-method)
|
||||
|
||||
uNOS (user-space Network Operating System) 是一个生产级的用户态网络操作系统设计:
|
||||
|
||||
- **三层剥洋葱法**: 从 Linux 内核到 DPDK,再到 DPU
|
||||
- **架构设计**: 控制平面 / 数据平面分离
|
||||
- **核心模块**: 协议栈、转发引擎、硬件抽象层
|
||||
- **对标**: Cisco IOS-XR / Juniper JunOS 架构
|
||||
Executable
+1735
File diff suppressed because it is too large
Load Diff
Executable
+359
@@ -0,0 +1,359 @@
|
||||
# uNOS 学习方法论 — 三层剥皮法
|
||||
|
||||
> **创建日期**: 2026-08-01
|
||||
> **适用**: 系统编程学习(DPDK / 内核 / TCP 协议栈 / PMD 驱动)
|
||||
> **关联**: `uNOS_REQUIREMENTS.md`、`uNOS_DESIGN_SPEC.md`、`lazy-puzzling-rocket.md`
|
||||
|
||||
---
|
||||
|
||||
## 一、核心公式
|
||||
|
||||
```
|
||||
掌握 = 内核为什么慢 → DPDK 怎么解决的 → 我亲手写一遍 → 性能数据证明
|
||||
────────── ────────────── ────────── ─────────────
|
||||
Layer 1 Layer 2 Layer 3 Layer 4
|
||||
(理解问题) (理解方案) (内化知识) (验证理解)
|
||||
```
|
||||
|
||||
每一层不可跳过:
|
||||
|
||||
| 跳过 | 后果 |
|
||||
|------|------|
|
||||
| Layer 1 | 不知道为什么需要 DPDK,面试回答停留在"因为快" |
|
||||
| Layer 2 | 只会调 API,不理解设计决策 |
|
||||
| Layer 3 | "看懂了但不会写",面试一问细节就崩 |
|
||||
| Layer 4 | "写出来了但不知道自己写对了没" |
|
||||
|
||||
---
|
||||
|
||||
## 二、四层详解
|
||||
|
||||
### Layer 1: 内核为什么慢(20 分钟,理解问题)
|
||||
|
||||
**做什么**:读内核源码,找到瓶颈的**精确位置**。
|
||||
|
||||
**方法**:
|
||||
|
||||
1. 先确定要研究的子系统(如:包分配、路由查找、TCP 状态机)
|
||||
2. 在 Linux 内核源码中找到对应的 C 文件
|
||||
3. 跟踪一个完整的数据路径(如:收包 → 分配 skb → 协议栈 → 应用)
|
||||
4. 标注每一步的延迟来源:中断、锁、内存分配、cache miss、上下文切换
|
||||
|
||||
**产出标准**:能用一句话解释"内核做 X 要走多少步、每一步慢在哪"。
|
||||
|
||||
**示例**(mempool):
|
||||
|
||||
```
|
||||
问题: 内核是怎么分配网络包的?
|
||||
|
||||
精确到源码:
|
||||
收包: net/core/dev.c → netif_rx() → enqueue_to_backlog()
|
||||
→ 每个包触发一次 ksoftirqd 调度
|
||||
分配: include/linux/skbuff.h → alloc_skb()
|
||||
→ kmem_cache_alloc() → slab allocator
|
||||
→ 每次分配都要获取 per-cpu 锁
|
||||
|
||||
为什么慢:
|
||||
1. 每包一次中断 → 每包一次上下文切换
|
||||
2. slab allocator 有锁 → 多核竞争
|
||||
3. kmem_cache 有冷热分离 → 热路径 miss 时有 cache miss
|
||||
4. sk_buff 结构体 200+ 字节,跨多个 cacheline
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Layer 2: DPDK 怎么解决的(20 分钟,理解方案)
|
||||
|
||||
**做什么**:读 DPDK 源码,找到优化的**精确手段**。
|
||||
|
||||
**方法**:
|
||||
|
||||
1. 在 DPDK 源码中找到对应的库/驱动
|
||||
2. 读头文件的 struct 定义和函数注释
|
||||
3. 读实现文件的几个关键函数
|
||||
4. 画出"数据路径图":一个包从进入到离开,经过哪些结构体、哪些函数
|
||||
|
||||
**产出标准**:能用一张图画出关键路径,标注每一级的延迟量级。
|
||||
|
||||
**示例**(mempool):
|
||||
|
||||
```
|
||||
方案: rte_mempool
|
||||
|
||||
精确到源码:
|
||||
lib/mempool/rte_mempool.h line 230 → struct rte_mempool
|
||||
lib/mempool/rte_mempool.h line 90 → struct rte_mempool_cache
|
||||
|
||||
三级路径:
|
||||
L1: mbuf 从 per-lcore cache 拿 → 零锁,L1 cache 常驻 (~5ns)
|
||||
L2: cache 空了 → 从 ring 批量补 256 个 → CAS 一次 (~50ns 均摊)
|
||||
L3: ring 也空了 → 从 hugepage 拿 → 很少触发 (~200ns)
|
||||
|
||||
为什么快:
|
||||
1. 预分配 → 收发过程零分配,只从池里取
|
||||
2. per-lcore cache → 无锁,无竞争
|
||||
3. ring 批量操作 → 分摊 CAS 开销
|
||||
4. mbuf 固定 128B,2 个 cacheline → prefetch 友好
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Layer 3: 亲手写一遍(60 分钟,内化知识)
|
||||
|
||||
**做什么**:关掉所有参考代码,自己从头写。
|
||||
|
||||
**方法**:
|
||||
|
||||
1. 读完 Layer 1-2 后,**立即关掉 DPDK 源码**
|
||||
2. 打开编辑器,自己写
|
||||
3. 编译 → crash → gdb backtrace → 读报错 → 修 → 继续
|
||||
4. 跑通后用 gdb 单步跟踪关键路径:打印 struct 内容、看指针偏移
|
||||
5. 用 `pahole` 看结构体 cache line 布局
|
||||
|
||||
**产出标准**:能在 gdb 里打印出关键结构体的内部状态,能解释为什么这么快。
|
||||
|
||||
**示例**(mempool):
|
||||
|
||||
```c
|
||||
// 自己写:
|
||||
|
||||
// 1. 创建 mempool
|
||||
struct rte_mempool *pool = rte_pktmbuf_pool_create(
|
||||
"my_pool", 8191, 256, 0,
|
||||
RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()
|
||||
);
|
||||
|
||||
// 2. 分配/释放循环,测性能
|
||||
uint64_t start = rte_rdtsc();
|
||||
for (int i = 0; i < 1000000; i++) {
|
||||
struct rte_mbuf *m = rte_pktmbuf_alloc(pool);
|
||||
rte_pktmbuf_free(m);
|
||||
}
|
||||
uint64_t cycles = (rte_rdtsc() - start) / 1000000;
|
||||
printf("avg cycles per alloc+free: %lu\n", cycles);
|
||||
|
||||
// 3. gdb 验证
|
||||
// (gdb) p *pool
|
||||
// (gdb) p pool->cache[0] ← per-lcore cache 里有多少个 mbuf?
|
||||
// (gdb) p pool->cache[0].len ← 当 len > 0 时 alloc 走 L1
|
||||
// (gdb) p pool->cache[0].len ← 当 len = 0 时 alloc 走 L2 补货
|
||||
```
|
||||
|
||||
**禁止事项**:
|
||||
|
||||
- ❌ 复制粘贴 DPDK examples 代码
|
||||
- ❌ 先看答案再写
|
||||
- ❌ crash 后直接问 AI 怎么修(先自己 gdb 至少 5 分钟)
|
||||
|
||||
---
|
||||
|
||||
### Layer 4: 性能数据证明(20 分钟,验证理解)
|
||||
|
||||
**做什么**:用数据证明你写的东西确实比内核快。
|
||||
|
||||
**方法**:
|
||||
|
||||
1. `perf stat` 对比内核路径 vs DPDK 路径
|
||||
2. `perf record` + FlameGraph 生成火焰图
|
||||
3. 填一张对比表
|
||||
4. 写入 `docs/` 文档
|
||||
|
||||
**产出标准**:有一张表格,有数字,有火焰图链接。
|
||||
|
||||
**示例**(mempool):
|
||||
|
||||
```
|
||||
│ 指标 │ 内核 (slab) │ DPDK (mempool) │
|
||||
│ alloc+free 延迟 │ ~200ns │ ~15ns │
|
||||
│ L1 cache miss │ ~8% │ ~0.5% │
|
||||
│ 锁竞争 │ 有 (per-cpu) │ 无 (per-lcore) │
|
||||
│ 火焰图 │ [链接] │ [链接] │
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、每阶段时间分配
|
||||
|
||||
```
|
||||
总时间: 2 小时/阶段
|
||||
|
||||
Layer 1: 20 分钟 (读内核源码 + 理解问题)
|
||||
Layer 2: 20 分钟 (读 DPDK 源码 + 理解方案)
|
||||
Layer 3: 60 分钟 (手写 + gdb 跟踪)
|
||||
Layer 4: 20 分钟 (perf 验证 + 写文档)
|
||||
|
||||
输入 (Layer 1-2): 30%
|
||||
输出 (Layer 3-4): 70%
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、应用到 uNOS 每个 Phase
|
||||
|
||||
### Phase 1: L2 MAC 转发
|
||||
|
||||
| Layer | 内容 | 产出 |
|
||||
|-------|------|------|
|
||||
| 1 | 读 `net/core/dev.c` NAPI 收包路径 | 理解中断驱动的瓶颈在哪 |
|
||||
| 2 | 读 DPDK PMD 轮询模式: `rte_eth_rx_burst` | 理解零中断轮询 |
|
||||
| 3 | 手写 L2 转发循环,gdb 跟踪 mbuf 从口0到口1 | 能跑通,iPerf3 双向通 |
|
||||
| 4 | `perf record` 火焰图 | CPU 100% 有用功,中断开销 0% |
|
||||
|
||||
### Phase 2: L3 路由转发
|
||||
|
||||
| Layer | 内容 | 产出 |
|
||||
|-------|------|------|
|
||||
| 1 | 读 `net/ipv4/fib_trie.c` 内核路由表 | 理解 FIB trie 的结构 |
|
||||
| 2 | 读 `lib/librte_lpm/rte_lpm.c` DIR-24-8 算法 | 理解 LPM 的 2 级 Trie |
|
||||
| 3 | 手写 LPM lookup + TTL 减一 + checksum | 跨网段 ping 通 |
|
||||
| 4 | rdtsc 测量单次 lookup 延迟 | 内核 ~500ns vs DPDK ~200ns |
|
||||
|
||||
### Phase 3: ARP 协议
|
||||
|
||||
| Layer | 内容 | 产出 |
|
||||
|-------|------|------|
|
||||
| 1 | 读 `net/ipv4/arp.c` 内核 ARP 实现 | 理解 ARP 表的维护逻辑 |
|
||||
| 2 | 读 `lib/ring/rte_ring.h` 无锁 CAS 原理 | 理解 ring 的 MP/MC 模式 |
|
||||
| 3 | 手写 ARP 请求/应答 + pending 队列 | arping 能收到回复 |
|
||||
| 4 | 连续 ARP 风暴,检查 mbuf 泄漏 | 内存无泄漏 |
|
||||
|
||||
### Phase 4: 控制面框架
|
||||
|
||||
| Layer | 内容 | 产出 |
|
||||
|-------|------|------|
|
||||
| 1 | 读内核 workqueue / tasklet 机制 | 理解异步任务调度 |
|
||||
| 2 | 读 `rte_eal_remote_launch` + `rte_timer` | 理解 DPDK 的多核 + 定时器 |
|
||||
| 3 | 手写 Pipeline 框架 + Interface Manager | 两机多 lcore 运行 |
|
||||
| 4 | 控制面定时器无影响数据面延迟 | 数据面延迟不因定时器而抖动 |
|
||||
|
||||
### Phase 5: NAT + 连接跟踪
|
||||
|
||||
| Layer | 内容 | 产出 |
|
||||
|-------|------|------|
|
||||
| 1 | 读 `net/netfilter/nf_conntrack_core.c` | 理解内核 conntrack 的锁竞争 |
|
||||
| 2 | 读 `lib/hash/rte_cuckoo_hash.c` Cuckoo hash | 理解 2-bucket 开放寻址 |
|
||||
| 3 | 手写 SNAT/DNAT + rte_hash 连接表 | 内网 curl 外网能通 |
|
||||
| 4 | 100K 连接查找延迟 + 哈希冲突率 | 查找 O(1),冲突率 < 5% |
|
||||
|
||||
### Phase 6: ACL + QoS
|
||||
|
||||
| Layer | 内容 | 产出 |
|
||||
|-------|------|------|
|
||||
| 1 | 读内核 `net/sched/` TC 调度框架 | 理解多队列调度的复杂度 |
|
||||
| 2 | 读 `lib/acl/rte_acl.h` + `lib/sched/` | 理解硬件加速分类 + 调度 |
|
||||
| 3 | 手写 5 元组 ACL + 8 队列 SP/WRR | 满载大包时小包延迟 < 2ms |
|
||||
| 4 | iperf3 大包 + ping 小包 CDF 曲线 | 内核 p99 > 80ms vs uNOS < 2ms |
|
||||
|
||||
### Phase 7: IPv4/IPv6 + ICMP + Fragmentation
|
||||
|
||||
| Layer | 内容 | 产出 |
|
||||
|-------|------|------|
|
||||
| 1 | 读 `net/ipv4/ip_input.c` IP 收包路径 | 理解 IP 层在内核中的位置 |
|
||||
| 2 | 读 RFC 791, RFC 792, RFC 815 | 理解协议规范本身 |
|
||||
| 3 | 手写 IP 封装/解封 + ICMP Echo Reply | 能 ping 通你的栈 |
|
||||
| 4 | 分包重组 2000 字节包 | 正确重组 |
|
||||
|
||||
### Phase 8: TCP 状态机
|
||||
|
||||
| Layer | 内容 | 产出 |
|
||||
|-------|------|------|
|
||||
| 1 | 读 `net/ipv4/tcp_input.c` tcp_rcv_state_process | 理解内核 11 状态 |
|
||||
| 2 | 读 RFC 793(逐字读) | 画出自己的状态转换表 |
|
||||
| 3 | 手写 11 状态 + scapy 逐状态触发 | gdb 单步跟踪每个转换 |
|
||||
| 4 | scapy 100 万 TIME_WAIT → 时间轮 O(1) | 内核链表 O(n) vs 时间轮 O(1) |
|
||||
|
||||
### Phase 9: 重传引擎
|
||||
|
||||
| Layer | 内容 | 产出 |
|
||||
|-------|------|------|
|
||||
| 1 | 读 `net/ipv4/tcp_output.c` tcp_retransmit_skb | 理解内核重传逻辑 |
|
||||
| 2 | 读 RFC 6298 (RTO), RFC 2018 (SACK) | 理解重传算法 |
|
||||
| 3 | 手写 RTO + 快速重传 + SACK | tc netem 丢包 5% → 正确补偿 |
|
||||
| 4 | rdtsc 测量 RTO 精度 | 误差 < 10ms |
|
||||
|
||||
### Phase 10: 拥塞控制
|
||||
|
||||
| Layer | 内容 | 产出 |
|
||||
|-------|------|------|
|
||||
| 1 | 读 `net/ipv4/tcp_cubic.c` 内核 CUBIC | 理解三次函数窗口增长 |
|
||||
| 2 | 读 RFC 8312 + 2008 年 CUBIC 论文 | 理解设计决策 |
|
||||
| 3 | 手写滑动窗口 + CUBIC | wireshark 验证窗口曲线 |
|
||||
| 4 | 内核 TCP vs 自研 TCP 吞吐对比 | 1G: 940M vs 940M (持平) |
|
||||
| | | CPU: 50% vs 8% (6x 优) |
|
||||
|
||||
### Phase 11: Socket API
|
||||
|
||||
| Layer | 内容 | 产出 |
|
||||
|-------|------|------|
|
||||
| 1 | 读 POSIX socket 语义 | 理解 API 设计哲学 |
|
||||
| 2 | 读内核 `net/socket.c` socket 系统调用 | 理解 fd 到 sock 的映射 |
|
||||
| 3 | 手写 tcp_socket/bind/listen/accept/send/recv | HTTP Server 跑在自研 TCP 上 |
|
||||
| 4 | wrk 压测 HTTP QPS | 自研栈 ≥ 内核栈 QPS |
|
||||
|
||||
### Phase 12-17 (应用层/管理面/生产级增强)
|
||||
|
||||
每个 Phase 同样应用四层框架。Layer 1-2 以阅读对应 RFC / 工业标准实现为主。
|
||||
|
||||
---
|
||||
|
||||
## 五、方法论红黑榜
|
||||
|
||||
### ✅ 做这些
|
||||
|
||||
```
|
||||
每个概念都按 1→2→3→4 顺序来,不跳层
|
||||
Layer 3 时关掉所有参考,完全自己写
|
||||
每次 crash 先 gdb 至少 5 分钟再搜答案
|
||||
每层产出必须可验证(代码能跑 / 图表能看 / 数据能复现)
|
||||
每天产出至少 50 行可运行的代码
|
||||
```
|
||||
|
||||
### ❌ 不做这些
|
||||
|
||||
```
|
||||
跳过 Layer 1 直接看 DPDK 文档 → 不知道为什么需要
|
||||
跳过 Layer 3 只看不写 → "我看懂了"(其实没有)
|
||||
Layer 3 复制粘贴 examples → 没有内化
|
||||
不写文档 → 一个月后全忘了
|
||||
反复讨论设计但不写代码 → 设计永远不会完美
|
||||
不跑 perf 不生成数据 → 证明不了自己写的东西好
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、每日日志模板
|
||||
|
||||
```
|
||||
日期: 2026-08-XX
|
||||
阶段: Phase X - XXX
|
||||
|
||||
Layer 1 (20min):
|
||||
读了: [内核文件/函数]
|
||||
理解: [一句话瓶颈描述]
|
||||
|
||||
Layer 2 (20min):
|
||||
读了: [DPDK 文件/函数]
|
||||
理解: [一句话优化描述]
|
||||
画了: [数据路径图]
|
||||
|
||||
Layer 3 (60min):
|
||||
写了: [文件路径,代码行数]
|
||||
Crash: [次数 + 最关键的 bug]
|
||||
gdb 发现: [最关键的 insight]
|
||||
|
||||
Layer 4 (20min):
|
||||
perf 数据: [关键对比数字]
|
||||
火焰图: [链接]
|
||||
文档: [docs/ 文件路径]
|
||||
|
||||
明天计划:
|
||||
[下一阶段的 Layer 1 要读什么]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **文档结束**
|
||||
>
|
||||
> 这个方法不是学知识的,是逼你写代码的。
|
||||
> 系统编程不能靠"看懂",要靠"调通"。
|
||||
> 每层 20/20/60/20 分钟,一天 2 小时一个模块。
|
||||
Executable
+3163
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user