RubyプログラムはUnixシステム上の1プロセスとして動いている。 プロセスは新たに生成したり,生成したものに信号を送ったりなどの 操作ができる。
Unixプロセスは1つのプログラムが活動している状態で, 状態を示す値がいくつかある,プログラミングをする上で 知っておくべき代表的なものが以下のものである。
各プロセスに振られる固有の整数
そのプロセスを生成したプロセスのプロセスID
プロセスの動作権限となるユーザID
プロセスの動作権限となるグループID
プロセスのその時点の作業ディレクトリ
入出力を結びつけられた端末
Rubyプログラムのシステムプロセスとしての情報は Process モジュールを介して得たり設定したりできる。以下のプログラムは 起動したプログラムのPID, UID, GIDを出力する。
#!/usr/bin/env ruby # coding: euc-jp printf("pid=%d, UID=%d, GID=%d\n", Process.pid, Process.uid, Process.gid)
Unixシステムは起動時に /sbin/init
プロセスが起動され,
残りのプロセスは全て init
の子として起動される。
プロセスの生成は以下の2つの機構を組み合わせて行なわれる。
fork
現在の自己プロセスのコピーを作り,プログラムの同じ場所から 続けて動作する
exec
現在の自己プロセスの情報を全て引き継ぐ外部プログラムに 実行を移す
一般的に,1つのプログラムの制御下で別のプログラムを起動するには
forkとexecを組み合わせて行なう。forkはシステムコールで,
呼出しと同時にプロセスの複製が作られる。呼出し元となったプロセスには
子プロセスのPIDが返されるが,子プロセスには0(Rubyプログラムの場合
nil
)が返る。
#!/usr/bin/env ruby # coding: euc-jp cmd = "kterm" if (pid = fork()) then # 親プロセスのみ必要な処理はここ else # 子プロセスのみの処理はここ exec(cmd) end STDERR.printf("%s について: 終了を待つ=w killする=k 放置=その他: ", cmd) case gets when /^k/i STDERR.puts "ボシュっ" Process.kill(:KILL, pid) when /^w/i STDERR.puts "じゃ,待ちます。" Process.wait else STDERR.puts "じゃ,わしゃ勝手に終わります。さいなら。" end
exec
は,プログラムの起動に失敗すると
例外を発生させる。実際のプログラムでは外部プログラムの実行が
失敗する可能性も考えなければならない。そのときの処理は
スレッドで解説する。
上記の例題プログラムにある Process.kill
は,
あるプロセスにシグナルを送るためのメソッドである。
シグナルとはUnixシステムで走行中のプロセスに対し,「何か」が起こったことを
非同期に通知するための仕組みで,
どんなプロセスも走行中,自分自身に送られるシグナルを即時に受け取り
それに応じた処理に移ることも,無視することもできる。
「それに応じた処理」は,シグナルを受け取ったときに自動的に呼ばれる
ルーチンを登録することで行なわせる。自動的に呼ばれる部分を
シグナルハンドラという。
シグナルはシステムによって若干異なるが数十種類のものが 存在する。日常的プログラミングで利用する主なものには以下のものがある。
シグナル名 | 意味 |
---|---|
HUP | Hangup 端末の回線が切断されたとき。 ktermなど親となる仮想端末が終了したときも該当。 |
INT | Interrupt 割り込みキー(C-c)を タイプしたとき。 |
QUIT | 終了キー(C-\)をタイプしたとき。 |
KILL | 強制終了(捕捉できない)。 |
SEGV | Segmantation violation セグメンテーション 違反(書き込み禁止メモリへの書き込みなど)。 |
ALRM | Alarm 設定した制限時間が経過したときに 自動的に送られる。 |
TERM | Terminate killコマンドによる 強制終了(捕捉できる)。 |
STOP | 端末以外からの実行中断(捕捉できない) |
TSTP | 端末からの実行中断(C-z) |
CONT | 中断からの復帰 |
Rubyプログラムから別のプロセスにシグナルを送るには
Process.kill
に
シグナル名の前にコロン(:
)を付けたシンボルと,
シグナル送信先のプロセスIDを指定する。
プログラムにシグナルが送られた場合のデフォルトの処理は
シグナルごとに決まっている。たとえば,SIGINTが送られた場合は
プログラムが中断させられる。これを別のものに変える場合は
シグナルハンドラの登録を行なう。このためには Signal.trap()
を用いる。
Signal.trap(:シグナル, ハンドラ)
ハンドラ として nil
を指定すると
シグナルを無視する。"DEFAULT"
を指定すると
独自のハンドラ登録を解除する。次のプログラムは C-c を
押されて SIGINT が送られたときの処理を変えるものである。
#!/usr/bin/env ruby # coding: euc-jp def bye() Signal.trap(:INT, nil) # ここでのSIGINTは無視 STDERR.puts "そんなー。" sleep 1 STDERR.puts "でもサヨナラ" exit 1 end Signal.trap(:INT, "bye") 10.downto(1) do |i| STDERR.printf("%d..", i) sleep 1 end STDERR.puts "0 おしまい!"
プログラムの実行時に一時ファイルを作成しているようなものは C-c で止められたときに,一時ファイルを消去するなどの 必要な後始末を行なってからexitするようにする。