読者です 読者をやめる 読者になる 読者になる

bekkou68の日記

開発しているサービス, IT技術, 英語など。

『Working with Unix Processes』の Processes Can Fork を読みました

Unix 勉強会

はじめに

Processes Have Exit Codes の続きです。

メモ

プロセスはフォークできる。フォークしてつくられた子プロセスは親プロセスの正確なコピーである。したがって500MBのサイズのプロセスをフォークしてできるプロセスのサイズも500MBである。また、親プロセスのファイルディスクリプタはそのまま子プロセスへ引き継がれる。それによってメモリ上では同じところへマップされるので親子でファイルやソケットを共有できる。フォークは複数の同じインスタンスを素早くつくるのに適している。
irb 上で `puts fork` を呼ぶと出力が 2度ある。それらは「親プロセスの出力として子プロセスの pid」と「子プロセスの出力として nil」である。

試したこと

if fork の挙動

次のコードを持つファイル fork_if.rb を用意します。

puts "#{Process.pid}: before if" # 1

if fork
  puts "#{Process.pid}: in if: true" # 2
else
  puts "#{Process.pid}: in if: false" # 3
end

puts "#{Process.pid}: after if" # 4

実行します。

$ ruby fork_if.rb
1701: before if    # parent
1701: in if: true  # parent
1701: after if     # parent
1702: in if: false # child
1702: after if     # child

以下のことが確認できました(コードの数字と対応します)。

  1. fork する前なので親プロセスでしか呼ばれない
  2. 親プロセスで true の処理が呼ばれる。親プロセスで fork の結果は作成した子プロセスの pid が返るため
  3. 子プロセスで false の処理が呼ばれる。子プロセスで fork の結果は nil が返るため
  4. if のあとの処理は親子両方のプロセスから呼ばれる
forkブロックの挙動

次のコードを持つファイル fork_block.rb を用意します。

puts "#{Process.pid}: before fork block" # 1

fork do
  puts "#{Process.pid}: in fork block" # 2
end

puts "#{Process.pid}: after fork block" # 3

実行します。

$ ruby fork_block.rb
1588: before fork block # parent
1588: after fork block  # parent
1589: in fork block     # child

以下のことが確認できました(コードの数字と対応します)。

  1. fork する前なので親プロセスでしか呼ばれない
  2. forkブロック内の処理は子プロセスでしか呼ばれない
  3. forkブロック後の処理は親プロセスでしか呼ばれない

わからなかった単語・熟語

  • Up until now: 今まで
  • mind-bending: ドキッとする。mind を bend する
  • consequence: 因果関係。文章中では、「こうなることを知っておこうね」というニュアンスで使われていると思う
  • concurrency: 並列
解釈に時間のかかった文章

英語の勉強がてら、この一文を読み解きます。

A call to the fork method has taken the once-familiar if construct and turned it on its head.

文章をパーツにわけると、"A call to the fork method" が主語で、"has taken the once-familiar if construct" と "turned it on its head" がそれぞれ動詞と目的語の組み合わせですかね。
それぞれを日本語訳します。

  • "A call to the fork method" → forkメソッドの呼び出し
  • "has taken the once-familiar if construct" → かつて親しみやすかった if文
    • once-familiar で「かつて親しみのあった」。once-xxx は「かつてxxxであった」と訳せるようです
    • if construct は「if文」。最初英語としての if だと思い込んで読んでいたら ??? となりましたw 斜体などにしていただきたい><
  • "turned it on its head" → if文の常識をくつがえした
    • turn xxx on its head で「xxx を覆す」

となるので、全体としては「forkメソッドの呼び出しは、今までなじみ深かった if文の常識をくつがえしてしまった。」と解釈できそうです。歯ごたえのある文章でした。

感想

いままで fork は「何となく新しいプロセスが生まれる」という理解だったのですが、そういうことなのか! と思いました。プロセスの正確なコピーができるのですね。また、本文中では プロセスを fork すると親子プロセスあわせて使用するメモリはシンプルに2倍になると書かれていました。が、メモリを2倍消費してしまう単純なコピーでなく、同じデータなら同じアドレスを指し、別のデータを指すようになったらそこだけメモリを新しくとることでメモリを節約する copy-on-write という機能があると教えてもらいました。
"フォークは複数の同じインスタンスを素早くつくるのに適している" の具体例としては、同じ Rails プロセスを 3つつくる例でメンバーに教えてもらったのが分かりやすかったです。Rails プロセスをたちあげて fork を 2回呼ぶのと、Rails プロセスをつくるコマンドを 3回たたいて環境のロードするのとでは、前者のほうが早い。というお話です。
勉強会で concurrent(並行)と parallel(並列)の違いが話題にあがったのですがよくわからず。そのうち勉強したい。
前章に引き続き、実際に fork がどう活用されているのかはおあずけですね。楽しみがつのります。だんだん内容が難しくなってきたので丁寧に学んでいきたいです。深く理解できればかなりの力がつきそうな予感がします。