UNIX 6th code reading - unix/iget.c

はじめに

今回はunix/iget.cを見ていきます。

iget.cはコア中のinode[ ]リソース管理が主です。他に、新たなinodeの生成(ファイルの生成)や、ディレクトリへのエントリ追加に関する処理などがあります。

コア中のinode[ ]

unix/inode.hの中でinode構造体の配列が宣言されています(5659行目)。カーネルでinodeを扱うときは、このinode[ ]の要素(へのポインタ)を使います。

unix/ino.hでも同名のinodeという構造体が宣言されていますが(5605行目)、こちらはカーネル内では使用しません。こちらのinode構造体はブロックデバイス中のinode領域のinodeのデータ形式を表しています。UNIX v6のファイルシステムをハックするときなどは、こちらを参考にするといいでしょう。

inode[ ]のエントリが未割当てであるかどうかは、i_modeが0かどうかでチェックします。

iget( )

iget( )(7276行目)は、inode[ ]配列を探索し、使用されていないエントリを探し出し、引数で渡されたdev(デバイス種)とino(inode number)を使って名前をつけ、そのエントリへのポインタを返します。
また、ブロックデバイス中のinode情報が読み出され、inode[ ]の該当エントリにコピーされます。

inode[ ]の中に、引数で渡されたdev, inoで名前付けされているエントリがいたら、参照カウンタをインクリメントしてから、そのエントリへのポインタを返します。ブロックデバイスを読み込みに行かないので、その分処理が速くなります。キャッシュの役割を果たしています。

以前のエントリでも触れましたが、該当inodeエントリのIMOUNTフラグが立っていた場合、mount[ ]を探査してdev(デバイス種)を取得し、そのマウントされたデバイスのルートディレクトリinodeを返します。
これにより、異なるデバイスを同一パス空間としてみなすことが可能になります。

前回解説したialloc( )では、ブロックデバイス中のinodeフリーリストから未割り当てinodeのi_numberを取得した後にiget( )を呼び出し、コア中のinodeリソース管理を担うinode[ ]のエントリを取得しているのが確認できます。

iput( )

iput( )(7344行目)はinodeの参照カウンタをデクリメントします。参照カウンタが0になり、かつ、どのディレクトリからも見られなくなる場合、inodeの解放処理を行います。

inodeの解放処理は以下です。参照カウンタは0になるけれどディレクトリからまだ見られている場合は、上三つは実行されません。

  • itrunc( )を呼んで使用しているデータ領域のblock numberをフリーリストに戻し、かつ、inodeが示すファイルサイズi_size0, 1とデータ領域のblock numberを指すi_addr[ ]を0クリア
  • i_modeを0に
  • ifree( )を呼んで、inodeのi_numberをフリーリストへ戻す
  • iupdate( )を呼んで、inodeの更新情報をブロックデバイスに反映
  • i_flagとi_numberを0クリア


i_nlinkがデクリメントされるのはunlinkシステムコールが呼ばれたときです。つまりそのファイルをrmなどで削除する場合です。

itrunc( )

itrunc( )(7414行目)は引数で与えられたinodeが使用しているデータ領域のblokc numberをフリーリストに戻します。そしてinodeのi_size0, 1を0にし、i_addr[ ]も全て0にします。つまりファイルサイズが0になります。

ILARGフラグが立っていて、間接参照を使用している場合は、間接blockも辿って全てフリーリストに戻します。

ファイルデータ自体はブロックデバイスのデータ領域に残ったままなことに注意してください。(参考)

iupdate( )

iupdate( )(7374行目)は、引数で与えられたinodeの更新フラグが立っていた場合ブロックデバイスへ更新情報を反映させます。

maknode( )

maknode( )(7455行目)は、新しいinodeを生成します(新しいファイルを生成します)。

ialloc( )を呼んで、フリーリストから未割り当てなinodeを取得し、初期設定をし、wdir( )を呼んでディレクトリへの追加をした後に、inodeを呼び出し元にreturnします。

