第三章 Fast-DDS核心源码导读与流程拆解

1.3 核心源码导读与流程拆解

章节状态:已完成

更新时间:2026-05-21


一、生活化通俗类比

类比:餐厅点餐系统

想象你去一家大型连锁餐厅点餐:

Discovery(发现阶段)

  • 你走进商场,看到多家餐厅(Participant Discovery)
  • 你选择一家,服务员给你菜单(Topic Discovery)
  • 菜单上的菜品就是Topic,如"宫保鸡丁"、"鱼香肉丝"
  • 你确认这家有你想吃的菜(Endpoint Discovery)

Data Flow(数据流)

  • 你下单(write):服务员记录 → 传给厨房 → 厨师做菜
  • 上菜(read):厨师完成 → 传菜员 → 送到你桌上

QoS(服务质量)

  • 普通餐厅:尽力而为,可能上错或漏单(Best-Effort)
  • 高档餐厅:必达承诺,每道菜确认(Reliable)
  • 快餐店:只保留最近10单(History Depth=10)

Transport(传输方式)

  • 堂食:店内传送带(Shared Memory,最快)
  • 外卖:电动车(UDP,快但可能丢)
  • 重要文件:专车送达(TCP,可靠但慢)

二、核心模块与商用场景

场景1:Discovery机制(自动发现)

2.1.1 商用需求
商用场景 需求 Fast-DDS解决方案
大规模集群(1000+节点) 快速发现、低带宽消耗 发现服务器模式(Discovery Server)
跨网段部署 NAT穿透、广域网发现 TCP传输 + 发现服务器
动态扩缩容 节点即插即用 SPDP/EDP自动发现协议
安全隔离 只发现授权节点 DDS-Security + 域名隔离
2.1.2 核心流程
sequenceDiagram autonumber participant A as Participant A
新加入节点 participant PD as PDP
Participant Discovery participant EDP as EDP
Endpoint Discovery participant B as Participant B
已有节点 Note over A,B: === Participant Discovery === A->>PD: 发送Participant DATA(p) PD->>B: 转发A的信息 B->>PD: 发送Participant DATA(q) PD->>A: 转发B的信息 A->>B: 直接通信建立 Note over A,B: === Endpoint Discovery === A->>EDP: 发布DataWriter信息
(Topic: /vehicle/speed) EDP->>B: 查询匹配的DataReader B->>EDP: 返回匹配结果 EDP->>A: 通知匹配成功 Note over A,B: === 数据传输就绪 === A->>B: 开始发送数据
2.1.3 源码定位
组件 源码路径 关键类
PDP src/cpp/rtps/builtin/discovery/participant/ PDP.cpp, PDPListener.cpp
EDP src/cpp/rtps/builtin/discovery/endpoint/ EDP.cpp, EDPSimple.cpp
发现数据 src/cpp/rtps/builtin/data/ ParticipantProxyData.cpp, WriterProxyData.cpp
2.1.4 gdb调试:完整案例(Discovery机制)
2.1.4.1 编译准备(Debug模式)
bash 复制代码
# 1. 进入Fast-DDS目录
cd /home/my/code/opensource/Fast-DDS

# 2. 创建构建目录(如果不存在)
mkdir -p build && cd build

# 3. 配置Debug编译选项
# -DCMAKE_BUILD_TYPE=Debug: 生成调试符号
# -DCMAKE_CXX_FLAGS="-g -O0": 保留符号表,关闭优化
cmake .. \
    -DCMAKE_BUILD_TYPE=Debug \
    -DCOMPILE_EXAMPLES=ON \
    -DCMAKE_CXX_FLAGS="-g -O0 -fno-omit-frame-pointer"

# 4. 编译hello_world示例(使用多线程加速)
make hello_world -j$(nproc)

# 5. 验证编译结果(检查是否包含调试符号)
file ./examples/cpp/hello_world/hello_world
# 预期输出:... not stripped ...

# 6. 检查符号表
nm -C ./examples/cpp/hello_world/hello_world | grep "DomainParticipantFactory::create_participant" | head -5
# 预期输出:T eprosima::fastdds::dds::DomainParticipantFactory::create_participant(...)

编译选项说明:

选项 作用 为什么需要
-DCMAKE_BUILD_TYPE=Debug 生成调试版本 保留调试符号,支持断点
-g 生成调试信息 gdb需要此信息定位源码
-O0 关闭编译优化 防止代码行错位,单步执行准确
-fno-omit-frame-pointer 保留帧指针 方便栈回溯分析
2.1.4.2 启动gdb调试Discovery流程
bash 复制代码
# 1. 启动gdb
gdb ./examples/cpp/hello_world/hello_world

# 2. 设置断点:Participant创建(Discovery起点)
(gdb) break eprosima::fastdds::dds::DomainParticipantFactory::create_participant

# 3. 设置断点:PDP发送发现消息
gdb> break eprosima::fastrtps::rtps::PDP::announce_participant_state

# 4. 设置断点:PDP接收发现消息
gdb> break eprosima::fastrtps::rtps::PDPListener::on_new_cache_change_added

# 5. 设置断点:EDP匹配Writer和Reader
gdb> break eprosima::fastrtps::rtps::EDP::pairing_writer_reader

# 6. 设置断点:匹配状态通知应用层
gdb> break eprosima::fastdds::dds::DataWriterListener::on_publication_matched

# 7. 运行程序
gdb> run

