popen()は、pipe() + fork()のような双方向パイプを作成できますか?

StackOverflow https://stackoverflow.com/questions/3884103

  •  28-09-2019
  •  | 
  •  

質問

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

でこれを達成できました pipe(), fork(), 、 と system() システムコールですが、使用することをお勧めします popen() (パイプの作成、プロセスの分岐、およびシェルにコマンドを渡すことを処理します)。これは不可能かもしれません(私は)パイプの親プロセスから書き込み、子プロセスの終わりを読み、子供から出力を書き戻し、最後に親からの出力を読むことができる必要があるためです。のマンページ 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() モード引数が両方ではなく、読み取りまたは書き込みを指定することのみを許可します。なぜなら popen() 現在、双方向パイプを使用して実装されているため、モード引数は双方向のデータフローを要求する場合があります。モード引数は、読み取りには「r」、執筆用の「w」、または読み書きのための「r+」でなければならないヌル終端文字列へのポインターです。

役に立ちましたか?

解決

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

本当の問題は、問題の古いシステムの正確な能力に関するものです。特に、彼らをします pipe 双方向パイプの作成をサポートしますか?彼らが持っている場合 pipe それは双方向のパイプを作成することができますが、 popen それはそうではありません、それから私は使用するコードのメインストリームを書きます popen 双方向のパイプで、 popen 必要に応じて使用済みにコンパイルされる双方向パイプを使用できます。

十分に古いシステムをサポートする必要がある場合 pipe 一方向のパイプのみをサポートしているだけで、使用することにかかっています pipe, fork, dup2, 、など、自分で。私はおそらくこれを機能する関数でまだまとめます ほとんど の現代バージョンのように popen, 、しかし、1つのファイルハンドルを返す代わりに、子供用の2つのファイルハンドルを2つのファイルハンドルで埋めます stdin, 、もう1つは子供のためです stdout.

他のヒント

私はあなたのために配管/フォーキング/システムを行うためにあなた自身の機能を書くことを提案することをお勧めします。関数がプロセスを生成し、読み取り/書き込みファイル記述子を返すことができます...

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; /* ? */
}

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

Posixはそれを規定しています popen() コールは、双方向の通信を提供するようには設計されていません。

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

  1. モードがrの場合、子プロセスが開始された場合、そのファイル記述子stdout_filenoはパイプの書き込み可能な端であり、ファイル記述子fileno(ストリーム)は呼び出しプロセスで、ストリームはpopen()によって返されるストリームポインターです。パイプの読み取り可能な端になります。
  2. モードがwの場合、子プロセスが開始された場合、ファイル記述子stdin_filenoはパイプの読み取り可能な端であり、ファイル記述子fileno(stream)は呼び出しプロセスで、ストリームはpopen()によって返されるストリームポインターです。パイプの書き込み可能な端になりましょう。
  3. モードが他の値の場合、結果は不特定です。

ポータブルコードは、それ以上の仮定を行いません。 BSD popen() あなたの質問が説明するものに似ています。

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

の1つで netResolve バックエンド私はスクリプトと話しているので、そのために書く必要があります stdin そして、それから読んでください stdout. 。次の関数は、パイプにリダイレクトされたstdinとstdoutを使用してコマンドを実行します。あなたはそれを使用して、あなたの好みに合わせて適応させることができます。

static bool
start_subprocess(char *const command[], int *pid, int *infd, int *outfd)
{
    int p1[2], p2[2];

    if (!pid || !infd || !outfd)
        return false;

    if (pipe(p1) == -1)
        goto err_pipe1;
    if (pipe(p2) == -1)
        goto err_pipe2;
    if ((*pid = fork()) == -1)
        goto err_fork;

    if (*pid) {
        /* Parent process. */
        *infd = p1[1];
        *outfd = p2[0];
        close(p1[0]);
        close(p2[1]);
        return true;
    } else {
        /* Child process. */
        dup2(p1[0], 0);
        dup2(p2[1], 1);
        close(p1[0]);
        close(p1[1]);
        close(p2[0]);
        close(p2[1]);
        execvp(*command, command);
        /* Error occured. */
        fprintf(stderr, "error running %s: %s", *command, strerror(errno));
        abort();
    }

err_fork:
    close(p2[1]);
    close(p2[0]);
err_pipe2:
    close(p1[1]);
    close(p1[0]);
err_pipe1:
    return false;
}

https://github.com/crossdistro/netresolve/blob/master/backends/exec.c#l46

(同じコードを使用しました Popen同時読み書き)

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

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top