系统服务 · 共享内存
1. 模块概述
- 主要功能:基于 Linux DMA-HEAP 的共享内存分配库,提供跨进程零拷贝传输能力,支持 DMA-BUF 文件描述符传递和 cache 同步操作,与 libdma 配合实现高效数据传输
- 规格或特性:
- 支持 DMA-HEAP 分配,可指定物理连续、非缓存等属性
- 支持 cache 同步操作(TO_DEVICE / FROM_DEVICE)
- shm_transport:基于 Unix Domain Socket 的 fd 传输框架,支持发布/订阅模式
- 支持多种内存域:POSIX 共享内存、DMA-BUF、物理内存
- 相关目录结构:
components/system/shm/
├── include/
│ ├── shm.h # 共享内存分配 API
│ └── shm_transport.h # 跨进程传输 API
├── src/ # 库实现源码
├── test/
│ ├── shm_transport_pub_test.c # 发布端测试
│ └── shm_transport_sub_test.c # 订阅端测试
├── CMakeLists.txt # 构建配置
└── README.md # 模块说明
2. 环境准备
前置条件
- 运行环境:Linux 内核(支持 DMA-HEAP)、CMake ≥ 3.16、gcc/clang 编译器
- 依赖与外部资源:
- 内核需支持 DMA-HEAP(
/dev/dma_heap/*设备节点存在) - 无额外外部依赖,仅依赖标准 C 库和 pthread
- 内核需支持 DMA-HEAP(
- 环境变量与初始化:若通过 SDK 编译,需先执行
source build/envsetup.sh并lunch <target> - 硬件与连接:目标板需支持 DMA-HEAP,确认
/dev/dma_heap/目录存在 - 工具与权限:访问
/dev/dma_heap/*设备节点需相应权限(通常需 root 或加入对应用户组)
构建编译
- 获取代码:代码位于 SDK 的
components/system/shm目录 - 本模块编译:
独立编译方式:
cd components/system/shm
mkdir build && cd build
cmake ..
cmake --build .
通过 SDK mm 编译:
source build/envsetup.sh
lunch <target>
cd components/system/shm
mm clean && mm -v
- 产物:
- 用户态库:
libshm.so或libshm.a - 测试程序:
build/output/shm_transport_pub_test、build/output/shm_transport_sub_test
- 用户态库:
3. 示例使用(从 0 跑通)
本节为读者按步骤复现的主线:命令可复制、路径写绝对或写明工作目录、每步给出预期现象(终端输出片段、退出码、界面或日志关键字)。
3.1 跨进程共享内存传输
前置:已编译 libshm,目标板已上电并可通过串口或 SSH 访问
步骤 1:确认 DMA-HEAP 设备节点存在
ls -l /dev/dma_heap/
预期现象:显示可用的 heap 设备,如 system、cma 等
步骤 2:启动发布端(终端 1)
cd components/system/shm/build/output
./shm_transport_pub_test
预期现象:程序启动并等待订阅者连接,输出类似:
[INFO] Publisher opened: /tmp/shm_trans_test.sock
[INFO] Waiting for subscribers...
步骤 3:启动订阅端(终端 2)
cd components/system/shm/build/output
./shm_transport_sub_test test
预期现象:订阅端连接成功,开始接收数据帧,输出类似:
[INFO] Subscriber connected to: test
[INFO] Received frame: seq=0, size=4096, timestamp=...
[INFO] Data verification passed
发布端同时显示:
[INFO] Subscriber connected
[INFO] Publishing frame: seq=0, size=4096
3.2 共享内存分配与 cache 同步
前置:见 §2
步骤 1:编写测试代码分配共享内存(参考 test/ 目录示例)
步骤 2:分配物理连续的 DMA buffer
struct shm_domain *domain = shm_create_domain_dmabuf("system");
struct shm_buffer buf;
shm_alloc(domain, 1024*1024, SHM_FLAG_CONTIGUOUS, &buf);
预期现象:分配成功,buf.vaddr 指向可用内存,buf.fd 为有效文件描述符
步骤 3:执行 cache 同步操作
// CPU 写入数据后,同步到设备
shm_sync_cache(domain, &buf, SHM_SYNC_TO_DEVICE);
预期现象:cache 刷新完成,数据对 DMA 设备可见
4. 应用开发
-
对外 API 或接口形态:
- 头文件:
include/shm.h(共享内存分配)、include/shm_transport.h(跨进程传输) - 库名:
libshm.so或libshm.a - 主要 API(shm.h):
shm_create_domain_dmabuf()- 创建 DMA-BUF 内存域shm_alloc()- 分配共享内存shm_import_fd()- 导入已有的 fdshm_sync_cache()- cache 同步操作shm_free_buffer()- 释放 buffershm_destroy_domain()- 销毁内存域
- 主要 API(shm_transport.h):
shm_trans_pub_open()- 打开发布端shm_trans_pub_add_buffer()- 添加 buffer 到传输池shm_trans_pub_acquire()- 获取可写 buffershm_trans_pub_publish()- 发布数据帧shm_trans_sub_open()- 打开订阅端shm_trans_sub_recv()- 接收数据帧shm_trans_sub_release()- 释放数据帧
- 头文件:
-
调用方式与注意点:
- 线程安全:单个 domain/pub/sub 对象不支持多线程并发,需上层加锁
- 权限:需确保
/dev/dma_heap/*设备节点可访问 - 资源释放:必须按顺序释放:先
shm_free_buffer(),再shm_destroy_domain() - fd 生命周期:
shm_buffer.fd在 buffer 释放前保持有效,可用于跨进程传递 - cache 同步:使用 DMA 传输前后需调用
shm_sync_cache()确保数据一致性 - transport 模式:发布端需先
add_buffer()再acquire(),订阅端recv()后必须release()
-
参考 demo 或示例路径:
components/system/shm/test/shm_transport_pub_test.c、shm_transport_sub_test.c
5. 调试指南
- 内核日志查看:
dmesg | grep dma_heap可查看 DMA-HEAP 相关日志 - 用户态日志:可在代码中添加
printf或使用strace跟踪系统调用 - 常见调试步骤:
- 确认 DMA-HEAP 设备存在:
ls -l /dev/dma_heap/ - 检查权限:
sudo chmod 666 /dev/dma_heap/*(测试用) - 检查 socket 文件:
ls -l /tmp/shm_trans_*.sock - 使用
lsof查看 fd 泄漏:lsof -p <pid> | grep dma_heap - 使用
strace跟踪:strace -e ioctl,mmap ./test_program
- 确认 DMA-HEAP 设备存在:
- 与硬件/内核同事分工时的信息收集清单:
- 内核版本和 DMA-HEAP 配置(
cat /proc/config.gz | gunzip | grep DMA_HEAP) - 可用 heap 列表(
ls /dev/dma_heap/) - 分配失败时的 dmesg 日志
- 内存使用情况(
cat /proc/meminfo)
- 内核版本和 DMA-HEAP 配置(
6. 常见问题
| 现象 | 可能原因 | 处理 |
|---|---|---|
/dev/dma_heap/ 不存在 | 内核未启用 DMA-HEAP | 检查内核配置,重新编译内核 |
shm_alloc() 返回失败 | 内存不足或 heap 不支持请求的属性 | 减小分配大小,或使用其他 heap |
shm_import_fd() 失败 | fd 无效或已关闭 | 确认 fd 来源正确且未被关闭 |
| 跨进程传输数据错误 | cache 未同步 | 在 DMA 传输前后调用 shm_sync_cache() |
shm_trans_sub_recv() 超时 | 发布端未发送数据或 socket 断开 | 检查发布端状态,确认 socket 连接正常 |
| socket 连接失败 | socket 文件不存在或权限不足 | 确认发布端已启动,检查 /tmp/ 权限 |
附录:性能与测试数据
| 指标 | 数值 | 测试条件 |
|---|---|---|
| 跨进程传输延迟 | ~100 μs | 4KB 数据帧,本地 Unix socket |
| 零拷贝吞吐量 | ~2.5 GB/s | 1MB 数据帧,连续传输 |
| fd 传递开销 | ~20 μs | 单次 fd 传递延迟 |
测试方法:使用 shm_transport_pub_test 和 shm_transport_sub_test 在目标板上测试,硬件平台为 SPACEMIT K1 芯片,Linux 内核 5.10,单发布端单订阅端,统计 1000 次平均值。测试命令:
# 终端 1
./shm_transport_pub_test --benchmark --frames 1000
# 终端 2
./shm_transport_sub_test test --benchmark