c++からのpipe,fork,execを用いた外部プログラムのコントロール

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
chibago

c++からのpipe,fork,execを用いた外部プログラムのコントロール

#1

投稿記事 by chibago » 14年前

皆様、お世話になっております。
C++から既存のコードシステム(fortranで記述、実行ファイルのみ)を
コントロールする必要があり、勉強中です。表題にありますように、
やるべきことは分かってはいるのですが、敷居が高すぎて、少々
ご協力いただきたいと考えて居ります。
対象のコードは標準入力から受け取った入力を処理し、標準出力
に結果を吐き出すものです。
とりあえず、以下のような(target.fと名づけます)コードでテストしております。

コード:

      character*72 buf
      read(5,*) buf
      write(6,*) buf
      stop
      end
これは標準入力から取得した文字列を標準出力から吐き出すためのコードです。
pipe等の使い方に関しては
http://www.ncad.co.jp/~komata/c-kouza3.htm
が参考になり、もしかしたら、そのまま使えそうなサンプルが記載されておりました。
以下のようなものです。

コード:

#include    <stdio.h>
#include    <signal.h>

#define R    (0)
#define W    (1)

int popen2(command,fd_r,fd_w)
char  *command;
int   *fd_r,*fd_w;
{
int   pipe_c2p[2],pipe_p2c[2];
int   pid;

    /* Create two of pipes. */
    if(pipe(pipe_c2p)<0){
        perror("popen2");
        return(-1);
    }
    if(pipe(pipe_p2c)<0){
        perror("popen2");
        close(pipe_c2p[R]);
        close(pipe_c2p[W]);
        return(-1);
    }

    /* Invoke processs */
    if((pid=fork())<0){
        perror("popen2");
        close(pipe_c2p[R]);
        close(pipe_c2p[W]);
        close(pipe_p2c[R]);
        close(pipe_p2c[W]);
        return(-1);
    }
    if(pid==0){   /* I'm child */
        close(pipe_p2c[W]);
        close(pipe_c2p[R]);
        dup2(pipe_p2c[R],0);
        dup2(pipe_c2p[W],1);
        close(pipe_p2c[R]);
        close(pipe_c2p[W]);
        if(execlp("sh","sh","-c",command,NULL)<0){
            perror("popen2");
            close(pipe_p2c[R]);
            close(pipe_c2p[W]);
            exit(1);
        }
    }

    close(pipe_p2c[R]);
    close(pipe_c2p[W]);
    *fd_w=pipe_p2c[W];
    *fd_r=pipe_c2p[R];

    return(pid);
}
引数としてはコマンドと、書き込み、読み込み用のファイルディスクリプタのポインター
(これが分かりません)を渡すと記載されているのですが使い方がいまいち分かりません。

当面の目標としては、上記のtarget.fをコンパイルしたものをC++より操作し"hello"と標準入力
から入力しtargetfが標準出力から吐き出した"hello"を受け取りたいと考えております。

とりあえず、popen2の動作について簡単に教えていただけると助かります。
(元サイトの説明は私には難しすぎます。)

また、pstreamというものをお使いの方は、私のやろうとしていることが実現できるか
お知らせいただければ助かります。(テストケースをのぞいた感じではなさそうでしたが)

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: c++からのpipe,fork,execを用いた外部プログラムのコントロール

#2

投稿記事 by softya(ソフト屋) » 14年前

えっらく引数が古い書き方のC言語ですね。
あとLinux/Unix系のOSが前提って話で良いですか?

ざっと見た感じだと、popenを拡張して標準入出力をpipe化しましょうと言う関数です。
char  *command; 起動するプログラム名など
int   *fd_r,*fd_w; 要するにファイルディスクリプタを戻すためのポインタ変数です。

cygwinでシェルsortのソートを動かしてみました。

コード:

#include    <stdio.h>
#include    <string.h>
#include    <signal.h>
#include <unistd.h>
#include <stdlib.h>

#define R    (0)
#define W    (1)

int popen2(char *command,int *fd_r,int *fd_w)
{
int   pipe_c2p[2],pipe_p2c[2];
int   pid;

    /* Create two of pipes. */
    if(pipe(pipe_c2p)<0){
        perror("popen2");
        return(-1);
    }
    if(pipe(pipe_p2c)<0){
        perror("popen2");
        close(pipe_c2p[R]);
        close(pipe_c2p[W]);
        return(-1);
    }

    /* Invoke processs */
    if((pid=fork())<0){
        perror("popen2");
        close(pipe_c2p[R]);
        close(pipe_c2p[W]);
        close(pipe_p2c[R]);
        close(pipe_p2c[W]);
        return(-1);
    }
    if(pid==0){   /* I'm child */
        close(pipe_p2c[W]);
        close(pipe_c2p[R]);
        dup2(pipe_p2c[R],0);
        dup2(pipe_c2p[W],1);
        close(pipe_p2c[R]);
        close(pipe_c2p[W]);
        if(execlp("sh","sh","-c",command,NULL)<0){
            perror("popen2");
            close(pipe_p2c[R]);
            close(pipe_c2p[W]);
            exit(1);
        }
    }

    close(pipe_p2c[R]);
    close(pipe_c2p[W]);
    *fd_w=pipe_p2c[W];
    *fd_r=pipe_c2p[R];

    return(pid);
}

