24

C++ (主に C) でシミュレートされたファイル システムにパイピングを実装しています。ホストシェルでコマンドを実行する必要がありますが、シミュレートされたファイルシステムでパイプ自体を実行します。

pipe()fork()、およびsystem()システム コールを使用してこれを実現できますが、 popen()(パイプの作成、プロセスのフォーク、シェルへのコマンドの受け渡しを処理する) を使用することをお勧めします。パイプの親プロセスから書き込み、子プロセス側で読み取り、子から出力を書き戻し、最後に親からその出力を読み取る必要があるため、これは不可能かもしれません。私のシステムの man ページにpopen()は、双方向パイプが可能であると書かれていますが、私のコードは、単方向パイプのみをサポートする古いバージョンのシステムで実行する必要があります。

上記の個別の呼び出しを使用して、パイプを開閉してこれを実現できます。それは可能popen()ですか?

些細な例として、実行するls -l | grep .txt | grep cmdsには次のことを行う必要があります。

  • ls -lホスト上で実行するパイプとプロセスを開きます。その出力を読み返す
  • ls -lの出力をシミュレーターにパイプで戻します
  • パイプとプロセスを開き、パイプさgrep .txtれた出力でホスト上で実行しますls -l
  • この出力をシミュレーターにパイプで戻します (ここで立ち往生)
  • パイプとプロセスを開き、パイプさgrep cmdsれた出力でホスト上で実行しますgrep .txt
  • この出力をシミュレーターに戻して出力します

マン・ポペン

Mac OS X の場合:

関数はpopen()、双方向パイプを作成し、フォークし、シェルを呼び出すことによって、プロセスを「開きます」。親プロセスの以前の呼び出しによって開かれたストリームpopen() は、新しい子プロセスで閉じられます。歴史的にpopen()、単方向パイプで実装されていました。したがって、 の多くの実装ではpopen()、 mode 引数のみで読み取りまたは書き込みを指定できますが、両方は指定できません。は双方向パイプを使用して実装されるようになったため popen()、mode 引数は双方向データ フローを要求する場合があります。mode 引数はヌル終了文字列へのポインタで、読み取りの場合は 'r'、書き込みの場合は 'w'、読み取りと書き込みの場合は 'r+' でなければなりません。

4

6 に答える 6

20

パイピング/フォーク/システム化を行う独自の関数を作成することをお勧めします。次のように、関数でプロセスを生成し、読み取り/書き込みファイル記述子を返すことができます...

typedef void pfunc_t (int rfd, int wfd);

pid_t pcreate(int fds[2], pfunc_t pfunc) {
    /* Spawn a process from pfunc, returning it's pid. The fds array passed will
     * be filled with two descriptors: fds[0] will read from the child process,
     * and fds[1] will write to it.
     * Similarly, the child process will receive a reading/writing fd set (in
     * that same order) as arguments.
    */
    pid_t pid;
    int pipes[4];

    /* Warning: I'm not handling possible errors in pipe/fork */

    pipe(&pipes[0]); /* Parent read/child write pipe */
    pipe(&pipes[2]); /* Child read/parent write pipe */

    if ((pid = fork()) > 0) {
        /* Parent process */
        fds[0] = pipes[0];
        fds[1] = pipes[3];

        close(pipes[1]);
        close(pipes[2]);

        return pid;

    } else {
        close(pipes[0]);
        close(pipes[3]);

        pfunc(pipes[2], pipes[1]);

        exit(0);
    }

    return -1; /* ? */
}

そこに必要な機能を追加できます。

于 2010-10-07T17:55:42.900 に答える
10

あなたはあなた自身の質問に答えたようです。popen双方向パイプを開くことをサポートしていない古いシステムでコードを動作させる必要がある場合、使用することはできませpopenん (少なくとも提供されているものは使用できません)。

本当の問題は、問題の古いシステムの正確な機能についてです。特に、pipe双方向パイプの作成はサポートされていますか? pipe双方向パイプを作成できるが作成できない場合は、双方向パイプでpopen使用するコードのメイン ストリームを記述し、コンパイルされる双方向パイプを使用できるpopen実装を提供します。popen必要に応じて使用されます。

単方向パイプしかサポートしない古いシステムをサポートする必要がある場合は、、、 などを自分pipeで使用することにかなり行き詰まっています。私はおそらくこれを の最新バージョンのように機能する関数にまとめたいと思いますが、1 つのファイル ハンドルを返す代わりに、2 つのファイル ハンドル (1 つは子の 用、もう 1 つは子の 用) を持つ小さな構造体を埋めます。pipeforkdup2popenstdinstdout

于 2010-10-07T17:32:18.113 に答える
9

POSIX は、popen()呼び出しが双方向通信を提供するように設計されていないことを規定しています。

popen() のモード引数は、I/O モードを指定する文字列です。

  1. mode が r の場合、子プロセスが開始されると、そのファイル記述子 STDOUT_FILENO はパイプの書き込み可能な終端となり、呼び出しプロセスのファイル記述子 fileno(stream) になります。ここで、stream は popen() によって返されるストリーム ポインタです。は、パイプの読み取り可能な終端でなければなりません。
  2. mode が w の場合、子プロセスが開始されたとき、そのファイル記述子 STDIN_FILENO はパイプの読み取り可能端であり、呼び出しプロセスのファイル記述子 fileno(stream) であり、stream は popen() によって返されるストリーム ポインタである必要があります。パイプの書き込み可能な端になります。
  3. モードがその他の値の場合、結果は規定されていません。

移植可能なコードは、それ以上の仮定を行いません。BSDpopen()は、質問の説明に似ています。

さらに、パイプはソケットとは異なり、各パイプ ファイル記述子は単方向です。各方向に 1 つずつ構成された 2 つのパイプを作成する必要があります。

于 2010-10-07T17:29:20.383 に答える
1

2 つのパイプを作成し、各プロセスでファイル記述子を無駄にする必要はありません。代わりにソケットを使用してください。https://stackoverflow.com/a/25177958/894520

于 2014-08-07T08:51:58.930 に答える