「なるほどUnixプロセス ― Rubyで学ぶUnixの基礎」を読んだ

良い本だった。Ruby関係ない人も、Unix的な仕組みの理解につながると思う。

この本の内容でそれほど触れているわけでは無いが、普段なにげなく叩いているコマンド実行時の「パイプライン」。
これの理解がコマンド同士の入力、出力をつなげるくらいしか理解できてなかったが、この本読んだきっかけで実際どういうう動きになってるのか多少整理できた。


たとえば下記のようなコマンドを入力した時、どういう挙動でデータの受け渡しがなされるのか。


ls -l|grep hoge

■実行された時の流れ

1.パイプ用のファイル(ディスクリプタ)を作る

「ls -l」から「grep hoge」への標準出力を渡すため、パイプ用のファイルを作る。
ファイルとは言ってもディスク上に書き込まれるものではなくメモリ上でのただの識別子(ファイルディスクリプタ
メモリ上なので、基本的には高速なやりとりが期待できる。

2.コマンド実行用のプロセスをそれぞれのコマンドごとに立ち上げる(fork)

コマンドの実行は、それぞれ1つのプロセス内で行われる。
「ls -l」 と「grep hoge」を実行するためのプロセスをそれぞれfork(2)する
(fork(2)実行時、作ったパイプのファイルディスクリプタdup(複製)して渡す。パイプにはread,writeそれぞれの口があるので、それが親と子プロセスどちらかになるように片方を閉じる。そうしないと、書き込み、読み込み処理の終了判定が壊れる)

3.パイプでのやりとりのために、出力を置き換える

それぞれのコマンドの標準入力、標準出力の参照を、パイプに置き換える。
forkしてできあがったプロセスは、それぞれexec(2)して「ls -l」 、「grep hoge」に姿を変える(exec(2)はプロセスを指定の実行コマンドに完全に置き換える )

4.実行される

それぞれのコマンドは単体で動作する時と同じように、標準入力、標準出力を使っての挙動をとる
書き込み、読み込みは随時行われる(tail -f file |grep hogeってやっても随時結果が出力されていくのはこのため)

このwikiの「Unixにおけるforkの重要性」、わかりやすいな。
http://ja.wikipedia.org/wiki/Fork

■割り込み時された処理(Ctrl+cでの中断など)

また例としてこのコマンドで。


ls -l|grep hoge

このコマンド実行時、例えば何かシグナル(例えばCtrl+c)などを送ると、「ls -al」と「grep hoge」の実行プロセスはどちらも終了される。
これは、「ls -al」と「grep hoge」のプロセス両方にシグナルが転送されたから。
なぜシグナルが転送されるかというと、このパイプラインでつながれたそれぞれのプロセス(コマンド)が、同じ「プロセスグループ」となるから。

また、「プロセスグループ」のさらに上に「セッショングループ」というのが存在する。
さらにこのセッショングループには「セッションリーダー」が存在し、コマンド実行時には基本的にログインシェルとなりそう。
なので、ログインシェルが終了し「セッションリーダー」がいなくなった時、そのログインシェルの「セッショングループ」内の全てのプロセスグループにもシグナルが転送され、同じように終了する事ができる。