SSブログ

GNU/Linux システムにおけるブロックデバイスに対する I/O 要求 + 他 [Linux]

○ 概要

 ブロックデバイスに対する I/O リクエストは、通常ファイルシステムのインターフェイスを介して発行される。ここでは、GNU/Linux (以下、Linux) において低レベルファイルシステムが提供するインターフェイスを用いずに行う、ブロック I/O リクエストの仕組みを紹介する。


○ はじめに

 ユーザプログラムから、ファイルシステムを用いずにデバイスファイルに対する I/O をプログラマブルに行う事は非常に簡単である。これは、Linux カーネルがデバイスファイルという形で、ファイルシステムが扱えるように構造を抽象化しているからである。逆を言うと、VFS のインターフェイスはユーザによって操作される事を前提としている為、カーネルの都合でデバイスを扱う為には、話が複雑になる。前回書いた記事にも共通する事が言える。
 ここでの目的は、ファイルシステムの力を借りずに、カーネルに組み込んだ自前の処理関数によってブロック I/O を発行する事である。ファイルシステムの力を借りる事ができない理由は、上述したように処理の発信元がカーネル側にある事に起因する。
 ブロックデバイスに対する I/O 処理は、一般的に Linux カーネルが提供するデバイスファイルを通して行われる。デバイスファイルに対するファイル操作処理は VFS インターフェイスを通し、bdev 特殊ファイルシステムによって処理される。


○ Linux カーネルによるブロック I/O

 まずは、システム起動後からブロックデバイス初期化までの大まかな流れを追い、処理の範囲を絞りこむ。
 システムを起動してから、デバイスをマウントし、ファイルシステムから参照されるまでの流れはおおよそ次のようになっている。
 システム起動後、Linux カーネルは PCI デバイスを捜査し、k オブジェクトのツリーを生成する。同時にブロックデバイスに対して block_device オブジェクトを生成し、all_bdevs が参照するリストに登録される。そして、デバイスファイルという形でデバイス情報がファイルシステムに対して提供される。最後に、/etc/fstab が参照され、各デバイスが指定されたファイルシステムでマウント処理される。
 ここでのミソは 2 つ。各デバイスは Linux によって、デバイスファイルという形で提供されるという事と、マウント処理されてはじめて VFS からデバイスに対してデータを読み書きする事ができるという事。

 次に、このデバイスファイルに対するファイルオープンの処理の流れと内部的な処理について見てゆく。
 デバイスファイルに対して open システムコールが発行されると、パス名検索を行った後、生成した nameidata オブジェクトを nameidata_to_filp() 関数に渡す。同関数では、パス名検索の結果取得した nameidata オブジェクトから対象ファイルのディレクトリエントリの情報を取り出し、ここから __dentry_open() を呼び出す。
 ここまでの処理は、対象ファイルの inode 情報を取り出す常套手段で、各種ファイルシステム共通の処理になる。ここから、ファイルシステムのファイル操作の共通インターフェイスを定義したデータ構造 file_operations オブジェクトを取得し、open() メソッドを呼び出す。マウントされていないブロックデバイスは、bdev 特殊ファイルシステムによって定義されるスーパーブロックを持ち、デバイスファイルに対する読み書きによって叩かれるインターフェイスの実態は、この bdev ファイルシステムが定義する処理にある。これらの処理は fs/blocks.c で定義される。

 重要なのは、どのように Linux がブロック I/O を発行するかという事である。
 Linux はファイルシステムからの I/O 処理の要求を submit_bio() という関数で一手に引き受けている。ここに処理の対象となるブロックデバイスのデータ構造 block_device オブジェクトと bio オブジェクト 及び I/O の範囲を個別に指定した bio_vec オブジェクトのリンクを渡す事でブロック I/O が発行される。
 まずは、処理の対象となるブロックデバイスの block_device オブジェクトの取得方法について。

 bdev ファイルシステムのスーパーブロックが提供する inode オペレーションの .alloc_inode() メソッドの本体は bdev_alloc_inode() [fs/block_dev.c] で定義される。ここでは、bdev_inode オブジェクトを生成し、inode メンバを返している。bdev_inode のデータ構造は struct inode * の他に、struct block_device * を持つ。これは、bdev ファイルシステムで inode を作成した時に、block_device も同時に作成される。
 なので、デバイスファイルの inode さえ取得できれば、オブジェクトのメンバのアドレスからオブジェクト本体のアドレスを逆算する container_of() などのマクロによって block_device オブジェクトを取得、またはその逆ができる。これらの操作を BDEV_I() や I_BDEV() マクロによって内部的に行っている。
 Linux は上述したような処理を行う関数を外部に対して提供している。それが lookup_bdev() になる。

 ブロックデバイスの操作が submit_bio() にて行われているわけだから、block_device オブジェクトを取得したら、bio オブジェクトを生成・初期化して submit_bio() を呼び出せば I/O 操作を行ってくれるように思うかもしれないが、そうは問屋が卸さない。Linux のブロックデバイスサブシステムに対して I/O 要求を発行する際にディスクを表すデータ構造の gendisk オブジェクトを参照し request_queue オブジェクトを生成して、デバイスドライバに処理を委託する。
 通常、デバイスのマウント時に gendisk オブジェクトは生成されるが、特定のファイルシステムを持たないこの状況で submit_bio() を呼び出すと、generic_make_request() 関数で無効なアドレスを参照して例外ハンドラが呼び出されるか、予測不可能な処理が起こる。
 この段階では、デバイスドライバの初期化が行われているだけで、ディスクオブジェクトなどの I/O ブロック層におけるオブジェクト郡は生成されていないようである。
 この為、対象デバイスに対するブロックデバイスサブシステムに関連するデータ構造の生成・初期化も自前で行わなければならない。これを行う為に、デバイスファイルに対してカーネル側でファイルオープン処理を行う。デバイスファイルに対して、ファイルオープンを行うと、対象デバイスの bdev に関する I/O ブロック層の各種オブジェクトを生成してくれる。
 そして、bio オブジェクトを生成し、書き込みを行う場所 (bi_sector) 等の設定を行う。又、block I/O を行う際に指定するバッファ領域としてページディスクリプタを指定する。なので、kmalloc() などによるスラブキャッシュからバッファ領域を取得するのではなく、直接ページアロケータにページの取得要求を出す。ページの取得等のページ操作については、ソースを見てもらえれば分かると思う。


