rcutils 源码详细分析

rcutils 源码详细分析

工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros2/rcutils
版本:5.1.8(Humble),单包仓库,构建类型 ament_cmake,语言 C11,许可证 Apache 2.0

rcutils 是 ROS 2 最底层 C 工具库:日志、线程局部错误链、可注入分配器、时间、文件系统、动态库加载、字符串/容器数据结构、环境变量等。被 rclrmwrosidl_runtime_crcpputils 及几乎所有 ROS 2 C/C++ 组件依赖。不含 ROS 语义(无 topic/node/DDS),却是整个栈的公共基础设施。


1. 总体认识

1.1 核心职责

类别 模块 说明
分配器 allocator.h 统一 malloc/free/realloc,支持测试注入
错误处理 error_handling.h 线程局部错误链,RCUTILS_SET_ERROR_MSG
日志 logging.h + logging_macros.h 分级日志、可替换 output handler
时间 time.h system/steady 时钟,纳秒时间戳
文件系统 filesystem.h exists/is_file/mkdir/cwd 等
动态库 shared_library.h dlopen/LoadLibrary 封装
环境变量 env.h get/set env
字符串 split/repl_str/format_string/snprintf 分割、替换、格式化
容器 types/* char_array、string_map、hash_map、array_list 等
进程 process.h PID、可执行文件名
CLI cmdline_parser.h 简单命令行选项解析
测试 testing/fault_injection.h 故障注入框架

1.2 在 ROS 2 栈中的位置

应用 / 工具中间层基础层rclcpprclpy C 扩展rosbag2 / rviz2rclrmw / rmw_implementationrosidl_runtime_crcutilsrcpputilslibc / OS API
上层 典型 rcutils 用法
rcl 日志、RCUTILS_SET_ERROR_MSG、allocator、time
rmw 错误链、logging、filesystem
rosidl_runtime_c allocator、字符串
rcpputils shared_library、env、filesystem 的 C 底层
rclcpp 经 rcl/rcl_logging 间接使用;RCLCPP_* 宏最终到 rcutils logging

与 rcpputils 的分工:C ABI 与跨语言边界能力在 rcutils;C++ 便利封装在 rcpputils。读 rcl 源码前宜先熟悉 loggingerror_handling


2. 仓库结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
rcutils/
├── include/rcutils/ # 35 个公开头文件
│ ├── logging.h # 542 行
│ ├── error_handling.h # 321 行
│ ├── filesystem.h # 300 行
│ ├── types/ # 容器与返回码
│ │ ├── rcutils_ret.h
│ │ ├── char_array.h
│ │ ├── string_map.h / hash_map.h
│ │ ├── string_array.h / array_list.h
│ │ └── uint8_array.h
│ ├── stdatomic_helper/ # C11 原子跨平台
│ └── testing/fault_injection.h
├── include/rcutils/logging_macros.h # ★ 构建时由 empy 生成
├── src/ # 27 个 .c(~10.6K 行)
│ ├── logging.c # 1000 行,最大单文件
│ ├── filesystem.c # 543 行
│ ├── hash_map.c / string_map.c
│ ├── error_handling.c # 264 行
│ ├── shared_library.c # 351 行
│ ├── time.c + time_unix.c / time_win32.c
│ └── testing/fault_injection.c
├── resource/logging_macros.h.em # 日志宏模板
├── rcutils/logging.py # empy 生成脚本输入
├── test/ # 大量 gtest + launch 测试
└── CMakeLists.txt

2.1 源码规模

指标 数量
公开头文件 ~35
C 源文件 27
src/*.c 总行数 ~10,635
最大实现文件 logging.c(1000 行)

3. 依赖关系

1
2
rcutils
└── libatomic(可选,非 Windows 下检测 __atomic_load_8)

rcl、rmw、rosidl 依赖——刻意保持最底层。构建工具:ament_cmake_rospython3-empy(生成 logging_macros.h)。


4. 核心设计:可注入分配器

几乎所有需动态内存的 API 接受 rcutils_allocator_t

1
2
3
4
5
6
7
8
typedef struct rcutils_allocator_s
{
void * (*allocate)(size_t size, void * state);
void (* deallocate)(void * pointer, void * state);
void * (*reallocate)(void * pointer, size_t size, void * state);
void * (*zero_allocate)(size_t number_of_elements, size_t size_of_element, void * state);
void * state;
} rcutils_allocator_t;
API 作用
rcutils_get_default_allocator() malloc/free/realloc/calloc
rcutils_get_zero_initialized_allocator() 全零函数指针(无效)
rcutils_allocator_is_valid() 校验四函数非 NULL
rcutils_reallocf() realloc 失败时 free 旧指针

设计动机:单元测试可注入 fault-injection / 计数 allocator,检测泄漏与分配失败路径。


5. 错误处理(error_handling)

2017 年从 rmw/error_handling.c 迁移至 rcutils,供 rcl/rmw 统一使用。

5.1 线程局部错误链

1
2
3
4
RCUTILS_THREAD_LOCAL bool gtls_rcutils_thread_local_initialized = false;
RCUTILS_THREAD_LOCAL rcutils_error_state_t gtls_rcutils_error_state;
RCUTILS_THREAD_LOCAL rcutils_error_string_t gtls_rcutils_error_string;
RCUTILS_THREAD_LOCAL bool gtls_rcutils_error_is_set = false;
API / 宏 作用
RCUTILS_SET_ERROR_MSG(...) 设置错误消息 + 文件/行号
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING(...) 格式化错误消息
rcutils_get_error_state() 获取结构化错误(message、file、line_number)
rcutils_get_error_string() 格式化字符串(含链式 “at file:line”)
rcutils_error_is_set() 是否有未处理错误
rcutils_reset_error() 必须在处理后调用,避免泄漏

5.2 典型用法(rcl/rmw 模式)

1
2
3
4
5
6
rcl_ret_t ret = rcl_something(...);
if (ret != RCL_RET_OK) {
const char * msg = rcutils_get_error_string().str;
// 记录或向上传递
rcutils_reset_error();
}

错误消息有长度上限(RCUTILS_ERROR_MESSAGE_MAX_LENGTH 1024),链式错误会截断。

5.3 与返回值码的关系

  • rcutils_ret_t:函数直接返回的操作结果(见 §6)
  • 错误链:函数返回 RCUTILS_RET_*RCL_RET_* 时,额外通过 TLS 携带人类可读说明

6. 返回码(rcutils_ret.h)

1
2
3
4
5
6
7
8
9
10
11
typedef int rcutils_ret_t;

#define RCUTILS_RET_OK 0
#define RCUTILS_RET_WARN 1
#define RCUTILS_RET_ERROR 2
#define RCUTILS_RET_BAD_ALLOC 10
#define RCUTILS_RET_INVALID_ARGUMENT 11
#define RCUTILS_RET_NOT_ENOUGH_SPACE 12
#define RCUTILS_RET_NOT_INITIALIZED 13
#define RCUTILS_RET_NOT_FOUND 14
// string_map / logging / hash_map 专用码 ...

rcl/rmw 定义各自的 rcl_ret_t / rmw_ret_t,但错误字符串机制统一走 rcutils。


7. 日志系统(logging)

最大模块(logging.c 1000 行 + 生成宏)。

7.1 初始化与 output handler

1
2
3
4
bool g_rcutils_logging_initialized = false;
static rcutils_allocator_t g_rcutils_logging_allocator;
rcutils_logging_output_handler_t g_rcutils_logging_output_handler = NULL;
static rcutils_string_map_t g_rcutils_logging_severities_map;
函数 作用
rcutils_logging_initialize_with_allocator() 初始化 severity map、解析环境变量
rcutils_logging_shutdown() 释放资源
rcutils_logging_set_output_handler() 替换输出(默认 rcutils_logging_console_output_handler
rcutils_logging_set_logger_level() 按 logger 名设置阈值
rcutils_logging_logger_is_enabled_for() 过滤低 severity

7.2 rcutils_log 流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void rcutils_log(
const rcutils_log_location_t * location,
int severity, const char * name, const char * format, ...)
{
if (!rcutils_logging_logger_is_enabled_for(name, severity)) {
return;
}
rcutils_system_time_now(&now);
output_handler = g_rcutils_logging_output_handler;
if (output_handler != NULL) {
va_start(args, format);
(*output_handler)(location, severity, name, now, format, &args);
va_end(args);
}
}

7.3 日志宏(构建时生成)

resource/logging_macros.h.em + rcutils/logging.pyinclude/rcutils/logging_macros.h

宏族 示例
基础 RCUTILS_LOG_DEBUG/INFO/WARN/ERROR/FATAL
命名 RCUTILS_LOG_INFO_NAMED(name, ...)
条件 RCUTILS_LOG_DEBUG_ONCERCUTILS_LOG_WARN_SKIPFIRST
节流 RCUTILS_LOG_ERROR_THROTTLE

编译期可通过 RCUTILS_LOG_MIN_SEVERITY 剔除低级别日志。

7.4 环境变量

变量 作用
RCUTILS_CONSOLE_OUTPUT_FORMAT 输出格式 token({severity}{name}{message}{time} 等)
RCUTILS_COLORIZED_OUTPUT 0/1 强制禁用/启用颜色
RCUTILS_LOGGING_USE_STDOUT 输出到 stdout 而非 stderr
RCUTILS_LOGGING_BUFFERED_STREAM 缓冲策略
RCUTILS_CONSOLE_STDOUT_LINE_BUFFERED 行缓冲

默认格式:[{severity}] [{time}] [{name}]: {message}

7.5 与 RCLCPP 的关系

1
2
3
4
RCLCPP_INFO(logger, "msg")
→ rcl/rcl_logging
→ rcutils_log() / RCUTILS_LOG_*
→ g_rcutils_logging_output_handler (console 或 spdlog 后端)

8. 时间(time.h)

类型 说明
rcutils_time_point_value_t int64_t 纳秒时间戳
rcutils_duration_value_t int64_t 纳秒时长
API 对应 C++
rcutils_system_time_now() std::chrono::system_clock
rcutils_steady_time_now() std::chrono::steady_clock

平台实现:

  • Unix:time_unix.c
  • Windows:time_win32.c
  • 公共逻辑:time.c(格式化、sleep 等)

宏:RCUTILS_S_TO_NSRCUTILS_NS_TO_S 等。


9. 文件系统(filesystem.h / filesystem.c)

C 风格路径 API(非 C++ fs::path):

函数 作用
rcutils_exists / rcutils_is_file / rcutils_is_directory 路径类型检测
rcutils_is_readable / rcutils_is_writable 权限
rcutils_get_cwd 当前工作目录
rcutils_expand_user ~ 展开
rcutils_mkdir 创建目录
rcutils_calculate_directory_size 目录大小
rcutils_get_file_size 文件大小

rcpputils 的 fs::path 在此基础上提供 C++ 封装。


10. 动态库(shared_library.h / shared_library.c)

1
2
3
4
5
6
typedef struct rcutils_shared_library_s
{
void * lib_pointer;
char * library_path;
rcutils_allocator_t allocator;
} rcutils_shared_library_t;
API 作用
rcutils_load_shared_library dlopen / LoadLibrary
rcutils_unload_shared_library 卸载
rcutils_get_symbol / rcutils_has_symbol dlsym
rcutils_get_platform_library_name libfoo.so / foo.dll

下游:rmw 加载 DDS 实现、rclcpp typesupport 动态加载、pluginlib/class_loader。

CMake 链接 ${CMAKE_DL_LIBS}


11. 环境变量(env.h / env.c)

API 说明
rcutils_get_env(name, &value) 读取;value 指向内部存储,下次调用失效
rcutils_set_env(name, value) 设置/取消;Windows 空串行为与 Unix 不同
rcutils_get_home_dir() 用户主目录

rcpputils get_env_var() 在此基础上抛 C++ 异常。


12. 字符串与文本工具

头文件 / 源文件 API 说明
split.h / split.c rcutils_splitrcutils_split_last 按 delimiter 分割,需 allocator
repl_str.h / repl_str.c rcutils_repl_str 子串替换
format_string.h rcutils_format_string snprintf + allocate(默认限 2048)
snprintf.h rcutils_snprintf 安全 snprintf 封装
strdup.h rcutils_strdup 带 allocator 的 strdup
strerror.h rcutils_strerror 线程安全 strerror
strcasecmp.h rcutils_strcasecmp 大小写无关比较
find.h / find.c rcutils_findrcutils_find_last 字符搜索
isalnum_no_locale.h rcutils_isalnum 不受 locale 影响的 isalnum

13. 容器数据结构(types/)

均支持 allocator 注入,部分使用 PIMPL(impl 指针)。

类型 文件 用途
rcutils_char_array_t char_array.c 可增长 char 缓冲区(日志格式化)
rcutils_string_map_t string_map.c 字符串键值 map(logger severity map)
rcutils_hash_map_t hash_map.c 通用 hash map
rcutils_array_list_t array_list.c 动态数组
rcutils_string_array_t string_array.c 字符串数组
rcutils_uint8_array_t uint8_array.c 字节数组

string_map 在 logging 中存储 logger 名 → severity 映射。


14. 其他模块

模块 说明
cmdline_parser rcutils_cli_option_existrcutils_cli_get_option
process rcutils_get_pidrcutils_get_executable_name
qsort rcutils_qsort — 可注入 comparator 的排序
macros.h RCUTILS_WARN_UNUSEDRCUTILS_THREAD_LOCAL、join 宏
visibility_control.h RCUTILS_PUBLIC DLL 导出
stdatomic_helper GCC/Win32 原子操作 shim
get_env.h 已废弃,转发 env.h

15. 测试与 fault_injection

15.1 fault_injection

RCUTILS_ENABLE_FAULT_INJECTION 编译开关(测试构建默认开启):

  • RCUTILS_FAULT_INJECTION_TEST({ ... }) — 在分配/错误路径注入失败
  • 用于覆盖 RCUTILS_RET_BAD_ALLOC 等分支

15.2 测试覆盖

类别 示例
单元测试 test_logging、test_error_handling、test_filesystem
生成宏 lint cppcheck/cpplint/uncrustify 对 logging_macros.h
launch 测试 长消息、输出格式 Python 验证
benchmark benchmark_logging、benchmark_error_handling
shared_library RUNPATH / LD_LIBRARY_PATH / preload 三种加载场景

Quality Level 1:见 QUALITY_DECLARATION.md


16. 构建要点

  1. logging_macros.h 生成:CMake add_custom_command 调用 Python empy 展开 .em 模板
  2. 平台 time 源文件WIN32time_win32.c,否则 time_unix.c
  3. GNU 源:Linux glibc 下 -D_GNU_SOURCE
  4. libatomic:检测并链接 -latomic(某些架构 64 位原子)
  5. Python 包ament_python_install_package(rcutils) 安装 logging.py 供构建使用

17. 日志数据路径

output_handlerseverity 过滤rcutils_logRCUTILS_LOG_* 宏rcl / rmw / 用户output_handlerseverity 过滤rcutils_logRCUTILS_LOG_* 宏rcl / rmw / 用户alt[通过]RCUTILS_LOG_INFO(...)rcutils_log(location, severity, name, fmt, ...)logger_is_enabled_for?rcutils_system_time_nowhandler(location, severity, name, time, fmt, va_list)格式化 token → stderr/stdout

18. 与 rcpputils 对照

能力 rcutils rcpputils
语言 C C++
日志 完整实现 无(用 rcutils)
错误链 TLS error state 无(捕获 rcutils 错误转 exception)
文件系统 C bool 返回值 API fs::path C++ API
动态库 rcutils_shared_library_t SharedLibrary
环境变量 rcutils_get_env get_env_var() → string
split/join rcutils_split + allocator header-only 模板

详见 rcpputils 源码详细分析


19. 下游依赖概览

依赖 rcutils 的典型场景
rcl 全程:init、logging、error、allocator
rmw 实现加载、错误、日志
rosidl_runtime_c 序列化 buffer、字符串
rcpputils shared_library、env、filesystem 底层
rcl_logging_spdlog 替换 rcutils output handler
rclpy C 扩展经 rcl 间接使用

20. 推荐阅读顺序

  1. allocator.h + types/rcutils_ret.h — 理解注入模式与返回码
  2. error_handling.h + error_handling.c — TLS 错误链(读 rcl 必备)
  3. logging.h + logging.c(rcutils_log、console handler) — 日志管线
  4. resource/logging_macros.h.em — 宏如何展开
  5. time.h + time_unix.c — 时钟抽象
  6. filesystem.h + shared_library.c — OS 交互
  7. types/char_array.h + types/string_map.h — 日志内部数据结构
  8. testing/fault_injection.h — 测试如何覆盖失败路径
  9. 对照 rcl 源码分析 中 error/logging 调用点

21. 小结

rcutils 是 ROS 2 C 栈的 零语义公共库,核心模式为:

  • allocator 注入 — 所有动态结构可测试、可定制
  • 线程局部错误链SET_ERROR_MSG + get_error_string + reset_error
  • 可替换 logging handler — 从 console 到 spdlog 的扩展点
  • 平台 shim — time、filesystem、shared_library、stdatomic 统一 Unix/Windows

不含 QoS/YAML/ROS 图等高层概念;这些在 rclrcl_yaml_param_parserrmw 中实现。调试 ROS 2 问题时,沿 上层 ret 码 → rcutils_get_error_string() 追根因是基本技能。

文章互动

阅读 --

留言

0 条留言

正在加载留言…