# 8. 程序会在第一个断点暂停,查看调用栈
gdb> bt

# 9. 查看局部变量
gdb> info locals

# 10. 查看Participant QoS配置
gdb> p participant_qos

# 11. 单步执行(进入函数内部)
gdb> step

# 12. 继续运行到下一个断点
gdb> continue

# 13. 当命中announce_participant_state时,查看发现消息内容
gdb> p participant_data

# 14. 查看Domain ID
gdb> p participant_data.m_domainId

# 15. 继续运行
gdb> continue

# 16. 当命中pairing_writer_reader时,查看匹配信息
gdb> p writer_guid
gdb> p reader_guid

# 17. 打印Topic名称
gdb> p topic_name

# 18. 继续运行直到程序结束或Ctrl+C
gdb> continue
2.1.4.3 常用gdb命令速查
命令 缩写 作用 使用场景
break <function> b 设置断点 在函数入口暂停
run r 运行程序 启动调试
continue c 继续运行 跳到下一个断点
step s 单步进入 进入函数内部
next n 单步跳过 不进入函数内部
finish fin 运行到函数返回 快速跳出当前函数
backtrace bt 查看调用栈 了解代码调用路径
info locals i lo 查看局部变量 检查当前上下文
print <var> p 打印变量值 查看变量内容
display <var> disp 持续显示变量 每次暂停自动显示
watch <var> wa 监视变量变化 变量改变时暂停
list l 显示源码 查看当前代码位置
info breakpoints i b 查看断点列表 管理所有断点
delete <num> d 删除断点 清除不需要的断点
quit q 退出gdb 结束调试
2.1.4.4 高级调试技巧

技巧1:条件断点(只在特定条件暂停)

bash 复制代码
# 只在domain_id为0时暂停
gdb> break eprosima::fastdds::dds::DomainParticipantFactory::create_participant if domain_id == 0

# 只在特定Topic匹配时暂停
gdb> break eprosima::fastrtps::rtps::EDP::pairing_writer_reader if strcmp(topic_name, "/vehicle/speed") == 0

技巧2:多线程调试

bash 复制代码
# 查看所有线程
gdb> info threads

# 切换到特定线程
gdb> thread 2

# 只运行当前线程
gdb> set scheduler-locking on

技巧3:保存和加载调试会话

bash 复制代码
# 保存断点配置到文件
gdb> save breakpoints discovery.gdb

# 下次调试时加载
gdb> source discovery.gdb
2.1.4.5 其他场景调试参考

其他场景的调试步骤与Discovery类似,参考以下模板:

bash 复制代码
# 1. 编译(同上,确保是Debug模式)
# 2. 启动gdb: gdb <可执行文件>
# 3. 设置对应场景的断点(见各场景断点列表)
# 4. run -> 观察 -> continue -> 分析

各场景关键断点快速参考:

场景 启动命令 关键断点
SHM零拷贝 gdb ./hello_world SharedMemTransport::send
DataSharing gdb ./hello_world WriterPool::create_new_payload
Reliable传输 gdb ./hello_world StatefulWriter::send_heartbeat
分片传输 gdb ./hello_world RTPSMessageGroup::add_data_frag
FlowControl gdb ./FlowControlExample FlowController::schedule
Stateless/Stateful gdb ./hello_world StatelessWriter::unsent_change_added
Liveliness gdb ./hello_world LivelinessManager::assert_liveliness
Persistence gdb ./hello_world SQLite3PersistenceService::add_persistent_sample
2.1.4.6 Discovery机制调试(详细)

观察要点

断点位置 观察变量 商用价值
create_participant domain_id, participant_qos 确认Domain隔离是否正确
announce_participant_state participant_guid, metatraffic_locators 确认发现消息发送地址
on_new_cache_change_added change->writerGUID 确认远程Participant身份
pairing_writer_reader writer_guid, reader_guid, topic_name 确认Topic匹配逻辑
on_publication_matched info.current_count, info.last_subscription_handle 确认匹配状态通知
2.1.5 对二次开发的启示
  1. 自定义发现协议 :继承PDP类,实现自定义发现机制(如通过etcd/consul)
  2. 发现过滤器 :在EDP::pairing_writer_reader中加入自定义匹配逻辑
  3. 发现性能优化:减少发现消息频率,或使用发现服务器模式

场景2:Shared Memory传输(零拷贝)

2.2.1 商用需求
商用场景 需求 Fast-DDS解决方案
高频传感器数据(激光雷达) 微秒级延迟、零拷贝 Shared Memory Transport
大带宽数据(摄像头图像) 避免内核态拷贝 共享内存 + 零拷贝序列化
同机多进程通信 低CPU占用、高吞吐 SHM代替UDP回环
实时控制 确定性延迟 SHM + 实时线程调度
2.2.2 核心流程
sequenceDiagram autonumber participant DW as DataWriter participant SHM_W as SHM Writer
Segment participant Port as SHM Port
(/dev/shm/fastrtps_*) participant SHM_R as SHM Reader
Segment participant DR as DataReader Note over DW,DR: === 初始化阶段 === DW->>Port: 创建/打开共享内存端口 DR->>Port: 打开共享内存端口 Note over DW,DR: === 数据传输(零拷贝) === DW->>SHM_W: 1. 分配SHM缓冲区 DW->>SHM_W: 2. 直接写入数据(用户态) DW->>Port: 3. 写入端口描述符(通知Reader) Port->>DR: 4. 通知数据到达 DR->>SHM_R: 5. 直接读取数据(用户态) DR->>SHM_R: 6. 释放缓冲区 Note over DW,DR: === 关键:全程无内核拷贝 ===
2.2.3 源码定位
组件 源码路径 关键类/函数
SHM Transport src/cpp/rtps/transport/shared_mem/ SharedMemTransport.cpp
SHM管理 src/cpp/rtps/transport/shared_mem/ SharedMemManager.cpp
SHM缓冲区 src/cpp/rtps/transport/shared_mem/ SharedMemSegment.hpp
当前IDE文件 src/cpp/rtps/transport/shared_mem/ SharedMemTransportBak.h
2.2.4 gdb调试:SHM零拷贝流程

