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

0 阅读15分钟

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 源码定位
组件源码路径关键类
PDPsrc/cpp/rtps/builtin/discovery/participant/PDP.cpp, PDPListener.cpp
EDPsrc/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模式)
# 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流程
# 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设置断点在函数入口暂停
runr运行程序启动调试
continuec继续运行跳到下一个断点
steps单步进入进入函数内部
nextn单步跳过不进入函数内部
finishfin运行到函数返回快速跳出当前函数
backtracebt查看调用栈了解代码调用路径
info localsi lo查看局部变量检查当前上下文
print <var>p打印变量值查看变量内容
display <var>disp持续显示变量每次暂停自动显示
watch <var>wa监视变量变化变量改变时暂停
listl显示源码查看当前代码位置
info breakpointsi b查看断点列表管理所有断点
delete <num>d删除断点清除不需要的断点
quitq退出gdb结束调试
2.1.4.4 高级调试技巧

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

# 只在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:多线程调试

# 查看所有线程
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类似,参考以下模板:

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

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

场景启动命令关键断点
SHM零拷贝gdb ./hello_worldSharedMemTransport::send
DataSharinggdb ./hello_worldWriterPool::create_new_payload
Reliable传输gdb ./hello_worldStatefulWriter::send_heartbeat
分片传输gdb ./hello_worldRTPSMessageGroup::add_data_frag
FlowControlgdb ./FlowControlExampleFlowController::schedule
Stateless/Statefulgdb ./hello_worldStatelessWriter::unsent_change_added
Livelinessgdb ./hello_worldLivelinessManager::assert_liveliness
Persistencegdb ./hello_worldSQLite3PersistenceService::add_persistent_sample
2.1.4.6 Discovery机制调试(详细)

观察要点

断点位置观察变量商用价值
create_participantdomain_id, participant_qos确认Domain隔离是否正确
announce_participant_stateparticipant_guid, metatraffic_locators确认发现消息发送地址
on_new_cache_change_addedchange->writerGUID确认远程Participant身份
pairing_writer_readerwriter_guid, reader_guid, topic_name确认Topic匹配逻辑
on_publication_matchedinfo.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<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 Transportsrc/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
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

观察要点

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

系统级观察

# 终端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差异
# 观察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 Writersrc/cpp/rtps/writer/StatefulWriter.cpp
Heartbeatsrc/cpp/rtps/writer/StatefulWriter::send_heartbeat
ACK处理src/cpp/rtps/writer/StatefulWriter::process_acknack
Reliable Readersrc/cpp/rtps/reader/StatefulReader.cpp
2.3.4 gdb调试:Reliable传输流程

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

# 启动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_heartbeatheartbeat_count, first_sn, last_sn确认心跳频率和范围
process_acknackack_count, nack_count, nack_bitmap确认丢包检测
resend_datasequence_number, reader_guid确认重传逻辑
wait_for_acknowledgmentsacknowledgment_count, max_blocking_time确认阻塞等待机制
2.3.5 Wireshark分析:Reliable协议交互
# 抓包
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调试:分片传输流程

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

# 启动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_datadata_size, max_size, fragment_size确认分片触发条件
add_data_fragfragment_starting_num, fragments_in_submessage确认分片编号
processfragmented_change, received_fragments确认分片缓存状态
is_completemissing_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<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
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_changem_changes.size(), m_resource_limits确认资源限制生效
remove_min_changehistory_qos.kind, history_qos.depth确认History策略
deadline_misseddeadline_qos.period, last_change_time确认Deadline检测
callbackliveliness_qos.kind, lease_duration确认Liveliness状态
2.5.5 对二次开发的启示
  1. 动态QoS:运行时调整QoS策略
  2. QoS监控:统计QoS违规事件
  3. 自适应QoS:根据网络状况自动调整

三、调试工具速查表

3.1 gdb断点速查

场景关键断点观察目标
DiscoveryPDP::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,recvfromUDP发现消息
SHM传输strace -e openat,mmap,ftruncate共享内存文件操作
性能分析strace -T -e sendto,recvfrom系统调用耗时
线程分析strace -e clone,pthread_create线程创建情况

3.3 Wireshark过滤条件

场景过滤条件观察目标
Discoveryrtps.sm.id == 0x15 && rtps.participantSPDP发现消息
Endpoint匹配rtps.sm.id == 0x15 && rtps.endpointSEDP端点宣告
Heartbeatrtps.sm.id == 0x07可靠性心跳
ACK/NACKrtps.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 源码定位
组件源码路径关键类/函数
DataSharingsrc/cpp/rtps/DataSharing/DataSharingPayloadPool.cpp
WriterPoolsrc/cpp/rtps/DataSharing/WriterPool.hpp
ReaderPoolsrc/cpp/rtps/DataSharing/ReaderPool.hpp
通知机制src/cpp/rtps/DataSharing/DataSharingNotification.cpp
2.6.4 gdb调试:DataSharing流程

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

