rcutils 源码详细分析
工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros2/rcutils
版本:5.1.8(Humble),单包仓库,构建类型 ament_cmake,语言 C11,许可证 Apache 2.0。
rcutils 是 ROS 2 最底层 C 工具库:日志、线程局部错误链、可注入分配器、时间、文件系统、动态库加载、字符串/容器数据结构、环境变量等。被 rcl、rmw、rosidl_runtime_c、rcpputils 及几乎所有 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 栈中的位置
| 上层 |
典型 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 源码前宜先熟悉 logging 与 error_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_ros、python3-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.py → include/rcutils/logging_macros.h
| 宏族 |
示例 |
| 基础 |
RCUTILS_LOG_DEBUG/INFO/WARN/ERROR/FATAL |
| 命名 |
RCUTILS_LOG_INFO_NAMED(name, ...) |
| 条件 |
RCUTILS_LOG_DEBUG_ONCE、RCUTILS_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_NS、RCUTILS_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_split、rcutils_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_find、rcutils_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_exist、rcutils_cli_get_option |
| process |
rcutils_get_pid、rcutils_get_executable_name |
| qsort |
rcutils_qsort — 可注入 comparator 的排序 |
| macros.h |
RCUTILS_WARN_UNUSED、RCUTILS_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. 构建要点
- logging_macros.h 生成:CMake
add_custom_command 调用 Python empy 展开 .em 模板
- 平台 time 源文件:
WIN32 → time_win32.c,否则 time_unix.c
- GNU 源:Linux glibc 下
-D_GNU_SOURCE
- libatomic:检测并链接
-latomic(某些架构 64 位原子)
- Python 包:
ament_python_install_package(rcutils) 安装 logging.py 供构建使用
17. 日志数据路径
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. 推荐阅读顺序
allocator.h + types/rcutils_ret.h — 理解注入模式与返回码
error_handling.h + error_handling.c — TLS 错误链(读 rcl 必备)
logging.h + logging.c(rcutils_log、console handler) — 日志管线
resource/logging_macros.h.em — 宏如何展开
time.h + time_unix.c — 时钟抽象
filesystem.h + shared_library.c — OS 交互
types/char_array.h + types/string_map.h — 日志内部数据结构
testing/fault_injection.h — 测试如何覆盖失败路径
- 对照 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 图等高层概念;这些在 rcl、rcl_yaml_param_parser、rmw 中实现。调试 ROS 2 问题时,沿 上层 ret 码 → rcutils_get_error_string() 追根因是基本技能。
正在加载留言…