调试目标:观察SHM如何分配、写入、通知、读取

bash 复制代码
# 启动gdb
gdb ./examples/cpp/hello_world/hello_world

# 设置断点:SHM Transport初始化
gdb> break eprosima::fastdds::rtps::SharedMemTransport::init

# 设置断点:SHM发送(关键:观察零拷贝路径)
gdb> break eprosima::fastdds::rtps::SharedMemTransport::send

# 设置断点:SHM接收
gdb> break eprosima::fastdds::rtps::SharedMemTransport::receive

# 设置断点:缓冲区分配
gdb> break eprosima::fastdds::rtps::SharedMemManager::alloc_buffer

# 设置断点:端口打开(观察/dev/shm创建)
gdb> break eprosima::fastdds::rtps::SharedMemManager::open_port

# 运行
gdb> run

观察要点

断点位置 观察变量 商用价值
init segment_name, segment_size, max_msg_size 确认SHM段大小配置
send shm_handle, data_size, dest_locators 确认数据写入SHM而非socket
receive shm_handle, buffer_size 确认从SHM读取数据
alloc_buffer buffer_size, segment->get_free_bytes() 确认缓冲区分配策略
open_port port_name, port_address 确认端口命名规则

系统级观察

bash 复制代码
# 终端2:监控共享内存文件创建
watch -n 0.5 'ls -la /dev/shm/ | grep fastrtps'

# 终端3:查看SHM段大小
df -h /dev/shm

# 终端4:监控进程打开的SHM文件
lsof -p $(pgrep hello_world) | grep /dev/shm
2.2.5 strace观察:SHM vs UDP差异
bash 复制代码
# 观察SHM传输的系统调用(注意:send/recv阶段无read/write)
strace -f -e trace=openat,mmap,munmap,close,ftruncate \
    ./examples/cpp/hello_world/hello_world 2>&1 | grep -E "shm|/dev/shm"

# 预期输出:只有文件操作,没有socket发送
openat(AT_FDCWD, "/dev/shm/fastrtps_port7410", O_RDWR|O_CREAT) = 6
ftruncate(6, 1048576) = 0
mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0) = 0x7f1234567000
# 注意:没有sendto/recvfrom调用!
2.2.6 对二次开发的启示
  1. 自定义SHM策略 :继承SharedMemTransport,实现环形缓冲区替代动态分配
  2. 大消息优化 :调整max_msg_sizesegment_size匹配业务需求
  3. 跨机SHM:结合RDMA技术,实现跨机零拷贝

场景3:Reliable传输(可靠传输)

2.3.1 商用需求
商用场景 需求 Fast-DDS解决方案
金融交易数据 100%送达、顺序保证 Reliable + KeepAll History
工业控制指令 必达、超时检测 Reliable + Deadline QoS
文件传输 大文件可靠传输 Reliable + 分片 + 重传
日志收集 不丢数据、可重传 Reliable + Durability
2.3.2 核心流程
sequenceDiagram autonumber participant W as Writer participant HB as Heartbeat机制 participant N as 网络 participant ACK as ACK/NACK机制 participant R as Reader Note over W,R: === 可靠传输流程 === W->>N: 发送Data(seq=1) W->>HB: 启动Heartbeat定时器 alt Reader正常接收 N->>R: 接收Data(seq=1) R->>ACK: 发送ACK(seq=1) ACK->>W: 确认收到 W->>W: 从History移除seq=1 else Reader未收到(丢包) W->>HB: Heartbeat超时 HB->>R: 发送Heartbeat(nack=seq=1) R->>ACK: 发送NACK(seq=1) ACK->>W: 请求重传 W->>N: 重传Data(seq=1) N->>R: 接收Data(seq=1) end
2.3.3 源码定位
组件 源码路径 关键类/函数
Reliable Writer src/cpp/rtps/writer/ StatefulWriter.cpp
Heartbeat src/cpp/rtps/writer/ StatefulWriter::send_heartbeat
ACK处理 src/cpp/rtps/writer/ StatefulWriter::process_acknack
Reliable Reader src/cpp/rtps/reader/ StatefulReader.cpp
2.3.4 gdb调试:Reliable传输流程

调试目标:观察Heartbeat、ACK、重传机制

bash 复制代码
# 启动gdb
gdb ./examples/cpp/hello_world/hello_world

# 设置断点:发送Heartbeat
gdb> break eprosima::fastrtps::rtps::StatefulWriter::send_heartbeat

# 设置断点:处理ACK/NACK
gdb> break eprosima::fastrtps::rtps::StatefulWriter::process_acknack

# 设置断点:数据重传
gdb> break eprosima::fastrtps::rtps::StatefulWriter::resend_data