○ サンプルプログラムソース

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


○ 注意

 多分誰もいないと思うが。ユーザ権限でデバイスのファイルシステム壊すことが出来るので、これを使う際には、間違っても重要なデータが入っているデバイスに対して行うべきではない。どうでもいい USB メモリか何かで試して貰いたい。



【義憤にかられた瞬間:とある大学教授のお話】

 新たな年度が始まり、今年度から学部 4 年となった。この時期は、やれ就活だ、研究だ、進学だと騒がしく。特に年度始めはそれが顕著である。
 今月始め、同学年の学生を集めたガイダンスが開かれた。

 しばらく事務的な話が続いた。窓に映る桜並木と春の暖かさが眠気を呼び寄せ、気がつくと教室は静まり、壇上には見慣れた偉い教授が立っていた。そして就活に関する話をはじめられた。
 そこでは、昨年度の就職実績 (?) や、おもな就職先の企業の話をした後、大学院への進学の話に移った。話をはじめてから 5 分程度の時間が経過し、ようやくエンジンがかかってきたのか声に熱が入りはじめ、「就職するなら大企業かベンチャーか」という話になった。

 教授は「まぁ、いろいろな考えを持った方がおられるでしょうが。」と前置きをした後、「絶対。大企業の方がいいです。」と言い切った。
 相手は学者なので、ここから話の根拠が展開される。教授は始めにこう結論づけた。「なんといっても、生涯賃金が違います。」
 ここまでは、よく耳にする話である。話を聞いている周りの学生達は退屈そうな目で教授を見ていたに違いない。
 しかし教授の話はここで終わらない。教授は次にこう言い放った。「昨今、中央省庁の官僚の天下りが問題視されています。ですが、民間企業では当り前の事です。」そして、具体的な企業名と役職を挙げ、過去に子会社の社長やら重役やらに就任した話を繰り広げ、生涯設計を検討するなら大企業に入る事はひとつの大きな鍵になると結論づけた。
 
 教授が仰りたかった事は、つまるところ『大企業の本体に入れば、生涯賃金は一般的に中小企業よりも高いし、定年過ぎても天下りやらを行え、人生安泰』という事であろう。

 これには、驚いた。自分自身、この問に関する答えは持ち合わせていないし、その行為自体が正しいか間違っているかを述べるだけの根拠を持っていないし、個別のケースに対して意見できる程事情も知らない。しかし、自分はその教授の結論づけ方に憤りを覚えた。「これからスタートする人間に対し、リタイヤメントの話をここですべきなのか」と。
 自分が今まで聞いた、「大企業とベンチャー」の話でもっとも酷い結論であり、もっとも言ってはいけない人間に対して言ったなと思った。

 4,50 代の人間が、酒場で話すネタならまだしも。教室に集められたのは 20 歳前半の若者であり。そこで、曇りきった眼をしたオッサンが「人生の逃げ」について説いたわけである。
 日本の癌細胞が細胞分裂を行う瞬間を目撃したような気分である。
 自己の利益の為に組織を利用する、ある種の老害的な行為を是とする人間が、若者に対して影響を及ぼすポジションに立っている事が非常に悲しい。

 っといった内容をその偉い教授に対して言いたい気持ちをグッと堪え。大人の階段を一歩登った気になる。

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

nice! 0

コメント 0

コメントを書く

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

トラックバック 0

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