SSブログ

非公式、未踏開発レポート (その1) [Linux]

 未踏云々といっても、書く内容は格別普段と変わるわけではない。いつものように Linux をいじる話をする。
 およそここ 2 ヶ月間、Linux ばかり相手にしてきた。ゼロからカーネルを作るのは格別に楽しいが。既に (ほぼ) 出来上がったカーネルをいじるのもまた楽しい。
 今回は、Linux におけるページキャッシュ関連の話をする。かつてもページキャッシュの話を何回かに分けて話をしたが、あれはページキャッシュの概論に過ぎない。今回話す内容は、限定的なシチュエーションにおけるページキャッシュの振舞い等についての話が主である。

 前回、カーネル空間からファイルオープンするという話をした。
 未踏に関連した話なのだが。現在システムが発行している I/O 要求は、どの (時間) タイミングでどこ (場所) に対してどのような (方向) 処理を行っているのかを知りたかったので、あのような話をした。
 ただし、取得したこれらの情報 (以下、I/O パターン) を保存する為に、Linux が通常利用しているインターフェイスを通してストーレジに保存すると、データにノイズが入り、また別のストレージインターフェイスを作るのも手間なので、klog が行っているように、一端カーネル空間のバッファにデータを保存し、ユーザプログラムによってこれを取得するコールゲートをカーネル側で提供してやり、それをネットワーク経由で別のマシン外に出す事で、純粋な I/O パターンを取得できる。
 この仕組は既に完成しており、Linux 上で滞りなく動作している。作成したソースはこちら。

http://dev.ariel-networks.com/Members/ohyama/stuff/onix.c/download
http://dev.ariel-networks.com/Members/ohyama/stuff/onix.h/download

 onix.c を fs 配下に、onix.h を適当に参照できる場所にそれぞれ置き、arch/xxx/kernel/syscall_table.S に onix.c で定義したサービスルーチン 3 つを登録し、onix_do_writelog() を submit_bh() から呼び出すようにすれば動くはず。もし、試してくれた玄人の中で、そでも動かなかった場合、メールを送ってもらえれば多分対応する。

 作成したカーネルに対して、web サービスに対して不規則にリクエストを投げた時に発生する I/O パターンと、MTA に対してメールを送った時に発生する I/O パターンをそれぞれ取得し、考察してみる。
 結果の一部を次のように示す。ちなみにフォーマットは、
[システム時間 (s)] ["システム時間" からの経過時間 (ns)] [I/O の方向] [LBA モードにおけるセクタ番号]
となっている。

49a04007|31066169|0|184a808
49a04007|31fa8952|0|185004f
49a04007|32749d43|0|1857000
49a04007|32eeb138|0|19a8061
49a04007|3368c52c|0|19b5bf1
49a04007|33a5cf26|0|19a8063
49a04007|33a5cf26|0|19b5da3
49a04007|33e2d920|0|19a8064
49a04007|33e2d920|0|19b5df0
49a04008|9ecf167|0|19b803e
49a04008|a670558|0|19c2358
49a04008|aa40f52|0|19b8061
49a04008|ae1194c|0|19c3808
49a04008|b1e2346|0|19c3809


 機能を絞った限定的な環境で I/O パターンの取得を行ったが、それでも対象の I/O 要求が上位レイヤでどのような抽象構造 (ファイル) に対応した処理なのかがわかれば親切である。
 幸い、大学は春休みであるので時間はたっぷりある。仮の配属をされたばかりの研究室の知人は就職活動に明け暮れているが。いまの御時世、自分が所属しているような三流大学では就職は難しいのではないかと、思ったり思わなかったり。
 などというわけで、暇に任せて、I/O 処理がどのファイルに対する処理なのかを知られる処理を追加した。
 ここでは、バッファヘッドから、バッファヘッドが存在するページを参照し、対象ページがページキャッシュに存在する時、キャッシュを構成しているファイルの名前を表示する。
 ちなみに、有効なページであり、かつページキャッシュに存在していないページは、カーネルのスラブキャッシュに属している物か、プロセスの仮想メモリ空間にマッピングされたものである。後者のページを特に「無名ページ」と呼ぶ。カーネルのコメントにもこれを「anonymous page」と書いている。

 次に、これを調べる為の具体的な実装の話をする。
 まずは、バッファヘッドから、自身が存在しているページを取得する。そして、対象ページがページキャッシュのデータであるか無名ページなのかを調べる。ページディスクリプタの mapping メンバは、ページがページキャッシュに属している場合、アドレス空間オブジェクトを参照する。逆に、無名ページである場合には、プロセスのメモリリージョンの元締めでる anon_vma オブジェクトを参照する。
 今回は、ページを確保しているファイルの名前を調べる仕組みの話なので、無名ページは扱っていないが。メモリリージョンからはプロセスのメモリディスクリプタが参照できる為、これを辿れば I/O を発行したプロセスを調べる事もできる (だろう)。
 さて、ページの所属判定だが。mapping メンバの下位 1 ビットでこれを判定している。具体的には、onix_check_page_mapping() でこれを行っている。これによって、対象ページがページキャッシュに属している事がわかり、所属しているページキャッシュのアドレス空間オブジェクトを取得できる。
 次に、ページキャッシュの所有者。つまりファイルの情報を取得する。ext2 や ext3 においてファイルはディスク上で inode というデータ構造で管理されている。VFS ではこれを inode というデータ構造で一般化している。ここでは、区別の為に前者をディスク inode と呼び、後者を単に inode と呼ぶ。
 inode は、アドレス空間オブジェクトの host メンバから参照ができる。Linux では VFS 上でファイルのパス名情報を管理する為に dentry というデータ構造を用意している。
 この inode と dentry の関係。つまり、ファイルの実体とパス名の関連付けは ext2 の場合、O_CREAT フラグ付でファイルオープンした際に実行される、ファイルオブジェクトの create メソッドの実体である ext2_create() 関数から参照される d_instantiate() 関数で行われる。
 ここでは、inode オブジェクトの i_dentry リストにディレクトリエントリの d_alias を追加している。

 さて。改めて作成したこのカーネルを実際に動かしてみると、アドレス空間オブジェクトが参照する inode が存在しない事がある。
 無名ページでもスワップアウトされたページでもないページで、所有者の無いページ。通常、ページキャッシュの所有者はファイル、即ち inode であり、ここではそれが無いという事になる。
 ファイルをマップしないページキャッシュとは一体何か。これの正体が全くわからず、バイト先企業であるアリエルにいる賢人に相談する。

 賢人に対して「ページキャッシュ中のアドレス空間オブジェクトが inode を参照しないケースがあるが、これは一体どうゆう事なのか」と問うた。
 すると恐るべき事に、賢人はこれだけを聞いて、瞬時に「mmap に ファイルを対象としないマッピング処理が存在する」とそっと答えた。
 自分が無能すぎるのか、賢人が凄すぎるのか。とにかく、凄まじい洞察力である。

 軽くヘコんだ後、オンラインマニュアルをよく読むと、MAP_ANONYMOUS フラグを指定する事でファイルディスクリプタが無視されるらしい。
 実際に、mmap システムコールのサービスルーチンを見ると、ファイルオブジェクトの取得を行わずに do_mmap_pgoff() を呼び出し、メモリリージョンの予約を行っている。この処理の詳細については次回以降に話をする。

nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。