Files
ldb/README.md

532 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 9.1 Kvstore
## 需求
1. ntyco需要作为kvstore的submodule,通过git clone一次下载。 **完成**
2. README需要包含编译步骤测试方案与可行性性能数据。 **完成**
3. 全量持久化保存数据集。 **BUG FIX**
4. 持久化的性能数据。 **完成**
5. 特殊字符可以解决redis的resp协议。 **完成**
6. 实现配置文件把日志级别端口ip主从模式持久化方案。 **完成**
7. 持久化落盘用io_uring加载配置文件用mmap。 **完成**
8. 主从同步的性能开启与关闭性能做到5%?。
9. 主从同步600w条,出现的coredump。 **BUG FIX**
10. 主从同步用ebpf实现。 **完成**
11. 内存池测试qps与虚拟内存物理内存。 **完成**
12. 实现一个内存泄露检测组件。 **完成**
## 环境安装与编译
```shell
# xml
sudo apt install libxml2 libxml2-dev
# hiredis client
sudo apt install -y libhiredis-dev
# bpftrace
sudo apt install -y bpftrace libelf libelf-dev clang
git clone git@gitlab.0voice.com:lianyiheng/9.1-kvstore.git
cd 9.1-kvstore/
git submodule update --init --recursive
./init.sh
make
```
```
docker run -it --rm \
-v "$(pwd)":/workdir \
-w /workdir \
--pid=host \
--privileged \
ghcr.io/eunomia-bpf/bpftime:latest \
/bin/bash
```
## 测试
### 测试1性能测试
测试条件:
1. 不启用持久化。
2. 不启用主从同步。
3. pipline
1. RSET 100w 条, p:i v:i -> +OK
2. RGET 100w 条, p:i -> +v:i
3. RDEL 100w 条。 p:i -> +OK
4. 本机发送请求。
#### 内存分配: malloc
```bash
lian@ubuntu:~/share/9.1-kvstore$ ./test-redis/testcase 192.168.10.129 8888 3
Connected to 192.168.10.129:8888
BATCH (N=3000000) --> time_used=3294 ms, qps=910746
BATCH (N=3000000) --> time_used=3501 ms, qps=856898
BATCH (N=3000000) --> time_used=3457 ms, qps=867804
BATCH (N=3000000) --> time_used=3351 ms, qps=895255
BATCH (N=3000000) --> time_used=3320 ms, qps=903614
BATCH (N=3000000) --> time_used=3551 ms, qps=844832
BATCH (N=3000000) --> time_used=3354 ms, qps=894454
BATCH (N=3000000) --> time_used=3475 ms, qps=863309
BATCH (N=3000000) --> time_used=3404 ms, qps=881316
BATCH (N=3000000) --> time_used=3460 ms, qps=867052
BATCH (N=3000000) --> time_used=3392 ms, qps=884433
BATCH (N=3000000) --> time_used=3427 ms, qps=875401
BATCH (N=3000000) --> time_used=3441 ms, qps=871839
BATCH (N=3000000) --> time_used=3471 ms, qps=864304
BATCH (N=3000000) --> time_used=3354 ms, qps=894454
BATCH (N=3000000) --> time_used=3447 ms, qps=870322
BATCH (N=3000000) --> time_used=3364 ms, qps=891795
BATCH (N=3000000) --> time_used=3200 ms, qps=937500
BATCH (N=3000000) --> time_used=3159 ms, qps=949667
BATCH (N=3000000) --> time_used=3482 ms, qps=861573
BATCH (N=3000000) --> time_used=3474 ms, qps=863557
BATCH (N=3000000) --> time_used=3591 ms, qps=835421
BATCH (N=3000000) --> time_used=3466 ms, qps=865551
BATCH (N=3000000) --> time_used=3425 ms, qps=875912
BATCH (N=3000000) --> time_used=3346 ms, qps=896592
BATCH (N=3000000) --> time_used=3532 ms, qps=849377
BATCH (N=3000000) --> time_used=3471 ms, qps=864304
BATCH (N=3000000) --> time_used=3616 ms, qps=829646
BATCH (N=3000000) --> time_used=3403 ms, qps=881575
BATCH (N=3000000) --> time_used=3426 ms, qps=875656
BATCH (N=3000000) --> time_used=3493 ms, qps=858860
BATCH (N=3000000) --> time_used=3411 ms, qps=879507
BATCH (N=3000000) --> time_used=3422 ms, qps=876680
BATCH (N=3000000) --> time_used=3556 ms, qps=843644
BATCH (N=3000000) --> time_used=3285 ms, qps=913242
BATCH (N=3000000) --> time_used=3486 ms, qps=860585
BATCH (N=3000000) --> time_used=3427 ms, qps=875401
BATCH (N=3000000) --> time_used=3563 ms, qps=841987
BATCH (N=3000000) --> time_used=3304 ms, qps=907990
BATCH (N=3000000) --> time_used=3582 ms, qps=837520
BATCH (N=3000000) --> time_used=3468 ms, qps=865051
BATCH (N=3000000) --> time_used=3360 ms, qps=892857
BATCH (N=3000000) --> time_used=3426 ms, qps=875656
BATCH (N=3000000) --> time_used=3186 ms, qps=941619
BATCH (N=3000000) --> time_used=3251 ms, qps=922792
BATCH (N=3000000) --> time_used=3400 ms, qps=882352
BATCH (N=3000000) --> time_used=3446 ms, qps=870574
BATCH (N=3000000) --> time_used=3302 ms, qps=908540
BATCH (N=3000000) --> time_used=3072 ms, qps=976562
BATCH (N=3000000) --> time_used=3458 ms, qps=867553
average qps:880462
ALL TESTS PASSED.
```
#### 内存分配: 自实现内存池
```bash
lian@ubuntu:~/share/9.1-kvstore$ ./test-redis/testcase 192.168.10.129 8888 3
Connected to 192.168.10.129:8888
BATCH (N=3000000) --> time_used=3241 ms, qps=925640
BATCH (N=3000000) --> time_used=3047 ms, qps=984574
BATCH (N=3000000) --> time_used=3085 ms, qps=972447
BATCH (N=3000000) --> time_used=3119 ms, qps=961846
BATCH (N=3000000) --> time_used=3104 ms, qps=966494
BATCH (N=3000000) --> time_used=3163 ms, qps=948466
BATCH (N=3000000) --> time_used=3033 ms, qps=989119
BATCH (N=3000000) --> time_used=3170 ms, qps=946372
BATCH (N=3000000) --> time_used=3299 ms, qps=909366
BATCH (N=3000000) --> time_used=3272 ms, qps=916870
BATCH (N=3000000) --> time_used=3294 ms, qps=910746
BATCH (N=3000000) --> time_used=3182 ms, qps=942803
BATCH (N=3000000) --> time_used=3190 ms, qps=940438
BATCH (N=3000000) --> time_used=3493 ms, qps=858860
BATCH (N=3000000) --> time_used=3111 ms, qps=964320
BATCH (N=3000000) --> time_used=3220 ms, qps=931677
BATCH (N=3000000) --> time_used=3067 ms, qps=978154
BATCH (N=3000000) --> time_used=3345 ms, qps=896860
BATCH (N=3000000) --> time_used=3381 ms, qps=887311
BATCH (N=3000000) --> time_used=3416 ms, qps=878220
BATCH (N=3000000) --> time_used=3192 ms, qps=939849
BATCH (N=3000000) --> time_used=3085 ms, qps=972447
BATCH (N=3000000) --> time_used=3150 ms, qps=952380
BATCH (N=3000000) --> time_used=3296 ms, qps=910194
BATCH (N=3000000) --> time_used=3001 ms, qps=999666
BATCH (N=3000000) --> time_used=3143 ms, qps=954502
BATCH (N=3000000) --> time_used=3111 ms, qps=964320
BATCH (N=3000000) --> time_used=3123 ms, qps=960614
BATCH (N=3000000) --> time_used=3257 ms, qps=921093
BATCH (N=3000000) --> time_used=3037 ms, qps=987816
BATCH (N=3000000) --> time_used=3135 ms, qps=956937
BATCH (N=3000000) --> time_used=3124 ms, qps=960307
BATCH (N=3000000) --> time_used=3276 ms, qps=915750
BATCH (N=3000000) --> time_used=3058 ms, qps=981033
BATCH (N=3000000) --> time_used=3024 ms, qps=992063
BATCH (N=3000000) --> time_used=3224 ms, qps=930521
BATCH (N=3000000) --> time_used=3235 ms, qps=927357
BATCH (N=3000000) --> time_used=3334 ms, qps=899820
BATCH (N=3000000) --> time_used=3427 ms, qps=875401
BATCH (N=3000000) --> time_used=3218 ms, qps=932256
BATCH (N=3000000) --> time_used=3191 ms, qps=940144
BATCH (N=3000000) --> time_used=3179 ms, qps=943692
BATCH (N=3000000) --> time_used=3104 ms, qps=966494
BATCH (N=3000000) --> time_used=3202 ms, qps=936914
BATCH (N=3000000) --> time_used=3184 ms, qps=942211
BATCH (N=3000000) --> time_used=3000 ms, qps=1000000
BATCH (N=3000000) --> time_used=3280 ms, qps=914634
BATCH (N=3000000) --> time_used=3141 ms, qps=955109
BATCH (N=3000000) --> time_used=3198 ms, qps=938086
BATCH (N=3000000) --> time_used=3126 ms, qps=959692
average qps:942837
ALL TESTS PASSED.
```
#### 内存分配jemalloc
```shell
lian@ubuntu:~/share/9.1-kvstore$ ./test-redis/testcase 192.168.10.129 8888 3
Connected to 192.168.10.129:8888
BATCH (N=3000000) --> time_used=3511 ms, qps=854457
BATCH (N=3000000) --> time_used=3280 ms, qps=914634
BATCH (N=3000000) --> time_used=3603 ms, qps=832639
BATCH (N=3000000) --> time_used=3418 ms, qps=877706
BATCH (N=3000000) --> time_used=3353 ms, qps=894721
BATCH (N=3000000) --> time_used=3435 ms, qps=873362
BATCH (N=3000000) --> time_used=3250 ms, qps=923076
BATCH (N=3000000) --> time_used=3550 ms, qps=845070
BATCH (N=3000000) --> time_used=3536 ms, qps=848416
BATCH (N=3000000) --> time_used=3273 ms, qps=916590
BATCH (N=3000000) --> time_used=3224 ms, qps=930521
BATCH (N=3000000) --> time_used=3161 ms, qps=949066
BATCH (N=3000000) --> time_used=3143 ms, qps=954502
BATCH (N=3000000) --> time_used=3342 ms, qps=897666
BATCH (N=3000000) --> time_used=3410 ms, qps=879765
BATCH (N=3000000) --> time_used=3522 ms, qps=851788
BATCH (N=3000000) --> time_used=3035 ms, qps=988467
BATCH (N=3000000) --> time_used=3352 ms, qps=894988
BATCH (N=3000000) --> time_used=3226 ms, qps=929944
BATCH (N=3000000) --> time_used=3406 ms, qps=880798
BATCH (N=3000000) --> time_used=3336 ms, qps=899280
BATCH (N=3000000) --> time_used=3307 ms, qps=907166
BATCH (N=3000000) --> time_used=3171 ms, qps=946073
BATCH (N=3000000) --> time_used=3252 ms, qps=922509
BATCH (N=3000000) --> time_used=3296 ms, qps=910194
BATCH (N=3000000) --> time_used=3301 ms, qps=908815
BATCH (N=3000000) --> time_used=3403 ms, qps=881575
BATCH (N=3000000) --> time_used=3234 ms, qps=927643
BATCH (N=3000000) --> time_used=3348 ms, qps=896057
BATCH (N=3000000) --> time_used=3517 ms, qps=852999
BATCH (N=3000000) --> time_used=3354 ms, qps=894454
BATCH (N=3000000) --> time_used=3529 ms, qps=850099
BATCH (N=3000000) --> time_used=3473 ms, qps=863806
BATCH (N=3000000) --> time_used=3521 ms, qps=852030
BATCH (N=3000000) --> time_used=3370 ms, qps=890207
BATCH (N=3000000) --> time_used=3267 ms, qps=918273
BATCH (N=3000000) --> time_used=3352 ms, qps=894988
BATCH (N=3000000) --> time_used=3433 ms, qps=873871
BATCH (N=3000000) --> time_used=3374 ms, qps=889152
BATCH (N=3000000) --> time_used=3360 ms, qps=892857
BATCH (N=3000000) --> time_used=3463 ms, qps=866300
BATCH (N=3000000) --> time_used=3499 ms, qps=857387
BATCH (N=3000000) --> time_used=3294 ms, qps=910746
BATCH (N=3000000) --> time_used=3311 ms, qps=906070
BATCH (N=3000000) --> time_used=3443 ms, qps=871333
BATCH (N=3000000) --> time_used=3381 ms, qps=887311
BATCH (N=3000000) --> time_used=3422 ms, qps=876680
BATCH (N=3000000) --> time_used=3421 ms, qps=876936
BATCH (N=3000000) --> time_used=3322 ms, qps=903070
BATCH (N=3000000) --> time_used=3494 ms, qps=858614
average qps:892493
ALL TESTS PASSED.
```
### 测试2持久化
测试条件:
1. 启用持久化。
2. 不启用主从同步。
3. pipline
1. RSET 100w 条, p:i v:i -> +OK
2. RGET 100w 条, p:i -> +v:i
3. RDEL 100w 条。 p:i -> +OK
5. 本机发送请求。
```shell
lian@ubuntu:~/share/9.1-kvstore$ ./test-redis/testcase 192.168.10.129 8888 4
Connected to 192.168.10.129:8888
BATCH (N=3000000) --> time_used=3500 ms, qps=857142
BATCH (N=3000000) --> time_used=3322 ms, qps=903070
BATCH (N=3000000) --> time_used=3424 ms, qps=876168
BATCH (N=3000000) --> time_used=3483 ms, qps=861326
BATCH (N=3000000) --> time_used=3421 ms, qps=876936
BATCH (N=3000000) --> time_used=3519 ms, qps=852514
BATCH (N=3000000) --> time_used=3597 ms, qps=834028
BATCH (N=3000000) --> time_used=3504 ms, qps=856164
BATCH (N=3000000) --> time_used=3281 ms, qps=914355
BATCH (N=3000000) --> time_used=3446 ms, qps=870574
average qps:870227
ALL TESTS PASSED.
```
### 测试3内存
#### malloc
```shell
VIRT 58504
RES 4604
插入 20w 删除 10w重复 10 次,共计插入 200w 删除 100w。
BATCH (N=9000000) --> time_used=12897 ms, qps=1395673
VIRT 489M
RES 430M
插入 10w 删除 20w重复 10 次,共计插入 100w 删除 200w。
BATCH (N=9000000) --> time_used=10033 ms, qps=1794079
VIRT 208M
RES 155M
```
![alt text](image11.png)
![alt text](image12.png)
![alt text](image13.png)
#### jemalloc
```shell
VIRT 69376
RES 5408
插入 20w 删除 10w重复 30 次,共计插入 600w 删除 300w。
BATCH (N=9000000) --> time_used=9436 ms, qps=1907587
VIRT 356M
RES 294M
插入 10w 删除 20w重复 30 次,共计插入 300w 删除 600w。
BATCH (N=9000000) --> time_used=9353 ms, qps=1924516
VIRT 356M
RES 119M
```
![alt text](image11.png)
![alt text](image22.png)
![alt text](image23.png)
#### mypool
```shell
VIRT 58504
RES 4636
插入 20w 删除 10w重复 30 次,共计插入 600w 删除 300w。
BATCH (N=3000000) --> time_used=3184 ms, qps=1884422
VIRT 625M
RES 572M
插入 10w 删除 20w重复 10 次,共计插入 100w 删除 200w。
BATCH (N=3000000) --> time_used=3022 ms, qps=1985440
VIRT 122M
RES 71492
```
![alt text](image31.png)
![alt text](image32.png)
![alt text](image33.png)
### 测试4主从同步
测试条件:
1. 不启用持久化。
2. 启用主从同步。
3. pipline
1. RSET 100w 条, p:i v:i -> +OK
2. RGET 100w 条, p:i -> +v:i
3. RDEL 100w 条。 p:i -> +OK
5. 本机发送请求。
```shell
lian@ubuntu:~/share/9.1-kvstore$ ./test-redis/testcase 192.168.10.129 8888 4
Connected to 192.168.10.129:8888
BATCH (N=3000000) --> time_used=3702 ms, qps=810372
BATCH (N=3000000) --> time_used=3804 ms, qps=788643
BATCH (N=3000000) --> time_used=4076 ms, qps=736015
BATCH (N=3000000) --> time_used=3840 ms, qps=781250
BATCH (N=3000000) --> time_used=3824 ms, qps=784518
average qps:780159
ALL TESTS PASSED.
lian@ubuntu:~/share/9.1-kvstore$ ./test-redis/testcase 192.168.10.129 8888 4
Connected to 192.168.10.129:8888
BATCH (N=3000000) --> time_used=3958 ms, qps=757958
BATCH (N=3000000) --> time_used=4043 ms, qps=742023
BATCH (N=3000000) --> time_used=3729 ms, qps=804505
BATCH (N=3000000) --> time_used=3989 ms, qps=752068
BATCH (N=3000000) --> time_used=3603 ms, qps=832639
average qps:777838
ALL TESTS PASSED.
```
### 面试题
1. 为什么会实现kvstore使用场景在哪里
2. reactor, ntyco, io_uring的三种网络模型的性能差异
3. 多线程的kvstore该如何改进
4. 私有协议如何设计会更加安全可靠?
5. 协议改进以后,对已有的代码有哪些改变?
6. kv引擎实现了哪些
7. 每个kv引擎的使用场景以及性能差异
8. 测试用例如何实现并且保证代码覆盖率超过90%
9. 网络并发量如何qps如何
10. 能够跟哪些系统交互使用?
## 项目收获
#### reactor网络模型用户态网络缓冲区的写法。
#### 特殊字符串支持的引擎层数据结构设计,支持\0作为键值存储。
1. 长度前缀 + 内容的 binary-safe 字符串表示,支持 \0 作为普通字符。
#### 实现RESP协议的服务端协议解析。
1. 解析流程:
1. 内核 拷贝到 用户态缓冲区
2. 用户态缓冲区 就地解析 bulk-string 为执行引擎可以看得懂的结构体
3. 执行引擎 拷贝 结构体的内容,插入到底层存储结构中
4. 循环解析直到 用户态缓冲区为空
2. 实现了 pipeline 支持:每次从 buffer 读到一个完整命令就交给应用层处理,应用层返回已消费字节数。如果 buffer 里有半包,连接层保留下次继续解析。
#### 快照异步创建。
1. 使用fork的Copy On Write机制实现的异步快照创建不会受到后续更新请求的影响
#### 实现SPSC/SPMC专门uring线程实现异步的增量、全量落盘操作。
1. 熟悉了SPSC无锁队列的实现方案。
1. 无锁、cache friendly
2. 流程:
1. 生产者组装task丢给SPSC。
2. 消费者从中取出然后执行置入destroy_queue触发eventfd。
3. 生产者释放destroy_queue。
3. 解决的问题:
1. iouring 由于 cqe 接收不及时导致的 task 丢失无法释放。
1. 通过背压解决设置inflight上限。
2. 背压后,生产者速度 > 消费者速度ringbuffer 满导致只能阻塞主线程或降低速度。
1. 通过修改SPSC为SPMC构建落盘线程组实现当task_queue满触发扩容线程组。
2. 每个落盘线程私有一个SPSC减少竞争。
3. 简易工作负载生产者线程随机选取两个uring线程选取任务少的push。
3. 采用读写段采用轮询方案导致乒乓现象,性能下降。
1. MESI协议定义了缓存行的四种状态Modified表示独占且已修改Exclusive表示独占且未修改Shared表示多核共享只读Invalid表示缓存行无效。
- **关键点:** 但从S状态读没有什么开销从I状态读则需要向其他CPU申请。
- **关键点:** 从E/M状态写也没有什么开销从S状态写则需要广播invalidate并且等待ACK(50-100时钟周期)。
- 原子操作由于内存序的顺序性和可见性语义也有额外开销刷新invalidate queue、阻止指令重排
2. 短临界区:分层退避。自旋-让出CPU时间片-较长时间休眠。允许生产者在期间无争用的写入一批数据,然后统一读。
3. 更通用的情况事件驱动。用futex替代轮询。Fast Userspace Mutex。
1. **消费者:** 调用`syscall(SYS_futex, &futex_word, FUTEX_WAIT, old_val, &timeout, NULL, 0)` , 如果futex_word仍等于old_val线程进入内核等待队列真正睡眠不占用CPU。
2. **生产者:** 调用`syscall(SYS_futex, &futex_word, FUTEX_WAKE, 1, NULL, NULL, 0)` 唤醒一个等待线程。
4. 优化:生产者速度 > 消费者速度则写端不需要关注读指针或者极少关注读指针缓冲区大小的1/2次写入才考虑一次。
#### 基于BinLog上OffSet的主从同步(已有数据+实时数据)设计。
1. 初始化阶段:
1. master 持续将收到的更新请求+seq_id 落盘到本地 binlog 中。
2. slave 向 master 发起连接并且发送本地binlog中最大的seq_id 为 slave_seq_id。
2. 执行阶段:
1. master启动一个独立同步线程与 slave 构建连接。同步线程有两个阶段:
1. slave_seq_id < local_min_seq_idmaster通过fork创建内存快照发送且发送同时刻的local_max_seq_id。
2. slave_seq_id < local_max_seq_id通过自实现同步协议批量发送binlog的seq并且回包new_slave_seq_id。
3. slave_seq_id == local_max_seq_id线程休眠。
2. master收到新的请求的时候会通过条件变量唤醒同步线程。
#### 基于ebpf的实时数据同步设计。
**基准性能**Kvstore QPS ~90w无持久化/同步)。
ebpf对程序的影响要考虑如下方面
1. eBPF 探针的触发频率(上下文切换)
2. 数据拷贝方式
**用户态探针 (Uprobe) 的开销**
1. **逐条命令探测**。QPS大概在 25w左右。**原因**Uprobe 基于断点指令int3实现用户态 → 异常 → 内核 → eBPF → 返回用户态,高频触发会导致 CPU 流水线停顿严重。
2. **批处理探测**。QPS大概在85w左右。大幅减少了上下文切换次数平摊了中断开销。
**内核态探针 (Tracepoint/Kprobe) 的开销**
1. **Tracepoint (sys_exit_recvfrom)等**
1. 纯探测QPS ~85w。
2. 带数据拷贝 (read_user)QPS 降至 ~70w。**原因****bpf_probe_read_user** 是一个非常重的 helper开销远大于一次 memcpy。
2. **Kprobe (tcp_rcv_established)等**
1. 纯探测QPS ~86w。
2. 带数据拷贝 (read_kernel)QPS ~83w。**原因**:比**bpf_probe_read_user**轻得多。
3. 问题:此时数据位于 TCP 协议栈底层可能存在分片Fragment、乱序或非线性存储Paged SKB需要处理复杂的数据重组逻辑。
工作流程:
1. **内核态捕获**
1. eBPF 程序挂载于内核网络路径( TC 或 TCP 层),拦截流量。
2. 使用 bpf_probe_read_kernel 或 bpf_skb_load_bytes 高效提取数据载荷。
3. 通过 bpf_ringbuf_submit 将数据写入环形缓冲区。
2. **用户态转发**
1. 独立进程消费 RingBuffer。
2. 将数据暂存入本地队列,平滑突发流量,防止 RingBuffer 溢出导致的数据丢失。
3. **状态机控制**
1. **SYNC 阶段**:探测到 __ssync识别 Slave 连接信息IP/Port标记状态为“同步中”。
2. **READY 阶段**:探测到 __sready标志全量同步完成。
3. **实时转发**Agent 启动独立线程,消耗 Pending Queue将增量数据通过标准 TCP 发送给 Slave。
##### TC 与 XDP
网络栈
```
-> [ 网卡 (NIC) ]
-> [ XDP (eXpress Data Path) ] <--- xdp 处理原始帧 no skb
-> [ sk_buff 分配 ]
-> [ TC (Traffic Control) Ingress ] | tc 可操作 on skb
-> [ Netfilter (PREROUTING) ]
-> [ IP 协议栈 ] | ip_rcv -> ip_local_deliver
-> [ TCP 协议栈 ] | tcp_v4_rcv -> tcp_rcv_established -> tcp_data_queue
producer skb -> sk->sk_receive_queue
____________________________________________________________________
consumer sk->sk_receive_queue
-> [ Socket Layer ] | tcp_recvmsg 拷贝到内存
-> [ Syscall Interface ] | sys_exit_recvfrom
-> [ 用户态应用 (Kvstore) ]
```
XDP 是网卡驱动层的探点操作系统刚刚收到数据包DMA读入ringbufferCRC校验还没有分配sk_buffer。数据形态是**裸的以太网帧** 。
TC 是在 sk_buff 分配之后IP 协议栈处理之前的探测点。数据形态是__sk_buffer。
TCP协议栈是分界点
#### 内存泄露探测功能,实现热插拔。
方案1 基于bpf
1. 通过预定义宏__FILE__等封装一个内存分配宏定义向真正的分配函数传递代码位置等信息。
2. 通过bpf探测内存分配的地址、大小、文件、代码位置、函数等信息并且记录。
3. 通过bpf探测内存释放的指针信息然后释放。
4. 打印最终剩余的内存地址。
5. 缺点bpf 探测 malloc 等对性能的影响非常的大。
方案2 基于全局变量启用的代码内置探测工具,在网络层接收启用/关闭探测工具的请求。
1. 分配时打开一个文件,关闭时删除。
2. 分配时使用kv保存删除时删除k。
都对性能和内存有很大的影响,不建议长时间启用。
#### 实现支持分配可变长度内存块的内存池。
1. 熟悉了glibc 的 ptmalloc的底层操作。默认阈值约 128KB且会根据分配行为动态调整。
1. '<= 默认阈值 通过 brk/sbrk 扩展连续堆,堆里维护了 chunk 结构。
2. '> 默认阈值 的块用 mmap 独立申请,释放时 munmap。
3. 每个 chunk 头部存大小信息(通常 8~16 字节),用户拿到的是 chunk + 头部后的地址。
4. 空闲 chunk 按大小分到多个 bintcache、fastbin、small bin、large bin 等fastbin 和 tcache 为了速度不合并相邻空闲块。
5. 缺点:
1. 分配路径有较多分支判断和链表遍历,不是严格 O(1)。
2. 小块故意不合并fastbin/tcache会导致外部碎片。
3. 长期运行内存利用率下降。
2. 内存池实现:
1. 内存池有多个桶,桶中存储固定大小的块。分配/释放均为 O(1)
2. 以 huge page 为单位向系统申请内存并切分为固定块。
3. 当页内块全部释放时整页归还系统,显著降低长期碎片。
4. 通过地址对齐 O(1) 反推出页头元信息(所属桶、空闲计数)。
5. malloc通常向上对齐桶对应的存储大小可以根据不同系统设定减少内部碎片。
6. 使用 bitmap + freelist 防 double free 且无额外查找开销。
7. 每线程独立内存池相对malloc更少的锁竞争。
8. 大块分配自动退化为 malloc 处理。
相比 ptmalloc该设计消除了外部碎片降低了系统调用次数并在多线程场景下显著提升分配性能与内存利用率。