# 设置断点:等待ACK确认
gdb> break eprosima::fastdds::dds::DataWriter::wait_for_acknowledgments

# 设置断点:Reader发送ACK
gdb> break eprosima::fastrtps::rtps::StatefulReader::send_acknack

# 运行
gdb> run

观察要点

断点位置 观察变量 商用价值
send_heartbeat heartbeat_count, first_sn, last_sn 确认心跳频率和范围
process_acknack ack_count, nack_count, nack_bitmap 确认丢包检测
resend_data sequence_number, reader_guid 确认重传逻辑
wait_for_acknowledgments acknowledgment_count, max_blocking_time 确认阻塞等待机制
2.3.5 Wireshark分析:Reliable协议交互
bash 复制代码
# 抓包
sudo tcpdump -i lo -w reliable.pcap portrange 7410-7415

# Wireshark过滤条件
# 查看Heartbeat
rtps.sm.id == 0x07

# 查看ACK
rtps.sm.id == 0x06

# 查看NACK
rtps.sm.id == 0x08

# 查看DATA消息
rtps.sm.id == 0x15 && rtps.data
2.3.6 对二次开发的启示
  1. 自定义重传策略 :修改resend_data实现自适应重传(如根据RTT调整)
  2. 批量ACK优化:修改ACK发送策略,减少小数据包数量
  3. 可靠性分级:实现部分可靠(如只保证关键消息)

场景4:分片传输(大消息处理)

2.4.1 商用需求
商用场景 需求 Fast-DDS解决方案
高清图像传输 4K图像(10MB+) 分片 + 可靠传输
点云数据 激光雷达一帧(100KB-1MB) 分片 + SHM零拷贝
日志批量上传 大批量日志数据 分片 + 流控
固件升级 大文件可靠传输 分片 + 校验
2.4.2 核心流程
sequenceDiagram autonumber participant W as Writer participant Frag as Fragmenter participant N as 网络 participant Reas as Reassembler participant R as Reader Note over W,R: === 分片发送 === W->>Frag: 数据(100KB) Frag->>Frag: 分片为10个10KB片段 loop 发送每个分片 Frag->>N: 发送DataFrag(seq=1, frag=1/10) Frag->>N: 发送DataFrag(seq=1, frag=2/10) Frag->>N: ... Frag->>N: 发送DataFrag(seq=1, frag=10/10) end Note over W,R: === 分片重组 === N->>Reas: 接收frag=1/10 N->>Reas: 接收frag=2/10 N->>Reas: ... Reas->>Reas: 缓存分片,检测完整性 Reas->>R: 完整数据(seq=1)
2.4.3 源码定位
组件 源码路径 关键类/函数
分片发送 src/cpp/rtps/messages/ RTPSMessageGroup::add_data_frag
分片重组 src/cpp/rtps/reader/ FragmentedChangePitStop.cpp
分片缓存 src/cpp/rtps/reader/ FragmentedChangePitStop.hpp
2.4.4 gdb调试:分片传输流程

调试目标:观察分片如何生成、发送、缓存、重组

bash 复制代码
# 启动gdb
gdb ./examples/cpp/hello_world/hello_world

# 设置断点:分片判断
gdb> break eprosima::fastrtps::rtps::RTPSMessageGroup::add_data

# 设置断点:分片发送
gdb> break eprosima::fastrtps::rtps::RTPSMessageGroup::add_data_frag

# 设置断点:分片接收
gdb> break eprosima::fastrtps::rtps::FragmentedChangePitStop::process

# 设置断点:分片完成通知
gdb> break eprosima::fastrtps::rtps::FragmentedChangePitStop::is_complete

# 运行
gdb> run

观察要点

断点位置 观察变量 商用价值
add_data data_size, max_size, fragment_size 确认分片触发条件
add_data_frag fragment_starting_num, fragments_in_submessage 确认分片编号
process fragmented_change, received_fragments 确认分片缓存状态
is_complete missing_fragments, fragment_count 确认完整性检测
2.4.5 对二次开发的启示
  1. 动态分片大小:根据网络MTU动态调整分片大小
  2. 分片优先级:关键分片优先发送
  3. 并行传输:多路径并行传输不同分片

场景5:QoS策略(服务质量保证)

2.5.1 商用需求
商用场景 需求 QoS组合
实时视频流 低延迟、允许丢帧 Best-Effort + KeepLast(1)
传感器融合 最新数据优先 Best-Effort + KeepLast(5)
控制指令 必达、顺序保证 Reliable + KeepAll
日志记录 持久化、不丢失 Reliable + TransientLocal
心跳检测 超时感知 Liveliness + Deadline
2.5.2 核心流程
graph TB subgraph "QoS策略生效点" direction TB Write[DataWriter::write] History[HistoryCache管理] Send[Transport发送] Receive[Transport接收] Read[DataReader::read/take] Write -->|ResourceLimits
max_samples| History History -->|Reliability
History depth| Send Send -->|LatencyBudget| Receive Receive -->|Deadline
Liveliness| Read end
2.5.3 源码定位
组件 源码路径 关键类/函数
QoS策略定义 include/fastdds/dds/core/policy/ QosPolicies.hpp
History管理 src/cpp/rtps/history/ WriterHistory.cpp, ReaderHistory.cpp
Deadline检测 src/cpp/rtps/writer/ WriterPeriodicHeartbeat.cpp
Liveliness检测 src/cpp/rtps/writer/ LivelinessManager.cpp
2.5.4 gdb调试:QoS策略生效