wdir( )

wdir( )(7477行目)は、ディレクトリに新たなエントリを追加します。

追加先のディレクトリや、追加するファイル・ディレクトリの名前、ディレクトリの中の空エントリ(今からエントリを追加しようとする場所。u.u_offsetで表現されている)はu領域に格納されています。これらの値はnamei( )でu領域に設定されます。つまり、wdir( )を呼ぶ前に、必ずnamei( )を実行しておく必要があります。

ディレクトリについての説明はこちらを参考にしてください。
u_dentがディレクトリのエントリに対応しており、u_dent.u_inoがintで2bytes, u_dent.u_name[DIRSIZ]が char[DIRSIZ=14]で14bytes, 合わせて1エントリあたり16bytesです。

writei( )を呼んで、ディレクトリの更新情報をブロックデバイスに反映させます。

最後のiput( )は、namei( )でu.u_pdirの参照カウンタが(iget( )により)インクリメントされたのを元に戻しているものと思われます。

maknode( ), wdir( )周りの流れを絵に描いてみました。


コードメモ

iget( )
  • arguments
    • dev : デバイス
    • ino : inode number(i_number)


アルゴリズムはgetblk( )とよく似ている

  • 7285 : inode[ ]配列を線形探索
    • 7286 : 引数のdev, inoに対応したエントリが見つかったら
      • 7287-7290 : ロックがかかっていたらIWANTフラグ(このinodeを待っているプロセスがいるフラグ)を立てて寝る。起きたらloopに戻ってinode探索やり直し
      • 7292-7301 : IMOUNTフラグ(このinodeはマウントされている別デバイスのroot directoryであることを表すフラグ)が立っていたら、mount[ ]からこのinodeに該当するエントリを探し出し、devをそのマウントされているデバイス種、inoをroot i_numberに設定しなおしてloopに戻りinode[ ]の探索をやり直す。もしmount[ ]から該当エントリが見つからなかったらpanic.
      • 7302-7305 : ロックがかかっていなくて、IMOUNTフラグも立っていなかったら、inodeの参照カウンタをインクリメントしロックをかけてからそのinodeをreturnする
    • 7306-7307 : inode[ ]中に空きエントリ(参照カウンタが0)の場合、そのinodeをipに保存しておく。inode[ ]中の一番最初の空きエントリがipに保存される
  • 7309-7313 : ここからはinode[ ]に引数のdev, inoに対応したエントリが見つからなかった場合。inode[ ]中に空きエントリがなかった場合はエラー
  • 7314-7318 : 空きinodeにdev, inoを使って名前付け。参照カウンタをインクリメント(参照カウンタは1になる。inode[ ]の空きエントリ = i_countが0のため)、ロックをかけて(他のフラグは全て落とす)、先読みブロックには-1(無効?)をセット
  • 7319 : ブロックデバイス中の該当inodeが存在するblockを読み出す
  • 7323-7327 : ブロックデバイス読み出し時にエラーが起きていたら(buffer pointerのB_ERRORフラグが立つ)、バッファを開放し、inodeを解放し、return.
  • 7328 : ブロック中の該当inodeのi_modeからi_addrまでを、コア中のinode(inode[ ]のエントリ)にコピー
  • 7332-7333 : バッファを解放してから、inodeをreturn
iput( )
  • arguments
    • p : inode pointer
  • 7350 : inodeの参照カウンタが1ならば(デクリメントして参照カウンタが0になるならば)
    • 7351 : ロックをかけて
    • 7352 : どのディレクトリからも見られていなかったら
      • 7353 : itrunc( )を呼んで、使用しているblock numberをフリーリストに戻す。ファイルサイズは0になる
      • 7354 : i_modeを0にして、このinodeエントリが未割当てであることを示す。
      • 7355 : ifree( )を呼んで、このinodeのi_numberをフリーリストに戻す
    • 7357 : iupdate( )を呼んで、ブロックデバイス中のinodeを更新する
    • 7358 : prele( )を呼んで、inodeのロックを解放
    • 7359-7360 : inodeのflagとi_numberを0に
  • 7362 : inodeの参照カウンタをデクリメント
  • 7363 : inodeのロックを解放(これはどこでかけたロックに対する解放?)
