You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下C++调用ROS启动文件报错:Unable to modify FD epoll: No such file or directory

解决ROS中fork+system调用roslaunch出现"Unable to modify FD epoll"错误的方案

这个问题我之前帮同事排查过好几次,本质是ROS节点的epoll文件描述符(FD)在fork后被子进程继承,导致子进程执行roslaunch时无法正确操作这些不属于它的FD。当你用fork()创建子进程时,子进程会全盘继承父进程打开的所有FD,包括ROS用来管理进程间通信的epoll句柄;而system()调用会启动一个shell,这个shell在执行roslaunch时会尝试修改这些继承来的FD,结果系统发现这些FD的上下文不属于当前子进程,就抛出了"Unable to modify FD epoll: No such file or directory"的错误。

下面是几个可行的解决办法,按推荐程度排序:

1. 子进程中关闭多余FD后改用exec系列函数(最优解)

system()会启动中间shell,带来额外的FD继承和环境问题,直接用exec系列函数替换system(),同时在子进程中关闭所有非标准FD:

int NodeletLauncher::startNode(std::string nodeName, std::string launchFile) {
    pid_t pid = fork();
    switch (pid) {
        case -1:
            ROS_ERROR_STREAM("Fork failed");
            return -1;
        case 0:
            // 关闭所有非标准输入输出的FD,避免继承父进程的ROS epoll句柄
            for (int fd = 3; fd < sysconf(_SC_OPEN_MAX); ++fd) {
                close(fd);
            }
            ROS_INFO_STREAM("Starting node: " << nodeName);
            // 用execlp直接执行roslaunch,不需要经过shell
            execlp("roslaunch", "roslaunch", nodeName.c_str(), launchFile.c_str(), (char*)NULL);
            // 如果execlp返回,说明执行失败
            ROS_ERROR_STREAM("Failed to launch " << nodeName << " with file " << launchFile);
            exit(EXIT_FAILURE);
        default:
            // 父进程可以记录pid,后续用于管理子进程
            ROS_INFO_STREAM("Launched node " << nodeName << " with PID " << pid);
            return pid;
    }
}

这里execlp会直接替换子进程的地址空间,不会保留父进程的任何多余FD,彻底避免了FD冲突问题。

2. 仅关闭ROS相关的FD(针对性方案)

如果你不想关闭所有FD,可以尝试只关闭ROS的epoll FD。不过这个方法需要你找到ROS创建的epoll句柄,相对麻烦:

  • 在父进程中通过lsof -p <父进程PID>查看所有打开的FD,找到类型为epoll的FD号
  • 在子进程中关闭这些特定的FD后再调用system()
    不过这个方法不够通用,因为不同ROS版本或节点创建的epoll FD号可能不同,所以还是推荐第一种方案。

3. 用popen替代system(折中方案)

popen()也会启动shell,但可以通过设置参数来控制FD的继承,不过本质上还是不如exec系列干净,仅作为备选:

case 0:
    // 同样先关闭多余FD
    for (int fd = 3; fd < sysconf(_SC_OPEN_MAX); ++fd) {
        close(fd);
    }
    ROS_INFO_STREAM("Starting node: " << nodeName);
    std::string command_call = "roslaunch " + nodeName + " " + launchFile + " &";
    FILE* pipe = popen(command_call.c_str(), "r");
    if (!pipe) {
        ROS_ERROR_STREAM("popen failed");
        exit(EXIT_FAILURE);
    }
    // 可以读取输出,不需要的话直接关闭
    pclose(pipe);
    exit(0);
    break;

最后提醒一下:父进程fork后,最好不要让子进程保留任何父进程的ROS上下文(比如节点句柄),所以关闭FD+用exec替换进程是最稳妥的方式,能彻底隔离父进程和子进程的ROS环境。

内容的提问来源于stack exchange,提问作者Gil404

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 11:13:32