0

C++ アプリケーション内から外部プログラムを実行する必要があります。そのプログラムからの出力が必要で (プログラムがまだ実行されている間にそれを見たい)、入力も取得する必要があります。

IO をリダイレクトするための最良かつ最もエレガントな方法は何ですか? 独自のスレッドで実行する必要がありますか?例はありますか?

OSX上で動作しています。

私は次のように実装しました:

ProgramHandler::ProgramHandler(std::string prog): program(prog){
// Create two pipes
std::cout << "Created Class\n";
pipe(pipe1);
pipe(pipe2);

int id = fork();

std::cout << "id: " << id << std::endl;

if (id == 0)
{
    // In child
    // Close current `stdin` and `stdout` file handles
    close(fileno(stdin));
    close(fileno(stdout));

    // Duplicate pipes as new `stdin` and `stdout`
    dup2(pipe1[0], fileno(stdin));
    dup2(pipe2[1], fileno(stdout));

    // We don't need the other ends of the pipes, so close them
    close(pipe1[1]);
    close(pipe2[0]);

    // Run the external program
    execl("/bin/ls", "bin/ls");
    char buffer[30];
    while (read(pipe1[0], buffer, 30)) {
        std::cout << "Buf: " << buffer << std::endl;
    }
}
else
{
    // We don't need the read-end of the first pipe (the childs `stdin`)
    // or the write-end of the second pipe (the childs `stdout`)
    close(pipe1[0]);
    close(pipe2[1]);


    // Now you can write to `pipe1[1]` and it will end up as `stdin` in the child
    // Read from `pipe2[0]` to read from the childs `stdout`
}

}

しかし、出力として私はこれを得る:

作成されたクラス

id: 84369

ID: 0

なぜ2回呼び出され、なぜ最初にフォークしないのかわかりません。私は何をしていますか/間違っていますか。

4

4 に答える 4

3

POSIX システム (OSX や Linux など) を使用している場合は、システム コールpipeforkclosedup2およびを学習する必要がありexecます。

外部アプリケーションからの読み取り用と書き込み用の 2 つのパイプを作成します。次にfork、新しいプロセスを作成し、子プロセスでパイプを設定してから、新しいハンドルstdinとファイルハンドルを使用して子プロセスを外部プログラムに置き換えるstdout呼び出しを行います。親プロセスでは、子プロセスからの出力を読み取り、その入力に書き込むことはできません。execstdinstdout

擬似コード:

// Create two pipes
pipe(pipe1);
pipe(pipe2);

if (fork() == 0)
{
    // In child
    // Close current `stdin` and `stdout` file handles
    close(FILENO_STDIN);
    close(FILENO_STDOUT);

    // Duplicate pipes as new `stdin` and `stdout`
    dup2(pipe1[0], FILENO_STDIN);
    dup2(pipe2[1], FILENO_STDOUT);

    // We don't need the other ends of the pipes, so close them
    close(pipe1[1]);
    close(pipe2[0]);

    // Run the external program
    exec("/some/program", ...);
}
else
{
    // We don't need the read-end of the first pipe (the childs `stdin`)
    // or the write-end of the second pipe (the childs `stdout`)
    close(pipe1[0]);
    close(pipe2[1]);

    // Now you can write to `pipe1[1]` and it will end up as `stdin` in the child
    // Read from `pipe2[0]` to read from the childs `stdout`
}

それらの詳細については、システム コールのマニュアル ページを参照してください。これらのシステム コールはすべて失敗する可能性があるため、エラー チェックも追加する必要があります。

于 2012-11-04T13:50:01.123 に答える
1

これを行うにはかなり標準的な方法があります。一般に、プロセスをフォークして、子プロセスの標準 I/O (fd 0,1) を閉じたいとします。分岐する前に 2 つのパイプを作成し、分岐後に子の標準入力と標準出力を閉じ、dup を使用してそれらをパイプに接続します。

疑似コードは、接続の一方の側のみを示しています。もう一方の側を理解できると確信しています。

int main(){
     int fd[2]; // file descriptors
     pipe(fd);

      // Fork child process
      if (fork() == 0){
            char buffer [80];
            close(1);
            dup(fd[1]); // this will take the first free discriptor, the one you just closed. 
            close(fd[1]); // clean up
        }else{
           close(0);
           dup(fd[0]);
           close(fd[0]);
        }    
        return 0;
    }

パイプをセットアップし、親スレッドの 1 つを選択などで待機させたら、外部ツールの exec を呼び出して、すべてのデータを流すことができます。

于 2012-11-04T13:59:45.217 に答える
0

POSIX システムで別のプログラムと通信するための基本的な方法は、pipe()、次にfork()プログラム、close()およびdup()ファイル記述子を正しい場所にセットアップし、最後にexec??()目的の実行可能ファイルにセットアップすることです。

これが完了すると、2 つのプログラムが適切なストリームに接続されます。残念ながら、これは 2 つのプログラムの非同期処理の形式には対応していません。つまり、作成されたファイル記述子に適切な非同期および非ブロッキング操作でアクセスしたい (つまり、さまざまなファイル記述子をノンブロッキングに設定したり、アクセスpoll()できることを示す結果が得られた場合にのみそれらにアクセスしたりする) ことを希望する可能性があります。それらにアクセスします)。ただし、実行可能ファイルが 1 つしかない場合は、別のスレッドから制御する方が簡単かもしれません。

于 2012-11-04T13:57:26.103 に答える
0

別のアプローチ (および外部プログラムも作成している場合) は、共有メモリを使用することです。(疑似コード)の行に沿った何か

// create shared memory
int l_shmid = shmget(key, size ,0600 | IPC_CREAT);

if(l_shmid < 0) 
  ERROR

// attach to shared memory
dataptr* ptr = (dataptr*)shmat(l_shmid, NULL, 0600);

// run external program
pid_t l_pid = fork();

if(l_pid == (pid_t)-1)
{
  ERROR

  // detach & delete shared mem
  shmdt(ptr);
  shmctl(l_shmid, 
         IPC_RMID,
         (shmid_ds *)NULL);
  return;
}
else if(l_pid == 0)
{
    // child: 
    execl(path,
          args,
          NULL); 
    return;
}

// wait for the external program to finish
int l_stat(0);
waitpid(l_pid, &l_stat, 0);

// read from shmem
memset(mydata, ..,..);
memcpy(mydata, ptr, ...);

// detach & close shared mem
shmdt(ptr);
shmctl(l_shmid, 
       IPC_RMID,
       (shmid_ds *)NULL);

外部プログラムは、同様の方法で共有メモリに書き込むことができます。パイプと読み取り/書き込み/選択などは必要ありません。

于 2013-01-14T17:26:34.287 に答える