调试目标:观察QoS如何在各阶段生效

bash 复制代码
# 启动gdb
gdb ./examples/cpp/hello_world/hello_world

# 设置断点:History添加(观察ResourceLimits)
gdb> break eprosima::fastrtps::rtps::WriterHistory::add_change

# 设置断点:History移除(观察KeepLast)
gdb> break eprosima::fastrtps::rtps::WriterHistory::remove_min_change

# 设置断点:Deadline检测
gdb> break eprosima::fastrtps::rtps::WriterPeriodicHeartbeat::deadline_missed

# 设置断点:Liveliness检测
gdb> break eprosima::fastrtps::rtps::LivelinessManager::callback

# 运行
gdb> run

观察要点

断点位置 观察变量 商用价值
add_change m_changes.size(), m_resource_limits 确认资源限制生效
remove_min_change history_qos.kind, history_qos.depth 确认History策略
deadline_missed deadline_qos.period, last_change_time 确认Deadline检测
callback liveliness_qos.kind, lease_duration 确认Liveliness状态
2.5.5 对二次开发的启示
  1. 动态QoS:运行时调整QoS策略
  2. QoS监控:统计QoS违规事件
  3. 自适应QoS:根据网络状况自动调整

三、调试工具速查表

3.1 gdb断点速查

场景 关键断点 观察目标
Discovery PDP::announce_participant_state EDP::pairing_writer_reader 发现流程、匹配逻辑
SHM零拷贝 SharedMemTransport::send SharedMemTransport::receive 零拷贝路径、缓冲区管理
Reliable传输 StatefulWriter::send_heartbeat StatefulWriter::process_acknack 心跳、ACK、重传
分片传输 RTPSMessageGroup::add_data_frag FragmentedChangePitStop::process 分片生成、重组
QoS策略 WriterHistory::add_change WriterHistory::remove_min_change History管理、资源限制
数据流 DataWriter::write DataReader::take_next_sample 端到端数据流

3.2 strace观察重点

场景 命令 观察目标
Discovery网络 strace -e connect,sendto,recvfrom UDP发现消息
SHM传输 strace -e openat,mmap,ftruncate 共享内存文件操作
性能分析 strace -T -e sendto,recvfrom 系统调用耗时
线程分析 strace -e clone,pthread_create 线程创建情况

3.3 Wireshark过滤条件

场景 过滤条件 观察目标
Discovery rtps.sm.id == 0x15 && rtps.participant SPDP发现消息
Endpoint匹配 rtps.sm.id == 0x15 && rtps.endpoint SEDP端点宣告
Heartbeat rtps.sm.id == 0x07 可靠性心跳
ACK/NACK `rtps.sm.id == 0x06 / rtps.sm.id == 0x08`
分片数据 rtps.data_frag 分片传输

四、问题定位决策树

graph TD A[问题现象] --> B{连接问题?} B -->|是| C[Discovery阶段] C --> C1[gdb: PDP/EDP断点] C --> C2[tcpdump: 抓SPDP消息] C --> C3[检查Domain ID] B -->|否| D{数据丢失?} D -->|是| E[Reliable传输] E --> E1[gdb: Heartbeat/ACK断点] E --> E2[Wireshark: 分析ACK/NACK] E --> E3[检查History QoS] D -->|否| F{延迟高?} F -->|是| G[传输层分析] G --> G1[gdb: SHM send/receive] G --> G2[strace -T: 系统调用耗时] G --> G3[检查Transport配置] F -->|否| H{内存问题?} H --> I[资源管理] I --> I1[gdb: History add/remove] I --> I2[valgrind: 内存泄漏] I --> I3[检查ResourceLimits]

场景6:DataSharing(数据共享)

2.6.1 商用需求
商用场景 需求 Fast-DDS解决方案
超大带宽数据(8K视频) 极致零拷贝、无内核参与 DataSharing机制
多订阅者同机消费 一份数据多份读取 共享内存池 + 引用计数
实时性要求极高 微秒级延迟 无锁队列 + 通知机制
降低CPU占用 减少数据拷贝 完全零拷贝路径
2.6.2 核心流程
sequenceDiagram autonumber participant DW as DataWriter participant WP as WriterPool participant SHM as Shared Memory participant RP as ReaderPool participant DR as DataReader Note over DW,DR: === 初始化 === DW->>WP: 创建WriterPool DR->>RP: 创建ReaderPool WP->>SHM: 映射共享内存段 RP->>SHM: 映射共享内存段 Note over DW,DR: === 数据发布(零拷贝) === DW->>WP: 获取空闲缓冲区 WP->>DW: 返回内存指针 DW->>SHM: 直接写入数据(用户态) DW->>WP: 提交缓冲区(通知Reader) WP->>RP: 通知数据到达 RP->>DR: 回调on_data_available DR->>RP: 获取数据指针 RP->>DR: 返回内存指针 DR->>SHM: 直接读取数据(用户态) DR->>RP: 释放缓冲区 Note over DW,DR: === 关键:全程无拷贝,无系统调用 ===
2.6.3 源码定位
组件 源码路径 关键类/函数
DataSharing src/cpp/rtps/DataSharing/ DataSharingPayloadPool.cpp
WriterPool src/cpp/rtps/DataSharing/ WriterPool.hpp
ReaderPool src/cpp/rtps/DataSharing/ ReaderPool.hpp
通知机制 src/cpp/rtps/DataSharing/ DataSharingNotification.cpp
2.6.4 gdb调试:DataSharing流程

