Boost.Process v2 在旧内核上报 `assign: Bad file descriptor`

Boost.Process v2 在旧内核上报 assign: Bad file descriptor

同一份二进制,在 Linux 6.18 上正常,在 Linux 3.10 上启动子进程必然失败。

现象

启动子进程时抛出异常:

复制代码
assign: Bad file descriptor [system:9 at /usr/local/include/boost/asio/detail/impl/reactive_descriptor_service.ipp:121 in function 'assign']
  • 环境:Boost 1.92.0、C++17、Linux 6.18(正常)/ Linux 3.10(异常)
  • 复现:旧内核上 100% 失败,与业务逻辑无关
  • system:9 即 EBADF

原因

Boost.Process v2 是否使用 pidfd_open,是编译期决定的:

cpp 复制代码
// boost/process/v2/detail/config.hpp
#if defined(SYS_pidfd_open)          // 只检查构建机的 kernel headers
#define BOOST_PROCESS_V2_PIDFD_OPEN 1
#endif

而调用是运行期无条件的:

cpp 复制代码
// boost/process/v2/detail/process_handle_fd.hpp:70
basic_process_handle_fd(executor_type executor, pid_type pid)
        : pid_(pid), descriptor_(executor, syscall(SYS_pidfd_open, pid, 0))
{
    if (descriptor_.native_handle() == -1)      // 永远来不及执行
        detail::throw_error(detail::get_last_error(), "wait(pid)");
}

pidfd_open(syscall 434)是 Linux 5.3 才引入的。在 3.10 上它返回 -1(ENOSYS),

而这个 -1 在成员初始化列表 里就被当作合法 fd 交给了

asio::posix::basic_stream_descriptor,最终 asio 的

reactive_descriptor_service::assign() 执行 epoll_ctl(epfd, ADD, -1) 失败,抛出 EBADF。

两个关键点:

  1. Boost 其实检查了返回值,但 if (native_handle() == -1) 写在构造函数体 里,
    而 syscall() 在成员初始化列表里,检查永远来不及生效。
  2. 所以这是典型的**「编译期探测 ≠ 运行期保证」**:构建机内核新、运行机内核旧时必然踩中。

解决办法

用 Boost 官方开关关掉 pidfd 路径,回退到可移植的 signal 版进程句柄:

cmake 复制代码
if(UNIX AND NOT APPLE)
    add_compile_definitions(BOOST_PROCESS_V2_DISABLE_PIDFD_OPEN)
endif()

放在顶层 CMakeLists.txt,让所有 target 都生效。

⚠️ 该宏会改变 basic_process_handle 的实际类型,必须保证同一二进制内所有

翻译单元一致,否则是 ODR 违规。因此要用目录级的 add_compile_definitions(),

不要只在单个 target 上用 target_compile_definitions()。

影响范围可控:fork/exec/stdio 重定向/process::id() 均不变,

只是 Boost 内部的等待实现由 pidfd 轮询改为 signal_set。

验证

编译一个最小程序,确认实际走了哪条路径:

cpp 复制代码
#if defined(BOOST_PROCESS_V2_PIDFD_OPEN)
    printf("pidfd path ENABLED\n");
#else
    printf("pidfd path DISABLED\n");
#endif
printf("handle: %s\n", typeid(boost::process::v2::process::handle_type).name());
编译方式 句柄类型 结果
默认 basic_process_handle_fd pidfd 路径
加宏 basic_process_handle_signal 子进程正常启动,stdout 与退出码均正确

经验

  • 「同一二进制换内核就挂」→ 优先怀疑 syscall 层面的新特性
  • 编译期探测内核能力,不能替代运行期检查
  • 在成员初始化列表里调用可能失败的函数时,返回值检查要放在能生效的位置
  • 优先用库提供的 *_DISABLE_* 官方开关,而不是打补丁
  • 会改变类型的编译宏,务必确认作用域覆盖全部翻译单元

附

复制代码
Boost          1.92.0 (BOOST_VERSION = 109200)
pidfd_open     syscall 434,Linux 5.3 引入
相关推荐
C^h2 小时前
c++ string
开发语言·c++
raindayinrain2 小时前
c++高性能--原子操作,无锁编程,操作顺序
开发语言·c++·性能优化
学逆向的2 小时前
C++ 合并命名空间
开发语言·c++·网络安全
Zhao-Jichao2 小时前
VS2026 打包 C++ 程序生成 EXE,借助 Installer Project 扩展工具
开发语言·c++
小徐不徐说3 小时前
C++ 接口与插件设计:从工厂注册到 Qt 动态插件
数据库·c++·qt·系统架构·程序设计
大都督会赢的4 小时前
C++第一节课
开发语言·c++
UIU1144 小时前
P1233 [ICPC 2001 Taejon R] 木棍加工
数据结构·c++·算法·动态规划
TMedical4 小时前
MedPlanning 截骨模拟矫形功能
c++·图像处理·人工智能·qt·健康医疗
qq_406981035 小时前
共享变量一 关于extern与const
开发语言·c++
chari克里7 小时前
QT5.15.2 + msvc2022部署全流程说明(windows可联网环境)
开发语言·c++·qt