如何通过BSUB -I将远程程序交互式Shell流到启动它的C++程序stdout
问题
在Linux环境下,C++进程P1通过LSF调度器的bsub命令在远程机器启动带交互式Shell的进程P2(比如Python),期望实现:
- P1的用户能直接操作P2的交互式Shell,操作完成退出后P1继续执行,后续可按需启动其他交互式Shell
- P2崩溃时P1能自动重启它
目前使用popen("bsub -Ip python", "r")仅能看到远程进程启动的提示,无法获取Python交互Shell的输出,也无法输入命令;但直接在Shell执行bsub -Ip python可正常使用交互Shell。
测试代码:
// Program for P1 #include <iostream> int main() { /* do P1 work */ char buffer[128]; FILE* pipe = popen("bsub -Ip python", "r"); // P2 is python here. if (!pipe) { return 0; } std::string result = ""; while (!feof(pipe)) { if (fgets(buffer, 128, pipe) != NULL) result += buffer; } pclose(pipe); /* continue P1 work */ return 0; }
运行结果仅输出启动提示:
$ ./a.out <<Waiting for dispatch ...>> <<Starting on 1i-38-204>>
需解决如何在P1中获取P2的交互流并重定向到P1的stdout/stdin,以及崩溃重启的问题。
解决方案
1. 替换popen,用fork+exec实现全双向流重定向
popen仅支持单向数据流(读或写),且未将P1的stdin传递给子进程,这是无法交互的核心原因。需手动fork子进程,将子进程的stdin/stdout/stderr全部重定向到P1的对应流:
示例代码片段:
#include <iostream> #include <unistd.h> #include <sys/wait.h> #include <termios.h> #include <cstring> int main() { // 保存原终端属性,后续恢复 struct termios orig_termios; if (tcgetattr(STDIN_FILENO, &orig_termios) == -1) { perror("tcgetattr failed"); return 1; } pid_t pid = fork(); if (pid == 0) { // 子进程:将标准流绑定到当前终端 dup2(STDIN_FILENO, STDIN_FILENO); dup2(STDOUT_FILENO, STDOUT_FILENO); dup2(STDERR_FILENO, STDERR_FILENO); // 执行bsub交互式命令 execlp("bsub", "bsub", "-Ip", "python", nullptr); // execlp失败则退出 perror("execlp failed"); _exit(1); } else if (pid > 0) { // 父进程:设置终端为原始模式,确保输入直接传递给子进程 struct termios raw = orig_termios; cfmakeraw(&raw); if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1) { perror("tcsetattr failed"); waitpid(pid, nullptr, 0); return 1; } // 等待子进程结束并获取状态 int status; waitpid(pid, &status, 0); // 恢复原终端属性 tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); // 检查子进程是否异常退出,实现重启逻辑 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { std::cout << "P2 crashed, restarting..." << std::endl; // 此处可添加循环重启逻辑,比如重新执行fork+exec流程 } } else { perror("fork failed"); return 1; } // P1继续执行后续逻辑 std::cout << "P1 resuming work..." << std::endl; return 0; }
2. 关键细节说明
- 终端模式配置:用
cfmakeraw将终端设为原始模式,避免P1的终端缓冲或处理用户输入,确保输入直接传递给bsub和远程的Python进程,和直接在Shell执行的交互体验一致。 - 全流重定向:通过
dup2将子进程的三个标准流完全绑定到P1的对应流,解决popen单向传输的局限。 - 崩溃重启:通过
waitpid获取子进程的退出状态,判断是否为异常退出,若满足条件则触发重启逻辑(比如循环重新启动子进程)。
3. 替代方案:使用LSF原生API
如果上述fork/exec方式仍有问题,可直接调用LSF提供的C API(如lsb_submit)提交交互式任务。通过API能更精细地控制任务的输入输出、状态监控,避免依赖Shell命令的流重定向,还能直接获取任务的异常状态以实现重启。
内容的提问来源于stack exchange,提问作者bdd
相关产品推荐
相关产品推荐

