試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

Vala言語で外部プロセスを実行する(GLib.Process.spawn_async_with_pipes()を使用・メインループを用いた例・メモ)

Vala言語で外部プロセスを実行する(簡単な例・メモ)」と「Vala言語で外部プロセスを実行する(簡単な例・コード例と出力結果)」の続き。ここではGLib.Process.spawn_async_with_pipes()関数を使用しつつ、GLibのメインループの機能により子プロセス終了時にステータス値を取得する方法についてを扱う。

GLib.Process.spawn_async_with_pipes()について

public static bool spawn_async_with_pipes (string? working_directory,
                                           string[] argv,
                                           string[]? envp,
                                           SpawnFlags _flags,
                                           SpawnChildSetupFunc? child_setup,
                                           out Pid child_pid,
                                           out int standard_input = null,
                                           out int standard_output = null,
                                           out int standard_error = null)
                                           throws SpawnError;

関数GLib.Process.spawn_async_with_pipes()は外部プロセス実行系関数の中で最も指定する引数の数が多いが、最も細かい制御ができるということでもある。
引数は順番に下のようになる。

  1. 作業ディレクトリ [string 親を継承するならnull]
  2. コマンド行 [string配列*1]
  3. 環境変数 [string配列*2 親を継承するならnull]
  4. SpawnFlags (後述)
  5. exec()の直前に子プロセスで*3実行する関数(null可)
  6. <out>子プロセスのプロセスIDを格納する変数 [GLib.Pid]
  7. <out>子プロセスの標準入力のファイル記述子を格納する変数 [int・null可]
  8. <out>子プロセスの標準出力のファイル記述子を格納する変数 [int・null可]
  9. <out>子プロセスの標準エラー出力のファイル記述子を格納する変数 [int・null可]

「SpawnFlags」については以下のフラグの中から指定する(「|」で区切って複数個指定可)。

public enum SpawnFlags {
 LEAVE_DESCRIPTORS_OPEN,
 DO_NOT_REAP_CHILD,
 SEARCH_PATH,
 STDOUT_TO_DEV_NULL,
 STDERR_TO_DEV_NULL,
 CHILD_INHERITS_STDIN,
 FILE_AND_ARGV_ZERO
}
  • SEARCH_PATHを指定しない場合は実行ファイル(GLib.Process.spawn_async_with_pipes()の2番目の引数の0番の文字列)の場所を絶対パスで指定する必要がある・指定した場合は環境変数PATHから探索される
  • DO_NOT_REAP_CHILDを指定すると子プロセスの終了を処理しない(手動で処理することになる)

「DO_NOT_REAP_CHILD」については下で詳しく扱う。他の説明は(中には名前で分かりそうなものもあるが)GLibのドキュメントを参照。
この関数を呼ぶと指定した外部プロセスが実行され、プロセスIDと子プロセスの標準入力/標準出力/標準エラー出力のファイル記述子が(out指定により)代入される。これらはGLibのIOチャンネルという機能を用いてファイル記述子から読み書きを行うことができるが、扱うのはまだ先となる。
この関数は、名前に「async」とあるように、この関数は子プロセスの実行を待たず(非同期)に処理を行う。ただし、別途Posix.waitpid()関数を用いることにより子プロセスの終了を待って戻り値を得ることもできる。*4こちらも詳しくは別記事で扱う。

子プロセスのステータス値を得る

子プロセスのステータス値を得るには

  • フラグGLib.SpawnFlags.DO_NOT_REAP_CHILDを有効にする
  • GLib.ChildWatch.add()もしくはGLib.ChildWatch.add_full()を用いて子プロセスの終了時に呼ばれる関数の関連付けを行う
  • 上記関数によって関連付けられた関数の中でGLib.Process.close_pid()を呼んで後始末を行う
  • 更にその関数の中でGLib.ChildWatch.add()系関数の戻り値(イベントソースID)を引数としてGLib.Source.remove()を呼んで子プロセス終了の監視を解除

のように記述する。
(2009/7/24)GLib.Source.remove()に関する記述を追加

public static uint add (Pid pid, ChildWatchFunc function);
public static uint add_full (int priority, Pid pid, owned ChildWatchFunc function);

add_full()の優先度指定(最初の引数)では優先度を示す定数(HIGH/DEFAULT/HIGH_IDLE/DEFAULT_IDLE/LOW)を指定する(例:GLib.Priority.DEFAULT)。
上記関数で指定した(子プロセス終了時に呼ばれる)関数の引数にプロセスID(GLib.Pid型)とともに子プロセスの終了時の状態を示すステータス値(int型)が入ってくる。

public delegate void ChildWatchFunc (Pid pid, int status);

このステータス値は「Vala言語で外部プロセスを実行する(簡単な例・メモ)」で扱ったspawn_command_line_sync()で得られるステータス値と同様に終了ステータス(戻り値)以外の情報も含んでいて、扱い方は同様となる。
ただし、GLib.ChildWatch.add()系関数を用いて子プロセス終了時に呼ばれる関数を指定する形はGLibのメインループ(GTK+のメインループも含む)の中でないと動作しない。
メインループを用いない場合のステータス取得の方法は別記事で扱う。

コマンド行の文字列を解析してstring配列に変換する

実行したいコマンド行が(string配列ではなく)文字列の形で入力される場合、それをGLib.Process.spawn_async_with_pipes()で渡す形のstring配列にするにはGLib.Shell.parse_argv()が便利。シェルなどと同じようにクォートした文字列は1つのまとまりとして処理してくれるのも嬉しい。

関連記事:

参考URL:

使用したバージョン:

  • Vala 0.7.4

*1:0番の文字列が実行ファイル名、1番が1番目の引数、...と続く形だが、C言語とは異なり最後にNULLを指定する必要はない

*2:「[名前]=[値]」という形式の文字列の配列

*3:Windowsの場合はこの部分の挙動が異なるらしいので注意

*4:あるいはGLib.Process.spawn_sync()を呼ぶ方法もあるが、パイプは使用できない