为何调用system()时会在fork()系统调用内部占用100%CPU?
根因分析
该问题是Linux 4.15版本内核的已知bug,已在后续5.x版本内核中修复,与测试场景下的插桩特性叠加触发死锁:
- 4.15内核
fork()系统调用的copy_process逻辑存在缺陷,和gcov覆盖率插桩、AddressSanitizer运行时的全局锁机制产生冲突:gcov与ASAN都会持有全局锁来统计运行数据,fork()触发的进程地址空间复制流程在特定路径下会陷入锁循环,占用单核心100%CPU资源。 std::terminate()调用会放大冲突概率:异常终止流程会触发ASAN、gcov的收尾逻辑,此时持有的全局锁可能未正常释放,后续调用system()触发fork()时会100%触发死锁。
现象对应说明
观测到的所有特征均匹配该bug的表现:
- 仅带gcov+ASAN插桩的二进制可复现:无插桩的二进制不存在额外的全局锁,不会触发冲突
- Ctrl+C无法终止、
kill <pid>可正常终止:死锁发生在内核态系统调用上下文,SIGINT信号默认会被阻塞直到系统调用返回,而SIGTERM可直接中断该死锁流程 - Ubuntu 22.04(5.15内核)下无问题:该bug在4.19及之后的内核版本中已完成修复,
copy_process路径的锁逻辑做了调整,避免和用户态插桩锁产生死锁。
规避方案
如果需要保留在Ubuntu 18.04(4.15内核)下运行测试的需求,可采用以下任意方案规避:
- 替换
system()调用为posix_spawn():该接口不会触发全进程地址空间复制逻辑,不会触发内核bug,执行性能也优于system() - 针对触发
std::terminate的测试单独关闭插桩:编译该部分测试用例时去掉-fprofile-arcs -ftest-coverage及-fsanitize相关参数即可 - 升级Ubuntu 18.04的HWE内核:执行
apt install linux-generic-hwe-18.04即可安装5.x版本的官方维护内核,从底层修复该问题。
内容的提问来源于stack exchange,提问作者Alexis Wilke
相关产品推荐
相关产品推荐