调试目标:观察DataSharing如何实现完全零拷贝

bash 复制代码
# 启动gdb
gdb ./examples/cpp/hello_world/hello_world

# 设置断点:WriterPool初始化
gdb> break eprosima::fastrtps::rtps::WriterPool::create_new_payload

# 设置断点:获取缓冲区
gdb> break eprosima::fastrtps::rtps::DataSharingPayloadPool::get_payload

# 设置断点:通知Reader
gdb> break eprosima::fastrtps::rtps::DataSharingNotification::notify

# 设置断点:Reader接收通知
gdb> break eprosima::fastrtps::rtps::DataSharingListener::on_data_available

# 运行
gdb> run

观察要点

断点位置 观察变量 商用价值
create_new_payload segment_size, payload_size 确认共享内存池大小
get_payload payload->data, payload->payload_owner 确认零拷贝分配
notify notification_fd 确认通知机制(eventfd/pipe)
on_data_available pool->get_payload_count 确认Reader获取数据
2.6.5 与SHM的区别
特性 SHM Transport DataSharing
拷贝次数 1次(序列化到SHM) 0次(直接写共享内存)
序列化 需要 可选(支持PLAIN类型)
适用场景 中等带宽 超大带宽、极低延迟
复杂度
2.6.6 对二次开发的启示
  1. 自定义内存池 :继承DataSharingPayloadPool,实现特定对齐要求
  2. 通知机制优化:使用更高效的通知方式(如eventfd替代pipe)
  3. PLAIN类型优化:利用Fast-DDS的PLAIN类型实现完全零序列化

场景7:FlowControl(流量控制)

2.7.1 商用需求
商用场景 需求 Fast-DDS解决方案
网络带宽受限 防止网络拥塞 FlowController
多优先级数据 高优先级优先发送 优先级队列
平滑流量 避免突发流量 令牌桶算法
带宽限制 限制最大发送速率 ThroughputController
2.7.2 核心流程
sequenceDiagram autonumber participant App as 应用 participant DW as DataWriter participant FC as FlowController participant Queue as 优先级队列 participant Token as 令牌桶 participant RTPS as RTPSWriter Note over App,RTPS: === 数据写入 === App->>DW: write(data, priority=HIGH) DW->>FC: schedule(data) FC->>Queue: 按优先级入队 Note over App,RTPS: === 流量控制 === loop 定时器触发 Token->>Token: 生成令牌 Token->>FC: 通知可发送 end Note over App,RTPS: === 数据发送 === FC->>Queue: 获取高优先级数据 Queue->>FC: 返回数据 FC->>Token: 消耗令牌 FC->>RTPS: 发送数据
2.7.3 源码定位
组件 源码路径 关键类/函数
FlowController src/cpp/rtps/flowcontrol/ FlowControllerImpl.hpp
优先级队列 src/cpp/rtps/flowcontrol/ FlowControllerFactory.cpp
令牌桶 src/cpp/rtps/writer/ ThroughputController.cpp
2.7.4 gdb调试:FlowControl流程

调试目标:观察流量控制如何限制发送速率

bash 复制代码
# 启动gdb
gdb ./examples/cpp/flow_control/FlowControlExample

# 设置断点:数据调度
gdb> break eprosima::fastrtps::rtps::FlowController::schedule

# 设置断点:令牌桶检查
gdb> break eprosima::fastrtps::rtps::ThroughputController::process

# 设置断点:实际发送
gdb> break eprosima::fastrtps::rtps::FlowController::send

# 运行
gdb> run

观察要点

断点位置 观察变量 商用价值
schedule priority, flow_controller_name 确认优先级调度
process tokens, data_size 确认令牌桶限流
send queue_size, sent_bytes 确认实际发送速率
2.7.5 对二次开发的启示
  1. 自定义调度策略:实现加权公平队列(WFQ)
  2. 动态限速:根据网络状况动态调整令牌生成速率
  3. 拥塞感知:结合RTT测量实现自适应流控

场景8:Stateless vs Stateful(两种Writer/Reader)

2.8.1 商用需求
商用场景 需求 解决方案
大规模广播 一对多、不维护状态 StatelessWriter
可靠传输 维护Reader状态、支持重传 StatefulWriter
资源受限 减少内存占用 Stateless模式
高可靠性 精确重传、顺序保证 Stateful模式
2.8.2 核心区别
graph LR subgraph "Stateless模式" SW[StatelessWriter] SR[StatelessReader] SW -->|广播发送| SR SW -.不维护.-> ReaderState end subgraph "Stateful模式" FW[StatefulWriter] FR[StatefulReader] RP[ReaderProxy] WP[WriterProxy] FW -->|定向发送| FR FW -.维护.-> RP FR -.维护.-> WP end
2.8.3 源码定位
组件 源码路径 关键类
StatelessWriter src/cpp/rtps/writer/ StatelessWriter.cpp
StatefulWriter src/cpp/rtps/writer/ StatefulWriter.cpp
ReaderProxy src/cpp/rtps/writer/ ReaderProxy.cpp
WriterProxy src/cpp/rtps/reader/ WriterProxy.cpp
2.8.4 gdb调试:两种模式对比

