UNIX 6th code reading - unix/sys3.c

はじめに

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

fstat( ), stat( )

fstat( )(6014行目)はfstatシステムコールのハンドラです。stat( )(6028行目)はstatシステムコールのハンドラです。

fstatもstatもファイルのinode情報を取得するシステムコールです。異なるのはファイルの指定の仕方です。fstatはファイルディスクリプタ(つまり、オープン済みのファイルが対象)で指定し、statはパス名で指定します。

処理の大部分が共通です。共通処理はstat1( )が行います。異なるのは如何にinodeを取得するか、です。

fstatはファイルディスクリプタを元に、getf( )を使ってfile構造体を取得します。そして、file構造体が指すinodeをstat1( )に渡して処理をさせます。

statはnamei( )を使ってパス名に該当したinodeを取得し、stat1( )に渡して処理をさせます。namei( )によって参照カウンタがインクリメントされるので、stat1( )が終った後にiput( )を呼んで参照カウンタをデクリメントします。

fstatシステムコールの引数はファイルディスクリプタ、inode情報を格納する変数のアドレスです。statシステムコールの引数はファイルのパス名、inode情報を格納する変数のアドレスです。

stat1( )

stat1( )(6045行目)はfstatとstatの共通処理を行います。

ディスクから該当のinode情報を読み出してユーザ領域にコピーします。コピーされる情報は以下です。

参考:ユーザマニュアルより引用

struct inode {
    char minor;      /* +0: minor device of i-node */
    char major;      /* +1: major device */
    int  inumber;    /* +2 */
    int  flags;      /* +4: see below */
    char nlinks;     /* +6: number of links to file */
    char uid;        /* +7: user ID of owner */
    char gid;        /* +8: group ID of owner */
    char size0;      /* +9: high byte of 24-bit size */
    int  size1;      /* +10: low word of 24-bit size */
    int  addr[8];    /* +12: block numbers or device number */
    int  actime[2];  /* +28: time of last access */
    int  modtime[2]; /* +32: time of last modification */
};
The flags are as follows:
100000 i-node is allocated
060000 2-bit file type:
000000 plain file
040000 directory
020000 character-type special file
060000 block-type special file.
010000 large file
004000 set user-ID on execution
002000 set group-ID on execution
001000 save text image after execution
000400 read (owner)
000200 write (owner)
000100 execute (owner)
000070 read, write, execute (group)
000007 read, write, execute (others)

dup( )

dup( )(6069行目)はdupシステムコールのハンドラです。

ファイルディスクリプタを複製します。同じfile構造体を別のu.u_ofile[ ]からも指すようになります。file構造体の参照カウンタもインクリメントされます。

同じfile構造体を指すため、二つのファイルディスクリプタはファイルオフセットも共有します。これを使ってパイプを実現しているようです。次のエントリで詳しく扱う予定です。

smount( )

smount( )(6086行目)はmountシステムコールのハンドラです。ブロックデバイスのマウント処理を行います。

mountシステムコールの引数は、マウントするデバイスのパス名、マウント先のパス名、読み書きフラグ、です。

マウントするデバイスのパス名が妥当であることをチェックします。この処理はgetmdev( )を呼んで行います。妥当ならばdevice Noが返ってきます。

また、マウント先のパス名に該当するinodeが既に存在していないといけないので、namei( )を使ってinodeの取得を試みます。

その後、mount[ ]から空きエントリを探し、そのエントリに取得したdevice No, inodeを設定します。また、マウントするデバイスのsuper blockを読み出し、mount[ ]のエントリに値を設定します。このとき、読み書きフラグによってはsuper blockにread onlyフラグがセットされます。

マウント先に該当するinodeにはIMOUNTフラグがセットされます。これにより、このinodeはマウントされたデバイスのroot directoryを指すようになります。iget( )について書いたエントリを参考にしてください。

sumount( )

sumount( )(6144行目)はumountシステムコールのハンドラです。アンマウント処理を行います。

umountシステムコールの引数はマウントされたデバイスのパス名です。

まずはsmount( )と同じように、getmdev( )を呼んでデバイスのパス名が妥当であることをチェックし、device Noを取得します。

その後mount[ ]からdevice Noで名前付けされたエントリを探し出します。

次にinode[ ]から、マウントされたデバイスのinodeがいないかチェックします。inode[ ]はアクティブなinodeが格納されているはずであり、もしマウントされたデバイスのinodeがいたら使用中ということでEBUSYエラーとなります。

BUSYでなければ、mount[ ]のエントリからマウント先のinodeを取得しIMOUNTフラグをリセットします。そしてmount[ ]のエントリをNULLにしてアンマウント処理の完了です。

getmdev( )

getmdev( )(6181行目)はsmount( ), sumount( )の共通処理を行う関数で、システムコールの一つ目の引数であるマウント元のデバイスのパス名が妥当であるかチェックし、妥当であった場合そのdevice Noを返します。

namei( )を使ってinodeを取得し、そのinodeがブロックデバイスを表しているか(IFBLK flagが立っているか)チェックします。

ブロックデバイスならばinodeのi_addr[0]にdevice Noが格納されているはずなのでそれをreturnします。

マウントするデバイスは予め/etc/mknodコマンドを実行して、/dev/ディレクトリの下にspecial file(block file(=IFBLK flagが立っている) & i_addr[0]にdevice No(=major番号)を格納している)を作成しておく必要があります。

