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
相关产品推荐
相关产品推荐

