Mac関連ネタをそれはもう凄まじい勢いで翻訳するスレ7

このエントリーをはてなブックマークに追加
54名称未設定
FSEvents

昔むかし、BeOS と呼ばれる OS があった。衝撃的かつ革新的な設計の OS だ。
しかし、その大胆不敵なアイディアがすべて期待どおりになったわけではなく、
うまくいった部分だけでは、滅亡をもたらす非技術的要因の力から自らを
救い出すことができなかった。だが、BeOS は OS 業界に多大な影響を残した。
特に、そのファイルシステムや関連インタフェイスには四つの特筆すべき特徴があった。

ジャーナリング、
任意かつ拡張可能なメタデータ、
非同期通知機能、
メタデータの自動インデクスと、統合検索エンジン

こうした、連携して当時のいかなる PC OS とも異なるユーザエクスペリエンスを
もたらす機能である。特に Mac ユーザは、こうした機能を見てその価値を理解し、
自分の愛する OS にも欲しいと思ったものだ - BeOS の出荷が延期されたことを
知らなかったとすれば。

(Practical File System Design with the Be File System という本に、
これらの機能の歴史と実装が説明されている。フリー PDF もある。
http://www.nobius.org/~dbg/practical-file-system-design.pdf)
55名称未設定:2007/12/24(月) 20:20:31 ID:YwinN/gg0
1997 年、Apple は Be ではなく NeXT を買収し、NEXTSTEP OS をもとに
Mac OS X を作り出した。当初 Mac OS X には上記 BeOS ファイルシステム機能の
いずれもなかった。追加するようにという内外からの圧力には、NeXT/Unix 哲学に
慣れ親しんだエンジニアからの抵抗があり、そのようにして Mac OS X ファイル
システムの将来に関する、多年に渡る抗争(Mac 野郎 vs NeXT さん) が始まったのだ。

ある時点で (と伝説は語る……) Apple 内部の Mac 野郎たちが「勝ち」、
Apple は新たな道を歩み始めた。しかし Mac OS X ほど大きな船の舵取りには
長い時が必要となるものだ。傍観者の観点からすると、Mac OS X における
ファイルシステム技術の歴史は、NeXT さんたちから「非効率的」や「無駄」として
酷評された上記 BeOS ファイルシステムの機能をひとつひとつ実装してゆく
6 年の苦闘になぞらえることができる。

ジャーナリングは HFS+ に追加された;
Spotlight はメタデータ自動インデクスと統合検索エンジンをもたらした;
新しい拡張属性 API 群は任意に拡張できるメタデータを。
そして今、Leopard で、最後の一片が到来した: FSEvents フレームワーク
という形の、非同期ファイルシステム通知 API である。
56名称未設定:2007/12/24(月) 20:21:33 ID:YwinN/gg0
* ファイルシステムイベント・デジャヴュ *

技術オタクな Mac ユーザは、そのような API が Tiger に存在していたことに
気付くだろう; それこそが Spotlight を可能にしたものなのだと。
その /dev/fsevents 機構は、カーネルを通るあらゆるファイル i/o を追跡し、
関心を持つクライエントに通知する。これにより Spotlight エンジンは、自分で
わざわざ調査しなくても新規作成あるいは変更されたファイルをインデクスする
ことができるようになった。(調査 [polling] とは、何かが変更されなかったか
どうかを繰り返し尋ねる行為を指す。これは極度に非効率的で、ファイルシステム
全体にわたる変更を検出する方法としてはまったく使いものにならない。)

/dev/fsevents API はプライベートだった - だからといって生産的なハッカーの
おもちゃにならずに済むわけではなかったが。プライベートだったのには、
ちゃんとした理由がある。それはファイルシステム通知の仕組みと関係がある。

必要なファイルシステム変更をすべて知るために、通知機構は
ローカル i/o すべての通る隘路に存在する必要がある: カーネルだ。
しかしカーネルは厳格な時間およびメモリ制限で満ちた、気難しい女将である。
理想を言えば /dev/fsevents カーネルコードは各イベントをクライエントに
渡してからできるだけ早く次に進みたい。

57名称未設定:2007/12/24(月) 20:22:20 ID:YwinN/gg0
ユーザスペースに戻ると、物事はもっとずっと気楽に扱われている。
通知待ちしていたプロセスは、いざイベントが来たときには別のことをしている
かもしれないのだ。これはユーザスペースでは仕方のないことだが、カーネルの
「今すぐ」最低限のメモリと CPU オーバヘッドでやらなきゃいけない状況とは
まったく相容れない。

たとえばこんなとき、カーネルはどうすれば良いのだろう。
2 秒間に 10000 個の変更が (ほら、ソフトウェアのインストールとかで) 起こった
のに、馬鹿でノロマなユーザスペースプロセスは他のことに手一杯でここ 3 秒間の
通知イベントをひとつもキューから引き出していない、とかいうときだ。

ああ、もちろんイベントをバッファする必要がある。つまり、保存しておいて
関心を持つ「すべての」クライエントが受け取りに来るまで待つのだ。
しかしバッファは無限ではない。カーネルでは特にそうだ。
では、バッファが一杯になってしまったら何が起こるか。

58名称未設定:2007/12/24(月) 20:23:05 ID:YwinN/gg0
そう、また使えるようになるまでブロックするかもしれない。
しかし考えてみよう、もしクライエントがキューの余地不足によりファイルシステム
操作でブロックされており、しかし通知を読んでバッファを空ける必要のある
別のクライエントは、その最初のクライエントのせいでブロックされているとしたら
どうなるか! やあ、デッドロックのお出ましだ。

残る唯一の手段は動的にメモリを割り当てることだが、それも問題の先送りに過ぎない。
単純に言うと、バッファできるイベントを制限して見落としを容認するか、
無尽蔵なメモリ使用に身を捧げるかのいずれかなのだ。

Apple は前者を選んだ。カーネルバッファは固定長で、遅いクライエントのために
一杯になれば、イベントは捨てられる。これは、お行儀の悪いクライエントがひとり
いると皆に迷惑がかかることを意味する。

だから、そう、/dev/fsevents はパブリック API にふさわしい候補ではない。
それでも効率的な非同期通知の必要性は消えない。どうするか。Leopard の
FSEvents フレームワークに入ろう。こいつは実用的なアプローチをとっている。

59名称未設定:2007/12/24(月) 20:52:10 ID:YwinN/gg0
これは Leopard の新技術全般にわたって何度も出てくるテーマだが、
技術的な難題に対して FSEvents も「あらゆる人に、あらゆるもの」となることを
目指してはいない。そうではなく、視点をソツなく狭め、可能性の高いところに
注意を集中するのだ。FSEvents は全方位の「完全無欠」な解を出すよりはむしろ、
「80 パーセントの解答」を (ほぼ) 100 パーセントの信頼性で出してくれる。


* FSEvents の設計と実装 *

FSEvents の設計で主要なブレイクスルーとなるその点は、/dev/fsevents にある
もうひとつの弱点を考慮することにより達成されたように思える。
プライベートな /dev/fsevents API は、通知をリアルタイムにクライエントへ
配給する。これは API の最良の機能に見えるが、実際にはクライエントにとって
非常に重荷である。クライエントが動作していないときに起こったイベントは
見ることができないのだ。これこそ Spotlight のインデクスプロセスがシステム
ブート時に起動されて動き続ける理由だ。ファイルシステムイベントをすべて
受け取って処理するにはこうする必要があるのだ。

もし他に全ファイルシステムイベントを観察したいプログラムがあるとすれば
同じことをする必要があるだろう: ブート時に起動し、永遠に動作し続けるのだ。
おっと、その上ぜったいにクラッシュしないこと。なぜなら瞬時に再起動したところで
落ちている間のイベントは見落としてしまうかもしれない; /dev/fsevents は
プロセスを待ってくれないのだ。

60名称未設定:2007/12/24(月) 20:53:07 ID:YwinN/gg0
では、この認識から FSEvents の設計がどのように導き出されたのか。その答えは、
常駐クライエント問題を解決することが他の多くの問題も消し去る、というものだ。
FSEvents がどう解決するかは以下のとおり。

/dev/fsevents API は非常に行儀の良い少数のクライエントだけをサポートする。
Spotlight はそのひとつである。Leopard では FSEvents もそうだ。
FSEvents フレームワークは、単一の常駐デーモンプロセス fseventsd に頼って
おり、そのデーモンは /dev/fsevents を読んで、イベントをディスク上の
ログファイル (複数。イベントが該当するボリュームのルート上にある .fseventsd
ディレクトリ内) に書き込む。おしまい。なんとも超ハイテクな解決ではないか:
イベントをログファイルに書くだけとは。退屈、現実的、しかし非常に効率的である。

FSEvents API を使いたいと思っているプログラムは常に動作している必要が「ない」。
好きなときに起動して聞けばいい、「よーし、前回から何か変わったかい?」と。
ログファイルのどこで自分がいなくなったかを知っている限り、FSEvents フレーム
ワークが (高速に) そこまで「巻き戻し」して正確に答えてくれる。

61名称未設定:2007/12/24(月) 20:53:51 ID:YwinN/gg0
現実的だって? こんな方法には「複雑に絡みあった問題がうようよしている」と
言ってもよさそうじゃないか。ログファイルはどれだけ大きくなるのか。
いつもファイルを作ったり編集したり消したりしていたらディスクが一杯になって
しまうのでは? ログファイルは古いほうから消えていくのか。一年間動いて
いなかったプロセスがその間の変更を知りたがったらどうなるのか。

現実的であることは妥協を意味する。そう、fseventsd が /dev/fsevents の
集中砲火を浴びたとき、逐一そのイベントを書き込んでいればディスク容量は
すぐさま足りなくなるだろう。そうならないために fseventsd はもっと細粒度を
下げて、ディレクトリレベルでのみ変更を記録する。そこで FSEvents フレーム
ワークは、ただ「/hoge/dir で何か変更されたみたいです」としか通知できない。

クライエントは変更のあったディレクトリを自分でスキャンして、正確に何が
起きたのかを (そこまで興味があるとすれば) 見極めることになっている。
一般的なパターンは、ファイルシステムツリーの一部からの通知に登録しておき、
最初のスキャンをし、特定のディレクトリからのイベントを待ち、それが来たら
最新の状態と前回のスキャン時の状態とを比較するというものだ。

62名称未設定:2007/12/24(月) 20:54:41 ID:YwinN/gg0
なんとも面倒な仕事のように思えるはずだ: 登録、スキャン、イベントを受け取る、
またスキャン、そして比較。この同じコードが FSEvents クライエントプログラムの
それぞれに必要であり、プログラマが不注意であれば競合状態が潜んでいる。
現実的であることには対価が伴うのだ。

しかし報いもまた大きい。もうデーモンプロセスは要らない; いつ起動しても、
前回からの間に何が変更されたか分かるのだ。行儀の悪いクライエントのせいで
イベントを落とす危険もない。好きなだけゆっくり読むがいい。ハング、クラッシュ、
再起動するがいい: 大丈夫、ひとつも見落とすことはない。
時間を遡って昔のイベントを見ることさえできる。

/dev/fsevents を含め、カーネルベースのあらゆるファイルシステム通知機構に
言えることだが、カーネルを通さないファイルシステムの変更も生じ得る。
たとえばリムーバブルディスクは別の非 Leopard コンピュータにつないで
変更されるかもしれない。元に戻したとき、カーネルとしては何が変わったか
まったくわからない。

FSEvents API にはこうした状況のためのコールバックがあり、効率よく
クライエントに通知することができる。「不明な変更がありますので、ご自分で
完全スキャンし直して、新しいイベントストリームの中から選んでください。」
そんなことを聞きたいのではない、とプログラムは言うかもしれないが、これが
現実だ。そのあたり FSEvents は正直なのだ。事実上、これも一種の信頼性と
言えよう。FSEvents はウソはつかないのだ。

63名称未設定:2007/12/24(月) 20:55:19 ID:YwinN/gg0
fseventsd ログファイルは圧縮バイナリ形式で書かれている。ディレクトリ単位の
変更しか保管されていないので、同一ディレクトリへ 30 秒以内に起きた複数の
変更は、ログファイル上でひとつのイベントに合体している。その結果はというと、
ディスク使いの荒いサーバ型の利用形態で 24 時間ぶっとおしだとしても、
fseventsd ログファイルは一日に 1、2 メガバイトしか増加しない。
通常の利用法ではほんのわずかであろう。

そりゃいいや、だってログファイルは「永遠に」保存されるんだから。
……うーん、まあ、可能な限りってことだけどね。FSEvents は、単調増加する
64 ビットのイベントカウンタを使う。何か悪意あるハックで数字を飛ばされたり
しない限り、これが一生のうちに一周することはない。ただ、もしも
一周してしまったり、ディスク容量が足りなくなったり、ログが明示的に
削除されたり (そのためのパブリック API がある) すれば、FSEvents は忠実に
バッドニュースを広める: 「申し訳ありません、完全スキャンの時間です。」

イベントは 64 ビットのイベント id で識別されるが、id は必ずしも日付や時刻と
一対一の関係があるわけではない。とはいえ FSEvents には、特定の日付および時刻に
対応する「おおよその」イベント id を尋ねる機能も付いている。

特定のボリュームに対する変更をまったく記録しないようにするには、no_log という
名前のファイルを .fseventsd ディレクトリに作ればいい。
なお、言うまでもないことだが FSEvents は Mac OS X のアクセス制御ルールに従う;
読み出し権限のないディレクトリに関わるイベントを受け取ることはできない。