simh+PDP11+UNIX v6の環境を構築するときのドキュメントが参考になるかと思います。

コードメモ

fstat( )
  • 6018-6020 : ユーザから渡されたファイルディスクリプタを使用してfile構造体を得る。失敗したら何もせずreturn
  • 6021 : file構造体が指すinodeと、ユーザから渡されたinode情報を格納するアドレスを引数にstat1( )を実行
stat( )
  • 6033-6035 : ユーザから渡されたパス名に対応するinodeを取得する。失敗したら何もせずreturn
  • 6036 : 取得したinodeと、ユーザから渡されたinode情報を格納するアドレスを引数にstat1( )を実行
  • 6037 : namei( )でinodeの参照カウンタがインクリメントされているので、iput( )を呼んでデクリメント
stat1( )
  • arguments
    • ip : inode pointer
    • ub : user address
  • 6050 : iupdat( )を実行してinode情報をディスクに反映しておく(なぜ?)
  • 6051 : ディスクから該当inode情報が存在するblockを読み出す
  • 6052 : block中の該当inodeの必要な情報を指すようにオフセットを調整
  • 6053-6061 : suword( )を使用してinodeの情報をユーザ領域にコピー。コピーする内容は上で参考として載せたユーザマニュアルの引用を参照のこと
  • 6062 : brelse( )を使用してblock bufferを解放
dup( )
  • 6073-6075 : ユーザから渡されたファイルディスクリプタに対応したfile構造体を取得。失敗したらエラー
  • 6076-6077 : ufalloc( )を実行して、u.u_ofile[ ]の新たなエントリを取得。失敗したらreturn.
  • 6078 : 取得したu.u_ofile[ ]のエントリから先ほど取得したfile構造体を指すように設定
  • 6079 : file構造体の参照カウンタをインクリメント
smount( )
  • 6093-6095 : getmdev( )を使用してマウント元のデバイスのdevice Noを取得する。失敗したら何もせずreturn
  • 6096-6099 : マウント先のinodeを取得。失敗したらエラー
  • 6100-6101 : 取得したinodeの参照カウンタが1より大きい(他の誰かに開かれている(使用されている)か、キャラクタ型だったらエラー
  • 6102-6112 : mount[ ]から空きエントリを探す。空きエントリがなければエラー。また、マウントしようとしているデバイスが既にマウントされていたらエラー
  • 6113-6115 : マウントするデバイスのopen処理。処理中にエラーがあればエラー処理
  • 6116-6120 : マウントするデバイスのblock No 1(=super block)を読み込む。読み込みに失敗したらblock bufferを解放してエラー処理
  • 6121-6122 : 取得したmount[ ]のエントリからマウント先のinodeを指すように設定。また、device Noを登録
  • 6123-6128 : 作業用にgetblk(NODEV)を使ってblock bufferを取得。ここにsuper blockの内容(filsys構造体)をコピーし、mount[ ]のエントリに登録。さらに、super blockのロック情報をクリアし、ユーザから渡された引数によってread onlyフラグを立てる
  • 6129 : super blockを読み出すのに使用したblock bufferを解放
  • 6130 : マウント先のinodeのIMOUNTフラグを立てる
  • 6131 : マウント先のinodeのロックを解除(参照カウンタはデクリメントしない。なぜ?)
  • 6132 : マウント処理完了。return
  • 6135-6136 : エラーフラグをセットし、マウント先のinodeを解放
sumount( )
  • 6150 : update( )を実行してdirtyなデータをフラッシュ(なんのため?)
  • 6151-6153 : getmdev( )を実行してアンマウントするデバイスのdevice Noを取得する。失敗したらエラー
  • 6154-6158 : mount[ ]からアンマウントするデバイスのdevice Noで名前付けられているエントリを探す。見つかったがfoundへ飛ぶ。見つからなかったらEINVAL(invalid)でエラー
  • 6161-6165 : inode[ ]にアンマウントするデバイスのinodeがいないか探す。これからアンマウントしようとしているデバイスのinodeが使用中のときは、システムの保守のためにアンマウントを行わない。EBUSY(使用中)エラー
  • 6166 : アンマウントするデバイスのclose処理
  • 6167-6169 : マウント先として使用していたinodeのIMOUNTフラグをリセット、iput( )を実行してinodeを解放
  • 6170-6171 : mount[ ]のエントリを解放(m_bufpがNULLを指すように変更)
  • 6172 : block bufferを解放。このblock bufferは6123行目で取得したもの
getmdev( )
  • 6186-6188 : ユーザから渡された1番目の引数のパス名に該当するinodeを取得。失敗したらエラー
  • 6189-6190 : マウント元のinodeはspecial file(ブロック型=IFBLKフラグが立っている)でないといけない。そうでなかったらエラー。マウント元のinodeは/etc/mknodで予め作成しておく必要がある
  • 6191 : special fileのinodeのi_addr[0]には、そのdeviceのdevice Noが格納されている。これを取得
  • 6192-6193 : デバイスのmajor番号が大きすぎたらエラー
  • 6194 : iput( )でinodeの解放
  • 6195 : device Noをreturn

終わりに

ファイルシステム周りのシステムコールを見終わりました。

次回はパイプについて見ていく予定です。これでファイルシステム周りの調査もようやく一段落です。