# 启动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_payloadsegment_size, payload_size确认共享内存池大小
get_payloadpayload->data, payload->payload_owner确认零拷贝分配
notifynotification_fd确认通知机制(eventfd/pipe)
on_data_availablepool->get_payload_count确认Reader获取数据
2.6.5 与SHM的区别
特性SHM TransportDataSharing
拷贝次数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 源码定位
组件源码路径关键类/函数
FlowControllersrc/cpp/rtps/flowcontrol/FlowControllerImpl.hpp
优先级队列src/cpp/rtps/flowcontrol/FlowControllerFactory.cpp
令牌桶src/cpp/rtps/writer/ThroughputController.cpp
2.7.4 gdb调试:FlowControl流程

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

# 启动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

观察要点

断点位置观察变量商用价值
schedulepriority, flow_controller_name确认优先级调度
processtokens, data_size确认令牌桶限流
sendqueue_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 源码定位
组件源码路径关键类
StatelessWritersrc/cpp/rtps/writer/StatelessWriter.cpp
StatefulWritersrc/cpp/rtps/writer/StatefulWriter.cpp
ReaderProxysrc/cpp/rtps/writer/ReaderProxy.cpp
WriterProxysrc/cpp/rtps/reader/WriterProxy.cpp
2.8.4 gdb调试:两种模式对比

Stateless模式调试:

# 设置断点:Stateless发送
gdb> break eprosima::fastrtps::rtps::StatelessWriter::unsent_change_added_to_history

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

Stateful模式调试:

# 设置断点:Stateful发送
gdb> break eprosima::fastrtps::rtps::StatefulWriter::send_to_fixed_locators

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

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

对比观察:

观察点StatelessStateful
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 源码定位
组件源码路径关键类/函数
LivelinessManagersrc/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_livelinessguid, lease_duration确认活性声明
callbackexpired_writers确认超时检测
on_liveliness_changedstatus.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 源码定位
组件源码路径关键类/函数
PersistenceServicesrc/cpp/rtps/persistence/SQLite3PersistenceService.cpp
持久化Writersrc/cpp/rtps/writer/PersistentWriter.cpp
持久化Readersrc/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 对二次开发的启示
  1. 自定义存储后端:实现Redis/MySQL持久化
  2. 数据压缩:持久化前压缩,减少存储空间
  3. 数据清理策略:实现TTL自动过期

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

3.1 gdb断点速查(完整版)

场景关键断点观察目标
DiscoveryPDP::announce_participant_state
EDP::pairing_writer_reader
发现流程、匹配逻辑
SHM零拷贝SharedMemTransport::send
SharedMemTransport::receive
零拷贝路径、缓冲区管理
DataSharingWriterPool::create_new_payload
DataSharingNotification::notify
完全零拷贝、通知机制
Reliable传输StatefulWriter::send_heartbeat
StatefulWriter::process_acknack
心跳、ACK、重传
分片传输RTPSMessageGroup::add_data_frag
FragmentedChangePitStop::process
分片生成、重组
FlowControlFlowController::schedule
ThroughputController::process
流量控制、令牌桶
Stateless/StatefulStatelessWriter::unsent_change_added
StatefulWriter::send_to_fixed_locators
两种模式差异
LivelinessLivelinessManager::assert_liveliness
LivelinessManager::callback
活性检测、超时
PersistenceSQLite3PersistenceService::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 Discoveryfast-dds.docs.eprosima.com/en/latest/f…
官方文档Fast-DDS SHMfast-dds.docs.eprosima.com/en/latest/f…
论文RTPS Spec 8.5OMG官方文档 - Discovery
论文RTPS Spec 8.6OMG官方文档 - Reliable Protocol
工具GDB手册sourceware.org/gdb/current…
工具Wireshark RTPSwiki.wireshark.org/RTPS

六、数据源标注

6.1 核心源码路径

模块路径关键文件
Discoverysrc/cpp/rtps/builtin/discovery/participant/PDP.cpp, PDPListener.cpp
Discoverysrc/cpp/rtps/builtin/discovery/endpoint/EDP.cpp, EDPSimple.cpp
SHM Transportsrc/cpp/rtps/transport/shared_mem/SharedMemTransport.cpp, SharedMemManager.cpp
Reliable Writersrc/cpp/rtps/writer/StatefulWriter.cpp
Reliable Readersrc/cpp/rtps/reader/StatefulReader.cpp
分片src/cpp/rtps/messages/RTPSMessageGroup.cpp
分片重组src/cpp/rtps/reader/FragmentedChangePitStop.cpp
Historysrc/cpp/rtps/history/WriterHistory.cpp, ReaderHistory.cpp
QoSinclude/fastdds/dds/core/policy/QosPolicies.hpp
Topicsrc/cpp/fastdds/topic/Topic.cpp, TopicImpl.cpp

6.2 示例代码

示例路径说明
hello_worldexamples/cpp/hello_world/基础发布订阅
discovery_serverexamples/cpp/discovery_server/发现服务器模式
delivery_mechanismsexamples/cpp/delivery_mechanisms/传输机制对比
flow_controlexamples/cpp/flow_control/流量控制QoS
custom_payload_poolexamples/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调试、二次开发启示。