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

如何通过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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 18:40:57