itrunc( )

こちらを見ておくと処理の中身の理解が深まるかも。

  • arguments
    • ip : inode pointer
  • 7421-7422 : inodeがIFCHR(キャラクタ特殊型)かIFBLK(ブロック特殊型)ならば、なにもせずにreturn. (キャラクタ特殊型とブロック特殊型って何?)
  • 7423-7424 : inodeのi_addr[ ]を辿る。要素がNULLでなければ
    • 7425 : inodeのILARGフラグが立っていたら(データ領域で間接参照を使っている)
      • 7426 : i_addr[ ]の要素が指すblock numberのブロックを読み出し
      • 7427-7441 : ブロックの中身はblock numberのリスト。後ろから辿り、free( )を呼んで一つずつblock numberをフリーリストに戻す。i_addr[7]は二重間接参照なので、各block numberが指すブロックを読み出し、そこに格納されているblock numberのリストをfree( )でフリーリストに戻す。
    • 7443 : free( )を呼んで、i_addr[ ]の要素が指すblock numberをフリーリストに戻す。i_addr[ ]の要素は0にする
  • 7445-7448 : inodeのILARGフラグをリセットし、i_size0, 1を0にし、更新フラグを立てる
iupdate( )
  • arguments
    • p : inode pointer
    • tm : 現在時刻
  • 7382 : inodeの更新フラグ(IUPD = 更新されたことを表すフラグ, IACC = 参照されたことを表すフラグ)が立っていたら
    • 7383-7384 : getfs( )を呼んでfilsys(superblock)を読み出し、filsysがreadonlyだったら何もせずreturn
    • 7385-7386 : 該当inodeが存在するblockを読み出す
    • 7387-7390 : block中の該当inodeのi_modeからi_addrまでを更新する
    • 7391-7393 : IACC(参照フラグ)が立っていたら、inodeの参照時間を更新する
    • 7396-7399 : IUPD(更新フラグ)が立っていたら、inodeの更新時間を更新する
    • 7400 : bwrite( )を呼んで、更新したinodeを含むブロックをブロックデバイスに書き込む
maknode( )
  • arguments
    • mode : inodeのモード。おそらくファイルのパーミッションのこと(read, write, execute)
  • 7459-7461 : ialloc( )を呼んで、新規inode pointerを取得する。u.u_pdirは新たにファイルを追加しようとしているディレクトリを指す。この値はnamei( )で設定される。inode pointerの取得に失敗したらエラー
  • 7462-7466 : inodeの初期設定
  • 7467 : wdir( )を呼んで、ディレクトリに新規inodeを追加する
  • 7468 : inodeをreturn.
wdir( )
  • arguments
    • ip : inode pointer
  • 7482 : inodeのi_numberをu.u_dent.u_inoに格納
  • 7483-7485 : u.u_dbufの値をu.u_dent.u_nameにコピー。。u.u_bufは追加しようとしているファイル・ディレクトリの名前。namei( )で設定される
  • 7486-7488 : writei( )を呼んで、ディレクトリ更新情報をブロックデバイスに書き込むためにパラメータを設定する
  • 7489 : writei( )を呼んでディレクトリ更新情報をブロックデバイスに書き込む
  • 7490 : iput( )を呼んでディレクトリの参照カウンタをデクリメント。参照カウンタはnamei( )でインクリメントされている

終わりに

コア中のinodeリソース管理の仕方がわかりました。

次回はnamei.cを解読し、システム中で各ファイル・ディレクトリがどのように管理されているのかを見ていきます。