valgrind memcheck 默认不跟踪子进程,fork后子进程完全脱离监控;必须显式添加--trace-children=yes才能检测子进程的内存错误,且每个进程独立做泄漏检查,fork+exec场景因执行新二进制而无法被注入检测。

Valgrind Memcheck 默认不跟踪子进程
默认情况下,valgrind --tool=memcheck 只监控主进程(fork 之前的进程),fork() 后的子进程**完全脱离 Valgrind 监控**——哪怕子进程调用了 malloc、访问了越界内存或发生 use-after-free,Memcheck 也不会报错。这是最常被忽略的前提,也是“为什么加了 valgrind 却没看到子进程内存错误”的根本原因。
--trace-children=yes 必须显式开启
要让 Memcheck 跟进 fork 出的子进程,必须加这个参数:
valgrind --tool=memcheck --trace-children=yes ./your_program
注意几点:
-
--trace-children=yes是核心开关,缺它等于没开子进程检测 - 它对所有子进程生效(包括
fork、vfork、clone创建的,也包括system()或popen()启动的) - 子进程的输出会混在主进程日志里,用
==PID==前缀区分,比如==12345==是父进程,==12346==是第一个子进程 - 如果子进程很快退出,可能来不及打印完整报告;可配合
--log-file=memcheck.%p按 PID 分文件记录
多进程下 --leak-check=full 的行为边界
即使开了 --trace-children=yes,--leak-check=full 仍只在**每个进程各自退出时**做一次泄漏检查,不会跨进程汇总。这意味着:
- 父进程 exit 前没 free 的内存,算父进程 leak
- 子进程 exit 前没 free 的内存,算子进程 leak(需看对应 PID 的报告段)
- 父子进程间通过
shared memory或mmap(MAP_SHARED)共享的堆内存,Memcheck 不认为是“泄漏”,因为它无法判断谁该负责释放 - 若子进程
exec了新程序(如fork + execv("/bin/ls", ...)),Valgrind 会自动停止监控该进程(因为执行的是全新二进制),此时--trace-children对它无效
fork 后立即 exec 的场景实际不可检
很多服务模型是 fork 后立刻 exec(如守护进程、CGI、shell 命令调用)。这种情况下:
- Valgrind 无法注入到
exec后的新程序中——它不是你编译的,也没有调试符号 - 即便强行用
valgrind --trace-children=yes --tool=memcheck /bin/ls,那只是单独跑/bin/ls,和原 fork 流程无关 - 真正要查这类子进程问题,得把目标逻辑改写成不
exec的形式(例如用fork + 手动实现功能),或改用LD_PRELOAD注入检测逻辑
真实生产环境里,fork+exec 是常态,而 Memcheck 对它的覆盖能力天然受限——别指望靠一个命令扫尽所有分支的内存问题。











