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<br/>新加入节点
participant PD as PDP<br/>Participant Discovery
participant EDP as EDP<br/>Endpoint Discovery
participant B as Participant B<br/>已有节点
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信息<br/>(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模式)
cd /home/my/code/opensource/Fast-DDS
mkdir -p build && cd build
cmake .. \
-DCMAKE_BUILD_TYPE=Debug \
-DCOMPILE_EXAMPLES=ON \
-DCMAKE_CXX_FLAGS="-g -O0 -fno-omit-frame-pointer"
make hello_world -j$(nproc)
file ./examples/cpp/hello_world/hello_world
nm -C ./examples/cpp/hello_world/hello_world | grep "DomainParticipantFactory::create_participant" | head -5
编译选项说明:
| 选项 | 作用 | 为什么需要 |
|---|
-DCMAKE_BUILD_TYPE=Debug | 生成调试版本 | 保留调试符号,支持断点 |
-g | 生成调试信息 | gdb需要此信息定位源码 |
-O0 | 关闭编译优化 | 防止代码行错位,单步执行准确 |
-fno-omit-frame-pointer | 保留帧指针 | 方便栈回溯分析 |
2.1.4.2 启动gdb调试Discovery流程
gdb ./examples/cpp/hello_world/hello_world
(gdb) break eprosima::fastdds::dds::DomainParticipantFactory::create_participant
gdb> break eprosima::fastrtps::rtps::PDP::announce_participant_state
gdb> break eprosima::fastrtps::rtps::PDPListener::on_new_cache_change_added
gdb> break eprosima::fastrtps::rtps::EDP::pairing_writer_reader
gdb> break eprosima::fastdds::dds::DataWriterListener::on_publication_matched
gdb> run
gdb> bt
gdb> info locals
gdb> p participant_qos
gdb> step
gdb> continue
gdb> p participant_data
gdb> p participant_data.m_domainId
gdb> continue
gdb> p writer_guid
gdb> p reader_guid
gdb> p topic_name
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:条件断点(只在特定条件暂停)
gdb> break eprosima::fastdds::dds::DomainParticipantFactory::create_participant if domain_id == 0
gdb> break eprosima::fastrtps::rtps::EDP::pairing_writer_reader if strcmp(topic_name, "/vehicle/speed") == 0
技巧2:多线程调试
gdb> info threads
gdb> thread 2
gdb> set scheduler-locking on
技巧3:保存和加载调试会话
gdb> save breakpoints discovery.gdb
gdb> source discovery.gdb
2.1.4.5 其他场景调试参考
其他场景的调试步骤与Discovery类似,参考以下模板:
各场景关键断点快速参考:
| 场景 | 启动命令 | 关键断点 |
|---|
| 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 对二次开发的启示
- 自定义发现协议:继承
PDP类,实现自定义发现机制(如通过etcd/consul)
- 发现过滤器:在
EDP::pairing_writer_reader中加入自定义匹配逻辑
- 发现性能优化:减少发现消息频率,或使用发现服务器模式
场景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<br/>Segment
participant Port as SHM Port<br/>(/dev/shm/fastrtps_*)
participant SHM_R as SHM Reader<br/>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如何分配、写入、通知、读取
gdb ./examples/cpp/hello_world/hello_world
gdb> break eprosima::fastdds::rtps::SharedMemTransport::init
gdb> break eprosima::fastdds::rtps::SharedMemTransport::send
gdb> break eprosima::fastdds::rtps::SharedMemTransport::receive
gdb> break eprosima::fastdds::rtps::SharedMemManager::alloc_buffer
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 | 确认端口命名规则 |
系统级观察:
watch -n 0.5 'ls -la /dev/shm/ | grep fastrtps'
df -h /dev/shm
lsof -p $(pgrep hello_world) | grep /dev/shm
2.2.5 strace观察:SHM vs UDP差异
strace -f -e trace=openat,mmap,munmap,close,ftruncate \
./examples/cpp/hello_world/hello_world 2>&1 | grep -E "shm|/dev/shm"
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
2.2.6 对二次开发的启示
- 自定义SHM策略:继承
SharedMemTransport,实现环形缓冲区替代动态分配
- 大消息优化:调整
max_msg_size和segment_size匹配业务需求
- 跨机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、重传机制
gdb ./examples/cpp/hello_world/hello_world
gdb> break eprosima::fastrtps::rtps::StatefulWriter::send_heartbeat
gdb> break eprosima::fastrtps::rtps::StatefulWriter::process_acknack
gdb> break eprosima::fastrtps::rtps::StatefulWriter::resend_data
gdb> break eprosima::fastdds::dds::DataWriter::wait_for_acknowledgments
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协议交互
sudo tcpdump -i lo -w reliable.pcap portrange 7410-7415
rtps.sm.id == 0x07
rtps.sm.id == 0x06
rtps.sm.id == 0x08
rtps.sm.id == 0x15 && rtps.data
2.3.6 对二次开发的启示
- 自定义重传策略:修改
resend_data实现自适应重传(如根据RTT调整)
- 批量ACK优化:修改ACK发送策略,减少小数据包数量
- 可靠性分级:实现部分可靠(如只保证关键消息)
场景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调试:分片传输流程
调试目标:观察分片如何生成、发送、缓存、重组
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 对二次开发的启示
- 动态分片大小:根据网络MTU动态调整分片大小
- 分片优先级:关键分片优先发送
- 并行传输:多路径并行传输不同分片
场景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<br/>max_samples| History
History -->|Reliability<br/>History depth| Send
Send -->|LatencyBudget| Receive
Receive -->|Deadline<br/>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如何在各阶段生效
gdb ./examples/cpp/hello_world/hello_world
gdb> break eprosima::fastrtps::rtps::WriterHistory::add_change
gdb> break eprosima::fastrtps::rtps::WriterHistory::remove_min_change
gdb> break eprosima::fastrtps::rtps::WriterPeriodicHeartbeat::deadline_missed
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 对二次开发的启示
- 动态QoS:运行时调整QoS策略
- QoS监控:统计QoS违规事件
- 自适应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如何实现完全零拷贝
gdb ./examples/cpp/hello_world/hello_world
gdb> break eprosima::fastrtps::rtps::WriterPool::create_new_payload
gdb> break eprosima::fastrtps::rtps::DataSharingPayloadPool::get_payload
gdb> break eprosima::fastrtps::rtps::DataSharingNotification::notify
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 对二次开发的启示
- 自定义内存池:继承
DataSharingPayloadPool,实现特定对齐要求
- 通知机制优化:使用更高效的通知方式(如eventfd替代pipe)
- 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流程
调试目标:观察流量控制如何限制发送速率
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 对二次开发的启示
- 自定义调度策略:实现加权公平队列(WFQ)
- 动态限速:根据网络状况动态调整令牌生成速率
- 拥塞感知:结合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模式调试:
gdb> break eprosima::fastrtps::rtps::StatelessWriter::unsent_change_added_to_history
Stateful模式调试:
gdb> break eprosima::fastrtps::rtps::StatefulWriter::send_to_fixed_locators
gdb> break eprosima::fastrtps::rtps::ReaderProxy::acked_changes_set
对比观察:
| 观察点 | Stateless | Stateful |
|---|
| Reader状态 | 无 | 有(ReaderProxy) |
| 重传支持 | 不支持 | 支持(基于ACK/NACK) |
| 内存占用 | 低 | 高 |
| 适用场景 | 广播、视频流 | 可靠传输、控制指令 |
2.8.5 对二次开发的启示
- 混合模式:根据Topic选择Stateless或Stateful
- 动态切换:运行时根据网络状况切换模式
- 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检测
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 对二次开发的启示
- 自定义检测策略:实现多层级活性检测(应用层 + 传输层)
- 故障预测:基于活性历史数据预测节点故障
- 自动恢复:检测到故障后自动重启或切换
场景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流程
gdb> break eprosima::fastrtps::rtps::SQLite3PersistenceService::add_persistent_sample
gdb> break eprosima::fastrtps::rtps::SQLite3PersistenceService::load_persistent_samples
gdb> run
2.10.5 对二次开发的启示
- 自定义存储后端:实现Redis/MySQL持久化
- 数据压缩:持久化前压缩,减少存储空间
- 数据清理策略:实现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 | 端到端数据流 |
四、推荐材料学习
六、数据源标注
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_state和EDP::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调试、二次开发启示。