execlp実行プロセスの終了

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

execlp実行プロセスの終了

#1

投稿記事 by chibago » 13年前

皆様、お世話になっております。
chibagoです。

先日、fork&pipeのプログラムが異常終了するという件でご相談させていただきましたが、
原因が特定できましたのでアドバイスをいただければと思います。

コード:

PipeToStandardIO::PipeToStandardIO(const std::string command){
  int pid;
  if((pid = this->generate_pipe(command)) == 0){
    std::cout<<"Cannot generate pipe"<<std::endl;
    exit(-1);
  }
  this->open_stream();
}

PipeToStandardIO::~PipeToStandardIO(){
  if (this->read_stream_status != "closed")
    this->close_standard_output();
  if (this->write_stream_status != "closed")
    this->close_standard_input();
}

  void PipeToStandardIO::set_standard_input(const std::string& str){
    fputs(str.c_str(), this->write_stream); 
}

  std::string PipeToStandardIO::get_standard_output(){
    std::ostringstream os;
    char buf[1000];
    while(fgets(buf, 1000, this->read_stream) !=NULL){
      os << buf;    
    } 
    return os.str();
}

void PipeToStandardIO::close_standard_output(){
  if (this->read_stream_status != "closed")
      fclose(this->read_stream);
  this->read_stream_status = "closed";
}

void PipeToStandardIO::close_standard_input(){
  if (this->write_stream_status != "closed")
       fclose(this->write_stream);
  this->write_stream_status = "closed";
}

int PipeToStandardIO::generate_pipe(const std::string command){
  int pipe_c2p[2], pipe_p2c[2];
  int pid;

  /* Create two pipes. */
  if(pipe(pipe_c2p)<0){
    std::cout<<"Cannot create a pipe of child to parent."<<std::endl;
    return -1;
  }
  if(pipe(pipe_p2c)<0){
    std::cout<<"Cannot create a pipe of parent to child."<<std::endl;
    close(pipe_c2p[0]);
    close(pipe_c2p[1]);
    return -1;
  }

  /* Invoke processs */
  if((pid = fork()) < 0 ){
    std::cout<<"Cannot invoke processs."<<strerror(errno)<<std::endl;
    close(pipe_c2p[0]);
    close(pipe_c2p[1]);
    close(pipe_p2c[0]);
    close(pipe_p2c[1]);
    return -1;
  }

  /* Implimentation of child. */
  if(pid == 0){
    close(pipe_p2c[1]);
    close(pipe_c2p[0]);
    dup2(pipe_p2c[0], 0);
    dup2(pipe_c2p[1], 1);
    close(pipe_p2c[0]);
    close(pipe_c2p[1]);
    if (execlp("sh", "sh", "-c", command.c_str(), NULL) < 0){
    std::cout<<"Cannot invoke child processs."<<strerror(errno)<<std::endl;
    close(pipe_p2c[0]);
    close(pipe_c2p[1]);
    exit(1);
    }
  }

  close(pipe_p2c[0]);
  close(pipe_c2p[1]);
  this->fd_w = pipe_p2c[1];
  this->fd_r = pipe_c2p[0];
  return pid;

  }

void PipeToStandardIO::open_stream(){
  this->read_stream = fdopen(this->fd_r, "r");
  if (this->read_stream == NULL){
    std::cout<<"Cannot establish a read stream."<<strerror(errno)<<std::endl;
    exit(-1);
  }

  this->write_stream = fdopen(this->fd_w, "w");
  if (this->write_stream == NULL){
    std::cout<<"Cannot establish a write stream."<<strerror(errno)<<std::endl;
    exit(-1);
  }
}

これは、外部のプログラムの標準入出力をパイプして制御するものです。
パイプがつながりましたらexeclp関数でプログラムを実行します。

このプログラムを使って何回も他のプログラムを呼び出して
計算をしておりますが、execlpにより呼び出されたプログラム
が終了してもプロセスとして残り続けているようです。(psで確認しました。)

そもそも、execlpは実行後にリソースを解放してくれるようなことがネットで
書かれておりますが、残ってしまうのはpipeやforkを用いているからでしょうか。

この問題を回避する方法もしくはプロセスを明示的に終了させる
方法をご存知でしたらご指導いただければ幸いです。

ISLe
記事: 2650
登録日時: 15年前
連絡を取る:

Re: execlp実行プロセスの終了

#2

投稿記事 by ISLe » 13年前

chibago さんが書きました:そもそも、execlpは実行後にリソースを解放してくれるようなことがネットで
書かれておりますが、残ってしまうのはpipeやforkを用いているからでしょうか。
パイプで繋がっているプロセスが勝手に消えては困るので、コマンドの実行が終わっても子プロセスのゾンビは残ります。
パイプを開いたらきちんと閉じる必要があります。

ひとつのPipeToStandardIOインスタンスに対してPipeToStandardIOメンバ関数を繰り返し呼び出すとパイプがクローズされないまま子プロセスがどんどん増えていくようになっていますが、そのような使い方をしていないでしょうか。

chibago

Re: execlp実行プロセスの終了

#3

投稿記事 by chibago » 13年前

ISLe様
お返事ありがとうございます。

使い方を確認しましたが、コントロールプログラムでは、

コード:

  PipeToStandardIO prog(this->get_load_module_path());
  prog.set_standard_input(this->get_input());
  prog.close_standard_input();
  std::string output = prog.get_standard_output();
PipeToStandardIOインスタンスを生成し、
一通り実行した後スコープが外れてインスタンス自体は消滅するはずです。

次の実行では、またこのコントロールプログラムが
よびだされますので、ご指摘いただいたような
状況は発生していないと思います。

なにか他に問題があるのでしょうか。

ISLe
記事: 2650
登録日時: 15年前
連絡を取る:

Re: execlp実行プロセスの終了

#4

投稿記事 by ISLe » 13年前

『fork 子プロセスが残る』というキーワードでググってみたところ、子プロセスに対してwaitしないで親プロセスが生き続けるとゾンビプロセスが増えていくという情報と対策の書かれたページを見付けました。

PipeToStandardIOのデストラクタでパイプを閉じた後に子プロセスをwaitする処理を追加してみてはいかがでしょうか。

chibago

Re: execlp実行プロセスの終了

#5

投稿記事 by chibago » 13年前

ISLe様
有難うございました。

良く理解はしておりませんが、
でコンストラクタの頭に
wiat(0);
としましたら、問題が解決で着ました。

ただ、良く理解しないでwait文を付け加えましたので、
弊害が発生しないか不安です。

もし、もう少し適切な実装がございましたら、
アドバイスをいただければ助かります。

ISLe
記事: 2650
登録日時: 15年前
連絡を取る:

Re: execlp実行プロセスの終了

#6

投稿記事 by ISLe » 13年前

wait(0)というかwait(NULL)は子プロセスが複数あればいずれかの子プロセスが終了すると返ります。
子プロセスを一度に一つしか作らないのであれば運用上は問題無いです。

設計として対称性を考慮するなら、forkで得られた子プロセスのPIDを記録して、デストラクタではwaitpidを使うのが良いと思います。

chibago

Re: execlp実行プロセスの終了

#7

投稿記事 by chibago » 13年前

ISLe様
お返事ありがとうございます。

基本的に一つのプロセスしかながしませんので、
問題ないとお墨付きをいただきましたので安心しております。

複数のプロセスを管理する際にはpididを保管しておいて
管理するフレームワークが必要そうですね。

閉鎖

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