Stateless模式调试:

bash 复制代码
# 设置断点:Stateless发送
gdb> break eprosima::fastrtps::rtps::StatelessWriter::unsent_change_added_to_history

# 观察:不维护Reader状态,直接广播

Stateful模式调试:

bash 复制代码
# 设置断点:Stateful发送
gdb> break eprosima::fastrtps::rtps::StatefulWriter::send_to_fixed_locators

# 设置断点:ReaderProxy更新
gdb> break eprosima::fastrtps::rtps::ReaderProxy::acked_changes_set

# 观察:维护每个Reader的状态

对比观察:

观察点 Stateless Stateful
Reader状态 有(ReaderProxy)
重传支持 不支持 支持(基于ACK/NACK)
内存占用
适用场景 广播、视频流 可靠传输、控制指令
2.8.5 对二次开发的启示
  1. 混合模式:根据Topic选择Stateless或Stateful
  2. 动态切换:运行时根据网络状况切换模式
  3. ReaderProxy优化:压缩ReaderProxy状态,减少内存

场景9:Liveliness(活性检测)

2.9.1 商用需求
商用场景 需求 Fast-DDS解决方案
节点故障检测 检测Publisher是否存活 Liveliness QoS
心跳超时 订阅端感知发布端异常 LeaseDuration
自动故障转移 主备切换 LivelinessChanged回调
系统监控 监控节点健康状态 定期Liveliness检测
2.9.2 核心流程
sequenceDiagram autonumber participant W as DataWriter participant LM as LivelinessManager participant Timer as 定时器 participant R as DataReader Note over W,R: === 自动模式(AUTOMATIC) === W->>LM: 声明Liveliness(写数据时自动) LM->>Timer: 启动LeaseDuration定时器 loop 定时检查 Timer->>LM: 检查是否超时 alt 未超时 LM->>LM: 重置定时器 else 超时 LM->>R: 通知LivelinessLost end end Note over W,R: === 手动模式(MANUAL_BY_TOPIC) === App->>W: 手动调用assert_liveliness W->>LM: 声明Liveliness LM->>Timer: 重置定时器
2.9.3 源码定位
组件 源码路径 关键类/函数
LivelinessManager src/cpp/rtps/writer/ LivelinessManager.cpp
定时器 src/cpp/rtps/resources/ TimedEvent.cpp
回调通知 src/cpp/rtps/reader/ WriterProxy.cpp
2.9.4 gdb调试:Liveliness检测
bash 复制代码
# 设置断点:声明活性
gdb> break eprosima::fastrtps::rtps::LivelinessManager::assert_liveliness

# 设置断点:超时检测
gdb> break eprosima::fastrtps::rtps::LivelinessManager::callback

# 设置断点:状态变化通知
gdb> break eprosima::fastdds::dds::DataReaderListener::on_liveliness_changed

# 运行
gdb> run

观察要点:

断点位置 观察变量 商用价值
assert_liveliness guid, lease_duration 确认活性声明
callback expired_writers 确认超时检测
on_liveliness_changed status.alive_count, status.not_alive_count 确认状态变化
2.9.5 对二次开发的启示
  1. 自定义检测策略:实现多层级活性检测(应用层 + 传输层)
  2. 故障预测:基于活性历史数据预测节点故障
  3. 自动恢复:检测到故障后自动重启或切换

场景10:Persistence(持久化)

2.10.1 商用需求
商用场景 需求 Fast-DDS解决方案
系统重启恢复 重启后不丢数据 PersistenceService
历史数据查询 查询历史样本 SQLite持久化
审计日志 数据可追溯 持久化存储 + 时间戳
状态恢复 断点续传 DurabilityService
2.10.2 核心流程
sequenceDiagram autonumber participant DW as DataWriter participant Cache as WriterHistory participant PS as PersistenceService participant DB as SQLite participant DR as DataReader Note over DW,DR: === 数据持久化 === DW->>Cache: add_change Cache->>PS: 通知持久化 PS->>DB: INSERT INTO samples DB->>PS: 确认写入 Note over DW,DR: === 系统重启后恢复 === DW->>PS: 启动加载 PS->>DB: SELECT * FROM samples DB->>PS: 返回历史数据 PS->>Cache: 恢复History Cache->>DW: 恢复完成 Note over DW,DR: === 新Reader订阅 === DR->>DW: 请求历史数据 DW->>Cache: 查询History Cache->>DR: 发送历史样本
2.10.3 源码定位
组件 源码路径 关键类/函数
PersistenceService src/cpp/rtps/persistence/ SQLite3PersistenceService.cpp
持久化Writer src/cpp/rtps/writer/ PersistentWriter.cpp
持久化Reader src/cpp/rtps/reader/ StatefulPersistentReader.cpp
2.10.4 gdb调试:Persistence流程
bash 复制代码
# 设置断点:持久化写入
gdb> break eprosima::fastrtps::rtps::SQLite3PersistenceService::add_persistent_sample

# 设置断点:加载历史
gdb> break eprosima::fastrtps::rtps::SQLite3PersistenceService::load_persistent_samples

# 运行
gdb> run
2.10.5 对二次开发的启示
  1. 自定义存储后端:实现Redis/MySQL持久化
  2. 数据压缩:持久化前压缩,减少存储空间
  3. 数据清理策略:实现TTL自动过期

三、调试工具速查表(更新)

3.1 gdb断点速查(完整版)