int main()
{
	char  str[512],*ptr;
	int fd_r,fd_w;
	int pid;
	char *outs = "ccc\naaa\nbbb\n";
	
    if((pid=popen2("sort",&fd_r,&fd_w))==0){
        fprintf(stderr,"error!!!\n");
        exit(-1);
    }
    //	sortに入力
    write(fd_w,outs,strlen(outs));
    close(fd_w);
    //	sortから出力を受け取る。
    while(1){
		if( read(fd_r,str,1) == 0 ) {
			break;
        }
        str[1]= 0x00;
        printf("%s",str);
    }
    close(fd_r);
	return 0;
}
おまけでLinux系マニュアルのページです。man on WWWで関数名などを入力すると該当ページを開いてくれます。調べるのに使ってください。
http://linuxjm.sourceforge.jp/
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

chibago

Re: c++からのpipe,fork,execを用いた外部プログラムのコントロール

#3

投稿記事 by chibago » 14年前

softyaさん、
大変ありがとうございました。
いきなり、目標達成できてしまいました。
(自分でテスト用に用意したtarget.fもコントロールできました。)

ご解説いただいたpopen2ですが、私はてっきり、コマンドの制御のすべて
を内部でおこなえるものだとおもっておりましたが、pipeをつなぐだけのもの
だったようですね。

ところで、解決はしたのですが、
>あとLinux/Unix系のOSが前提って話で良いですか?
OSに依存しない形で同様の処理はできるのでしょうか。

いろいろ調べた結果(この板にも質問しました)、結局、
この方法しかなさそうだという結論に達しました。
ただ、可搬性の高いシステムを構築しようと思っておりますので、
もし、他のやり方がありましたらご教示いただければ幸です。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: c++からのpipe,fork,execを用いた外部プログラムのコントロール

#4

投稿記事 by softya(ソフト屋) » 14年前

少なくともcygwinでコンパイルすればそのままのコードでWindowsで使えます。
ただし、exeと同じフォルダにcygwin1.dllが必要ですけど。
もしVC++などでコンパイルしたければ、Windowsにはfork関数がないので結構書き換えないと行けないと思います。

[追記]
MacはUnix系なのでMacでコンパイルすればそのまま使えます。
ということで、Mac/Linux/Unix/Windows(cygwin)で一般的な環境はカバーできると思います。

[追記の追記]
もっと可搬性を高くしたい&OS依存を避けたければC++ではなくJavaの利用も考えられた方が良いと思います。
そうすればOS毎にコンパイルする必要もなくなります。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

hjkl

Re: c++からのpipe,fork,execを用いた外部プログラムのコントロール

#5

投稿記事 by hjkl » 14年前

ファイルディスクリプタだとシステムコールを自分で呼ばなきゃなので
fdopenでストリームに変換した方が標準C 入出力の関数が使えるので
便利かなーと。

コード:

int r, w;

//たぶんpopen2の戻り値は-1がエラーだと思う。
if (popen2("target", &r, &w) == -1) {
  //エラー処理
}

FILE *read_stream = fdopen(r, "r");
if (read_stream == NULL) {
  //エラー処理
}

FILE *write_stream = fdopen(w, "w");
if (read_stream == NULL) {
  //エラー処理
}

//fwriteとかfread等で通信
あんまりプロセス間通信ってしたことないんで希望する動作を実現するものか分かりませんが
名前付きパイプ以外でいいなら
c++ならboost.asioでソケット(fortranにネットワークライブラリがあるのか知りませんが)
とか多言語間の通信にはThriftやGoogle Protocol Buffers等のRPCをつかった方が可搬性はあがるかもしれませんね。
自分はRPCフレームワークは使ったこと無いのでなんとも言えませんが...。

chibago

Re: c++からのpipe,fork,execを用いた外部プログラムのコントロール

#6

投稿記事 by chibago » 14年前

貴重なご情報ありがとうございます。
頂いた情報を元にさらに調べましたら
http://d.hatena.ne.jp/c2top/20090528/1243495644
istreamにもつながりそうです。

結局のところ、pstreamと同じになるのかもしれませんが。
(pstreamで2つのパイプを設定できれば、何も苦労はないのですが。)

マルチプラットフォームについては、好き好きだとおもいますが、
わたしは、fedoraをつかい、windows用にはmingwのクロスコンパイルを
利用しております。(Macはまわりの人も含めて、あまりユーザーがいません。)
(残念ながらmingwではforkは利用できないようです。)

Javaも使ったことはあるのですが、実行環境(ランタイムライブラリ)等のバージョン
等が複雑過ぎて、作成したプログラムの動作がまちまち(GUIアプリですが)なのが気になります。

閉鎖

“C言語何でも質問掲示板” へ戻る