场景 关键断点 观察目标
Discovery PDP::announce_participant_state EDP::pairing_writer_reader 发现流程、匹配逻辑
SHM零拷贝 SharedMemTransport::send SharedMemTransport::receive 零拷贝路径、缓冲区管理
DataSharing WriterPool::create_new_payload DataSharingNotification::notify 完全零拷贝、通知机制
Reliable传输 StatefulWriter::send_heartbeat StatefulWriter::process_acknack 心跳、ACK、重传
分片传输 RTPSMessageGroup::add_data_frag FragmentedChangePitStop::process 分片生成、重组
FlowControl FlowController::schedule ThroughputController::process 流量控制、令牌桶
Stateless/Stateful StatelessWriter::unsent_change_added StatefulWriter::send_to_fixed_locators 两种模式差异
Liveliness LivelinessManager::assert_liveliness LivelinessManager::callback 活性检测、超时
Persistence SQLite3PersistenceService::add_persistent_sample 持久化写入
QoS策略 WriterHistory::add_change WriterHistory::remove_min_change History管理、资源限制
数据流 DataWriter::write DataReader::take_next_sample 端到端数据流

四、推荐材料学习

类型 资源 说明
官方文档 Fast-DDS RTPS层 fast-dds.docs.eprosima.com/en/latest/f...
官方文档 Fast-DDS Discovery fast-dds.docs.eprosima.com/en/latest/f...
官方文档 Fast-DDS SHM fast-dds.docs.eprosima.com/en/latest/f...
论文 RTPS Spec 8.5 OMG官方文档 - Discovery
论文 RTPS Spec 8.6 OMG官方文档 - Reliable Protocol
工具 GDB手册 sourceware.org/gdb/current...
工具 Wireshark RTPS wiki.wireshark.org/RTPS

六、数据源标注

6.1 核心源码路径

模块 路径 关键文件
Discovery src/cpp/rtps/builtin/discovery/participant/ PDP.cpp, PDPListener.cpp
Discovery src/cpp/rtps/builtin/discovery/endpoint/ EDP.cpp, EDPSimple.cpp
SHM Transport src/cpp/rtps/transport/shared_mem/ SharedMemTransport.cpp, SharedMemManager.cpp
Reliable Writer src/cpp/rtps/writer/ StatefulWriter.cpp
Reliable Reader src/cpp/rtps/reader/ StatefulReader.cpp
分片 src/cpp/rtps/messages/ RTPSMessageGroup.cpp
分片重组 src/cpp/rtps/reader/ FragmentedChangePitStop.cpp
History src/cpp/rtps/history/ WriterHistory.cpp, ReaderHistory.cpp
QoS include/fastdds/dds/core/policy/ QosPolicies.hpp
Topic src/cpp/fastdds/topic/ Topic.cpp, TopicImpl.cpp

6.2 示例代码

示例 路径 说明
hello_world examples/cpp/hello_world/ 基础发布订阅
discovery_server examples/cpp/discovery_server/ 发现服务器模式
delivery_mechanisms examples/cpp/delivery_mechanisms/ 传输机制对比
flow_control examples/cpp/flow_control/ 流量控制QoS
custom_payload_pool examples/cpp/custom_payload_pool/ 自定义内存池

七、学习检查清单与解答

检查项 解答要点
Discovery如何满足大规模集群? 使用Discovery Server模式,减少广播风暴
SHM如何实现零拷贝? 通过mmap共享内存,用户态直接读写,无内核拷贝
Reliable传输如何保证送达? Heartbeat + ACK/NACK机制,超时重传
分片传输何时触发? 数据大小超过Transport的max_message_size
gdb如何观察Discovery? PDP::announce_participant_stateEDP::pairing_writer_reader设置断点
strace如何验证SHM零拷贝? 观察只有mmap操作,没有sendto/recvfrom
Wireshark如何分析Reliable? 过滤rtps.sm.id == 0x07(Heartbeat)和rtps.sm.id == 0x06(ACK)
QoS策略在哪些阶段生效? write → History → send → receive → read,每个阶段都有QoS检查

本章内容按五件套规范编写,分场景、分模块组织,每个场景包含:商用需求、核心流程、源码定位、gdb调试、二次开发启示。

相关推荐
爱编程的小新☆9 小时前
Spring-AI入门
java·后端·spring
蝎子莱莱爱打怪9 小时前
👋🏻👋🏻再见,拉勾网——那个"最懂互联网人"的招聘平台倒了😭
前端·后端·招聘
BU摆烂会噶9 小时前
【LangGraph】House_Agent 实战(一):架构与环境配置
人工智能·vscode·python·架构·langchain·人机交互
woniu_buhui_fei9 小时前
单体服务拆分微服务
微服务·架构
heimeiyingwang9 小时前
【架构实战】日志体系ELK:集中化日志管理实践
elk·架构·wpf
用户2986985301410 小时前
Java 获取 Word 文档中修订记录的实现方法
java·后端
BU摆烂会噶10 小时前
【LangGraph】House_Agent 实战(五):持久化、流式输出与部署
人工智能·python·架构·langchain·人机交互
Trouvaille ~10 小时前
【Redis篇】为什么需要 Redis:从单机到分布式的架构演进之路
数据库·redis·分布式·缓存·中间件·架构·后端开发
启山智软10 小时前
从零搭建商城系统前端:技术选型与核心架构实践
前端·架构