0から作るソフトウェア開発

日々勉強中。。。

Tips
リンク

ファイルシステム >

0から作るLinuxプログラム Ext2ファイルシステム ステップ2 Ext2スーパーブロックの読み込み

Ext2ファイルシステム

Linuxのファイルシステムのうち基本的なファイルシステムであるExt2について
みていきます。

それでは、順番に見ていきます。

目次 Ext2ファイルシステム

  1. ファイルシステムの概要

  2. ファイルシステムへのアクセス

  3. カーネルサイドから見たシステムコール

  4. ファイルシステム関連のシステムコール

  5. 仮想ファイルシステム(VFS)

  6. ステップ0 Ext2ファイルシステムと簡単なモジュールの実装

  7. ステップ1 Ext2ファイルシステムタイプとマウントメソッド

  8. (現在のページ) ステップ2 Ext2スーパーブロックの読み込み

  9. ステップ3 スーパーブロック管理情報

  10. ステップ4 マウントとスーパーブロックオブジェクト

  11. ステップ5 スーパーブロックオブジェクトと管理情報のセットアップ

  12. ステップ6 ブロックグループディスクリプターの読み込み

  13. ステップ7 ルートディレクトリのinode読み込み

  14. ステップ8 簡単なディレクトリの読み込み

  15. ステップ9 ディレクトリ読み込みとページキャッシュとaddress_space

  16. ステップ10 inodeオブジェクトのlookupメソッド

  17. ステップ11 inodeオブジェクトのmkdirメソッドとper-cpuカウンター

  18. ステップ12 簡単なブロックの新規割り当て処理

  19. ステップ13 スーパーブロックオブジェクトのwrite_inodeメソッド

  20. ステップ14 Orlov方式による新規ディレクトリのinodeを割り当てるブロックグループ選択

  21. ステップ15 inodeオブジェクトのrmdirメソッドとunlinkメソッド

  22. ステップ16 inodeオブジェクトのrenameメソッド

  23. ステップ17 inodeオブジェクトのcreateメソッドとファイルオブジェクトの汎用メソッド

  24. ステップ18 inodeオブジェクトのlinkメソッド

  25. ステップ19 シンボリックとinodeオブジェクトのsymlinkメソッド

  26. ステップ20 inodeオブジェクトのmknodメソッドとtmpfileメソッド

  27. ステップ21 スーパーブロックオブジェクトのevict_inodeメソッド

  28. ステップ22 スーパーブロックオブジェクトのsync_fsメソッド

  29. ステップ23 スーパーブロックオブジェクトのstatfsメソッドとメモリーバリアー/フェンス

  30. ステップ24 スーパーブロックオブジェクトのremount_fsメソッドとマウントオプション解析

  31. ステップ25 スーパーブロックオブジェクトのshow_optionsメソッドとprocfs

  32. ステップ26 カーネルオブジェクトとsysfs

  33. ステップ27 スーパーブロックオブジェクトのfreeze_fs/unfreeze_fsメソッド

  34. ステップ28 ファイルオブジェクトのioctlメソッド

  35. ステップ29 ファイルオブジェクトのsetattrメソッド

  36. ステップ30 address_spaceのdirect_IOメソッド

  37. ステップ31 リザベーションウィンドウ

  38. ステップ32 拡張アトリビュートその1 ハンドラーの呼び出し

  39. ステップ33 拡張アトリビュートその2 コア処理

  40. ステップ34 POSIX ACLその1 ハンドラーの呼び出し

  41. ステップ35 POSIX ACLその2 コア処理

  42. ステップ36 ディスククォータ

マウントメソッドの処理

マウントメソッドの処理時にファイルシステムのスーパーブロックを読み込み、仮想ファイル

システムのスーパーブロックオブジェクトに反映させる必要があります。


マウントメソッドでは仮想ファイルシステムで共通処理となるブロックデバイスのマウント処理

を呼び出します。仮想ファイルシステムのブロックデバイスマウント処理にファイルシステム

固有処理となるスーパーブロック読み込み処理関数を指定して呼び出します。仮想

ファイルシステムはスーパーブロックオブジェクトを割り当てた後にファイルシステム固有の

スーパーブロック読み込み処理関数をコールバックします。スーパーブロック読み込み処理

では、ディスクからスーパーブロックを読み出して指定されたスーパーブロックオブジェクトの値

を埋めます(このため、コールバック関数の名前はfill_superとなっています)。スーパー

ブロック読み込み処理では、スーパーブロックオブジェクトに関連付けるルートのinodeも

ディスクから読み出す必要があります。読みだしたinodeも仮想ファイルシステムで使用する

inodeオブジェクトの値に格納していきます。後はルートのdエントリーオブジェクトとinode

オブジェクトを関連付けて仮想ファイルシステムに返します。これを受けて仮想ファイルシステムは

マウント処理を続けて完了します。

スーパーブロック読み出し

それでは、スーパーブロック読み出し処理の起点となるマウントメソッドの処理を見ていきましょう。

マウントメソッドの処理実装

マウントメソッド処理自体の処理内容はすごく簡単です。Ext2ファイルシステムはブロック

デバイスが必要となるファイルシステムであるため、ブロックデバイスのマウント処理を呼び出す

だけとなります。ブロックデバイスのマウント処理は各ファイルシステム共通処理となるため、

仮想ファイルシステム層で実装されています。これを呼び出すようにします。その他にも、

ブロックデバイス無しのマウント処理があります。ついでですので、一緒に見ていきましょう

(今回は使いませんが)。

仮想ファイルシステムのマウント処理

ブロックデバイスのマウント処理、ブロックデバイス無しのマウント処理は次のようになっています。


ブロックデバイスのマウント処理

struct dentry *mount_bdev( struct file_system_type *fs_type,
                           int flags,
                           const char *dev_name,
                           void *data,
                           int (*fill_super)(struct super_block *, void *, int));



ブロックデバイス無しのマウント処理はramfsなどブロックデバイスが必要ないファイルシステム
で呼び出しを行います。


struct dentry *mount_nodev( struct file_system_type *fs_type,
                            int flags,
                            void *data,
                            int (*fill_super)(struct super_block *, void *, int ));



ほとんど同じ引数となります。mount_nodevはブロックデバイスが必要ありませんので、

mount_bdevのdev_nameが無いだけの違いとなります。(処理内容は違います)。

mount_bdevとmount_nodevの引数
名前 説明
struct file_system_type *fs_type ファイルシステムタイプオブジェクトのポインターを指定します。
intflags mountシステムコールで指定されたマウントフラグを指定します。
const char *dev_name mountシステムコールで指定されたデバイス名を指定します。
void *data mountシステムコールで指定されたマウントオプションを指定します。
int ( * )( ... )fill_super ファイルシステム固有のスーパーブロック読み込み処理関数を指定します。詳細はこの後見ていきましょう。


戻り値は次のようになっています。

mount_bdevとmount_nodevの戻り値
説明
struct dentry * ファイルシステムのルートdエントリーを返します。失敗時NULLまたエラーを表すポインターを返します。


となります。Ext2はブロックデバイスが必要なファイルシステムとなりますので、

mount_bdevを使います。

スーパーブロックの読み込み処理fill_super関数の呼び出し

mount_bdev関数にコールバック関数として指定するfill_superについて

見ていきましょう。fill_superで行う処理の概要は次のようになります。

  1. ディスクからスーパーブロックを読み込みます。

  2. 読み込みしたスーパーブロックの各値を仮想ファイルシステムの

    スーパーブロックオブジェクトに格納します。

  3. ディスクからファイルシステムのルートinodeを読み込みます。

  4. inodeをスラブアロケーターから新規に割り当て、読み込みした

    ルートinodeの値を仮想ファイルシステムのinodeオブジェクトに

    格納します。

  5. ルートのdエントリーをスラブアロケーターから新規に割り当て、ルート

    inodeオブジェクトと関連付けします。

  6. ここまで全部成功した場合、ルートのdエントリーを返します。

となります。結構大変な処理内容となります。この手順はステップバイステップで作成

していきましょう。まずは、fill_superのインターフェースを見ていきましょう。次のような

型の関数となります。

fill_superコールバック関数の引数
名前 説明
struct super_block *sb 仮想ファイルシステム層のスーパーブロックオブジェクトのポインターとなります。ディスクからスーパーブロックを読み出した後、fill_super関数内でこのポインターが指すスーパーブロックオブジェクトに値を格納します。
void *data mountシステムコールで指定されたマウントオプションです。
intsilent 仮想ファイルシステムから(printkで出力する)エラー情報を出力するか出力しないかが指示されます。値が0のときエラー情報を出力します。0以外の値の時エラー出力を抑制するようにしてください。


fill_superコールバック関数の戻り値は次のような値を返します。

fill_superコールバック関数の戻り値
説明
int 成功時0を返します。失敗時0以外を返します。


それでは、まずfill_superのひな形とmount_bdev呼び出し処理を実装

していきましょう。


今回はスーパーブロックの処理となりますので、me2fs_super.cというファイル

に処理を実装していきます。(Linuxの通常のファイルシステムではmain.cファイル

ではなくsuper.cにファイルシステムタイプオブジェクト登録処理などを実装します。)

ここまでで、次のようにプログラムファイルを作ることになります。


Makefile
me2fs_main.c
me2fs.h
me2fs_util.h
me2fs_super.c    // new
me2fs_super.h    // new



次のような実装例となります。ここではfill_superコールバック関数として登録する

関数をme2fsFillSuperBlock関数として実装しています。中身はデバッグ情報を

出力するだけで何もしません。まずは呼び出されるか確認を行ってみましょう。そして、

ブロックデバイスのマウント処理を呼び出す関数を実装しています。

me2fsMountBlockDev関数です。この関数ではfill_superコールバック関数

としてme2fsFillSuperBlockを指定して、mount_bdev関数を呼び出します。

[me2fs_super.c]


/********************************************************************************
    File            : me2fs_super.c
    Description     : super block operations for my ext2 file sytem

********************************************************************************/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h>

#include "me2fs.h"
#include "me2fs_util.h"


/*
=================================================================================

    Prototype Statement

=================================================================================
*/
static int me2fsFillSuperBlock( struct super_block *sb,
                                void *data,
                                int silent );

/*
=================================================================================

    DEFINES

=================================================================================
*/

/*
==================================================================================

    Management

==================================================================================
*/

/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    < Open Functions >

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    Function    :me2fsMountBlockDev
    Input       :struct file_system_type *fs_type
                 < file system type >
                 int flags
                 < mount flags >
                 const char *dev_name
                 < device name >
                 void *data
                 < user data >
    Output      :void
    Return      :struct dentry *
                 < root dentry >

    Description :mount me2fs over block device
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
struct dentry*
me2fsMountBlockDev( struct file_system_type *fs_type,
                    int flags,
                    const char *dev_name,
                    void *data )
{
    return( mount_bdev( fs_type, flags, dev_name, data, me2fsFillSuperBlock ) );
}

/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    < Local Functions >

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/*
==================================================================================
    Function    :me2fsFillSuperBlock
    Input       :struct super_block *sb
                 < vfs super block object >
                 void *data
                 < user data >
                 int silent
                 < silent flag >
    Output      :void
    Return      :void

    Description :fill my ext2 super block object
==================================================================================
*/
static int me2fsFillSuperBlock( struct super_block *sb,
                                void *data,
                                int silent )
{
    DBGPRINT( "<ME2FS>fill_super!" );

    return( 0 );
}

/*
==================================================================================
    Function    :void
    Input       :void
    Output      :void
    Return      :void

    Description :void
==================================================================================
*/



そして、me2fsMountBlockDev関数をme2fs_main.cから呼び

出せるようにme2fs_super.hに定義しておきます。

[me2fs_super.h]

/********************************************************************************
    File            : me2fs_super.h
    Description     : Definietion for Super Block

********************************************************************************/
#ifndef __ME2FS_SUPER_H__
#define __ME2FS_SUPER_H__


/*
=================================================================================

    Prototype Statement

=================================================================================
*/

/*
=================================================================================

    DEFINES

=================================================================================
*/

/*
==================================================================================

    Management

==================================================================================
*/

/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    < Open Functions >

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    Function    :me2fsMountBlockDev
    Input       :struct file_system_type *fs_type
                 < file system type >
                 int flags
                 < mount flags >
                 const char *dev_name
                 < device name >
                 void *data
                 < user data >
    Output      :void
    Return      :struct dentry*
                 < root dentry >

    Description :mount me2fs over block device
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
struct dentry *
me2fsMountBlockDev( struct file_system_type *fs_type,
                    int flags,
                    const char *dev_name,
                    void *data );

#endif  // __ME2FS_SUPER_H__



そして、me2fs_main.cからme2fsMountBlockDevを呼び出します。

me2fs_super.hをインクルードします。

[me2fs_main.c]

				
/********************************************************************************
    File            : me2fs_super.c
    Description     : super block operations for my ext2 file sytem

********************************************************************************/
#include <linux/module.h>
#include <linux/fs.h>

#include "me2fs.h"
#include "me2fs_super.h"
#include "me2fs_util.h"


/*
=================================================================================

    Prototype Statement

=================================================================================
*/
static struct dentry *me2fs_mount( struct file_system_type *fs_type,
                                  int flags,
                                  const char *dev_name,
                                  void *data );

/*
=================================================================================

    DEFINES

=================================================================================
*/

#define ME2FS_VERSION_STRING        "me2fs_0.01"

#define ME2FS_PROC_MODULE_NAME      "me2fs"

#define ME2FS_MODULE_DESC           "My ex2 file system (me2fs)"
#define ME2FS_MODULE_AUTHOR         "yabusame2001 <mail@address>"

/*
==================================================================================

    Management

==================================================================================
*/
/*
---------------------------------------------------------------------------------
    File system type
---------------------------------------------------------------------------------
*/
static struct file_system_type me2fs_fstype =
{
    .name       = "me2fs",
    .mount      = me2fs_mount,
    .fs_flags   = FS_REQUIRES_DEV,
};

/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    < Open Functions >

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    Function    :void
    Input       :void
    Output      :void
    Return      :void

    Description :void
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/

/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    < Local Functions >

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/*
==================================================================================
    Function    :me2fs_mount
    Input       :struct file_system_type *fs_type
                 < file system type >
                 int flags
                 < flags for mounting >
                 const char *dev_name
                 < device file name >
                 void *data
                 < options >
    Output      :void
    Return      :dentry *
                 < root dentry object >

    Description :mount a ext2 fs
==================================================================================
*/
static struct dentry *me2fs_mount( struct file_system_type *fs_type,
                                  int flags,
                                  const char *dev_name,
                                  void *data )
{
    me2fsMountBlockDev( fs_type, flags, dev_name, data );
    return( NULL );
}

/*
==================================================================================
    Function    :initMe2fs
    Input       :void
    Output      :void
    Return      :void

    Description :initialize Me2fs module
==================================================================================
*/
static int __init initMe2fs( void )
{
    DBGPRINT( "(Me2FS)Hello, world\n" );

    return( register_filesystem( &me2fs_fstype ) );
}

/*
==================================================================================
    Function    :exitMe2fs
    Input       :void
    Output      :void
    Return      :void

    Description :clean up vsfs module
==================================================================================
*/
static void __exit exitMe2fs( void )
{
    DBGPRINT( "(Me2FS)Good-bye!\n" );

    unregister_filesystem( &me2fs_fstype );
}

/*
==================================================================================
    Function    :void
    Input       :void
    Output      :void
    Return      :void

    Description :void
==================================================================================
*/

/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    < Kernel Registrations >

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/*
---------------------------------------------------------------------------------
    General Module Information
---------------------------------------------------------------------------------
*/
module_init( initMe2fs );
module_exit( exitMe2fs );

MODULE_AUTHOR( ME2FS_MODULE_AUTHOR );
MODULE_DESCRIPTION( ME2FS_MODULE_DESC );
MODULE_LICENSE( "GPL" );



そしてMakefileにme2fs_super.cを加えます。

[Makefile]

ME2FS_SOURCE = me2fs_main.c me2fs_super.c

obj-m += me2fs.o
me2fs-objs := $(ME2FS_SOURCE:.c=.o)

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

insmod:
    sudo insmod me2fs.ko

rmmod:
    sudo rmmod me2fs.ko

mount:
    sudo mount -t me2fs -o loop ../ext2.img ../mnt

umount:
    sudo umount ../mnt

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean



makeして一度動かしてみてください。

準備ができましたので、me2fsFillSuperBlock(fill_superコールバック関数)

を順番に実装していきたいと思います。スーパーブロックの読み込みを処理を実装する

必要がありますが、まずはExt2ファイルシステムがどのようにディスクに格納されているのか

を見て行きましょう。

Ext2ファイルシステムのディスク格納構造

Ext2ファイルシステムではデータをブロック単位で扱います。ブロックのサイズはファイル

システムをmkfsコマンドで作成したときに指定することができます。ブロックサイズは通常

512バイトから4096バイトまでの範囲で、512の倍数となります。Ext2ファイルシステム

では、その仕様上から1024の倍数のブロックサイズとなります。つまりExt2では

				
ブロックサイズ = 1024 × ( 2 ^ 31 ) - 1



のブロックサイズを扱うことができます。(今回Ext2ファイルシステムのイメージを作成する

ときにブロックサイズは1024バイトを指定しています)。

補足
Ext2ファイルシステムの仕様上は4096バイトより大きなブロックサイズを扱うことが

できます。OSが扱うメモリーはページサイズ単位となりますので、なるべくページサイズ

の大ききさにあわしてブロックサイズを決定したほうがメモリー効率上よくなる場合が

あります。このため、ファイルシステムを作成する場合は、OSがあつかうページサイズを

上限にして扱うことがあります。(パソコンの場合、ページサイズは4096バイトとなり

ます。64ビットLinuxでは更に大きなサイズのページサイズも扱うことができます。)



また、Ext2ファイルシステムのデータ構造は全てリトルエンディアンで格納されています。

ブロックグループ

Ext2ファイルシステムではブロックをある程度集めてグループとして管理を行います。この

集まりをブロックグループといいます。HDDでは目的のデータが記録されている場所まで

磁気ヘッドを移動させてからデータを読み込みます。磁気ヘッドを移動は機械動作が

伴うため、プロセッサーの動作速度に比べて非常に遅いです。このため、大きなデータを読み

出す時にはなるべく磁気ヘッドの移動が発生しないように、ブロックを連続したディスク領域

に置くようにします。ブロックグループを作っておけば、あるファイルのデータをなるべく1つのブロック

グループに置き、そして連続したブロックになるようにディスク上に記録するように管理することが

できるようになります。



ブロックの割り当てはビットマップで管理を行います。ビットマップは1つのブロックグループにつき

1つのブロックで管理することになります。そのため、1つのブロックグループの最大ブロック数は

1ブロックのバイト数×8ビットとなります。今回は1ブロックは1024バイトとなりますので、

ブロックグループの最大ブロック数は1024×8=8192個となります。

ブロックグループ

スーパーブロック

スーパーブロックはファイルシステム全体の管理情報を記録している重要なブロックとなります。

inodeの総数、ファイルシステムのブロック数、空きブロック数、各グループのinode数および

ブロック数などがあります。


スーパーブロックはディスクの最初から1024バイト目に格納されています。ファイルシステムを

マウントするときにこのブロックを読み込む必要があります。ファイルシステム全体の情報が記録

されているため、スーパーブロックとブロックグループディスクリプター(後述します)は非常に重要な

ブロックでありますので、各ブロックグループにバックアップを作ります。バックアップはグループ0、1、3と

5の累乗グループ、7の累乗グループに作成されます。Linuxカーネルの処理ではグループ0の

スーパーブロックのみ更新を行います。他のブロックグループへのバックアップは随時ユーザーが

実施してください。

補足
ディスクの最初の512バイトはブートブロックとなります。ブートローダーを格納するブロック

となります。続く512バイトの領域はブートの補助領域となります。このためスーパー

ブロックは1024バイト目以降に格納されることになります。



スーパーブロック

inode

inodeはファイルの属性とファイルのデータブロックがどこに格納されているのかの情報が記録

されています。ファイルの属性にはパーミッション、所有者、サイズ、使用しているブロック数

アクセス時刻、更新時刻、リンク数などが記録されています。inodeのサイズは変更することが

できます。ファイルのデータブロックの記録にはブロックのポインターが12個あります(直接参照)。

更に、間接参照(2段階目の直接参照ブロックを指しています)と2重間接参照(関節参照ブロック

を指すポインターとなります)、3重間接参照(2重間接参照ブロックを指すポインターとなります)が

あります。

inode

ブロックグループディスクリプター

ブロックグループディスクリプターはスーパーブロックの次のブロックから格納されています。ブロック

グループ数分のディスクリプターがあります。各ブロックグループの管理情報が記録されています。

ブロックグループ内で割り当てられているinodeを示すinodeビットマップ、inodeが格納されて

いるinodeテーブル、ブロックの割り当て状況を示すブロックビットマップがどのブロックに記録されて

いるのかが格納されています。

ブロックビットマップ

ブロックビットマップはそのブロックグループ内で割り当てられてるブロックをビットマップで管理を行って

います。ブロックビットマップの大きさは1ブロックとなります。例えば1ブロックが1024バイトの場合には

そのブロックグループ内で管理できるブロック数は1024×8=8192個となります。

inodeビットマップ

ブロックグループ内で割り当てられてるinodeをビットマップで管理を行っています。inodeビットマップ

の大きさも1ブロックとなります。例えば1ブロックが1024バイトの場合にはそのブロックグループ内で

管理できるinode数は1024×8=8192個となります。

inodeテーブル

inodeが格納されているテーブルです。連続した複数ブロックに記録されています。



ここまでで、Ext2ファイルシステムの主要なディスク構成は次のようになります。

ディスク構造

ここで、例で作成しました20MiBのEx2ファイルシステムイメージの構成は次のようになります。

20MiBパーティションのレイアウト例(ブロックサイズは1KiB)
ブロックオフセット サイズ 説明
0(バイト)512バイト (あれば)ブートレコード
512(バイト)512バイト (あれば)追加のブートレコード
ブロックグループ0、ブロック1から8192
1024(バイト)1024バイト スーパーブロック
ブロック21ブロック ブロックグループディスクリプターテーブル
ブロック377ブロック 予備
ブロック811ブロック ブロックビットマップ
ブロック821ブロック inodeビットマップ
ブロック83209ブロック inodeテーブル
ブロック2927901ブロック データブロック
ブロックグループ1、ブロック8193から16384
ブロック81931ブロック 予備
ブロック81941ブロック スーパーブロック
ブロック81951ブロック ブロックグループディスクリプターテーブル
ブロック819677ブロック 予備
ブロック82731ブロック ブロックビットマップ
ブロック82741ブロック inodeビットマップ
ブロック8275209ブロック inodeテーブル
ブロック84847901ブロック データブロック
ブロックグループ2、ブロック16385から20000
ブロック163851ブロック ブロックビットマップ
ブロック163861ブロック inodeビットマップ
ブロック16387209ブロック inodeテーブル
ブロック165963404ブロック データブロック


これらの管理データを読み出すことでファイルシステムの管理を行っていきます。

スーパーブロックの読み込み

ファイルシステムをマウントするときに最初にスーパーブロックの読み込みを行う必要が

ありました。ここでは、更に詳しくスーパーブロックの構造について見ていきましょう。

スーパーブロックの構造

ディスクに格納されているExt2スーパーブロックはディスクの先頭から1024バイト目に

格納されています。その構造は次のようになっています。

スーパーブロックの構造
オフセット(バイト) サイズ(バイト) 名前 説明
04s_inodes_count ファイルシステムのinode総数が格納されます。inode総数はファイルシステム中の使用中inodeと未使用のinodeの総数となります。この値はs_inodes_per_group(ブロックグループごとのinode数)×ブロックグループ数以下である必要があります。また、各ブロックグループのinode総数と等しくなる必要があります。
44s_blocks_count ファイルシステムのブロック総数が格納されます。ブロック総数は使用中のブロックと未使用のブロックと予約ブロックの総数となります。この値はs_blocks_per_group(ブロックグループごとのブロック数)×ブロックグループ数以下である必要があります。また、各ブロックグループのブロック数の合計と等しくなる必要があります。
84s_r_blocks_count スーパーユーザーが使用するために予約されたブロック総数が格納されます。他のユーザーがファイルシステムの容量一杯に使い切っても、スーパーユーザーは専用の予約ブロックを使ってファイルの編集や保存を行うことができます。
124s_free_blocks_count 予約ブロック(s_r_blocks_count)を含めた、空きブロックの総数が格納されます。ここに格納される空きブロック数は各ブロックグループの空きブロックの総数となります。
164s_free_inodes_count 空きinodeの総数が格納されます。ここに格納される空きinode数は各ブロックグループの空きinode数の総数となります。
204s_first_data_block データブロックが格納される最初のブロック番号が格納されます。つまり最初のスーパーブロックが格納されているブロック番号となります。
244s_log_block_size ファイルシステムのブロックサイズとなりますが、実際に格納されているのは2の累乗の値が格納されています。ブロックサイズはこの値をNとすると1024×2^Nで計算できます。プログラムでは簡単に1024をこの値分、左シフトすることで求めることができます。この値は正の値のみとなります。
				
ブロックサイズ = 1024 << s_log_block_size

284s_log_frag_size フラグメントサイズが格納されます。ブロックサイズと同様に1024をこの値分、左シフトしてフラグメントサイズを求めます。ただし、この値は符号付となりますので、負の場合は1024を右シフトしてください。

if( 0 <= s_log_frag_size )
    フラグメントサイズ = 1024 << s_log_frag_size;
else
    フラグメントサイズ = 1024 >> -s_log_frag_size;

324s_blocks_per_group ブロックグループごとのブロック数が格納されます。この値とs_first_data_blockを使用して各ブロックグループの先頭ブロック番号を計算することができます。
364s_frags_per_group ブロックグループごとのフラグメント数が格納されます。
404s_inodes_per_group ブロックグループごとのinode数が格納されます。この値はinodeビットマップのサイズを計算するときにも使用できます。inodeビットマップは1ブロックのサイズとなりますので、ブロックグループごとのinode数はブロックサイズ×8(1バイト=8ビット)より大きくなりません。この値は次のように計算することもできます。

ブロックグループごとのinode総数 = ( 1024 << s_log_block_size ) / s_inode_size;

444s_mtime ファイルシステムが最後にマウントされた時刻を、POSIXで定義されているUNIX時刻で格納されています。
484s_wtime ファイルシステムが最後に書き込みアクセスされた時刻を、POSIXで定義されているUNIX時刻で格納されています。
522s_mnt_count ファイルシステムがマウントされた回数が格納されます。(ファイルシステムをベリファイした後この値はクリアーされます)。
542s_max_mnt_count 最大マウント回数が格納されます。マウント回数がこの値になるまでに、ファイルシステムのチェックを行ってください。
562s_magic Ext2ファイルシステムのマジックナンバーが格納されます。Ext2のマジックナンバーは0xEF53となります。
582s_state ファイルシステムの状態が格納されます。ファイルシステムのマウントが完了したときに値EXT2_ERROR_FSをセットするようにします。また、ファイルシステムのアンマウントが完了したときに値EXT2_VAID_FSをセットするようにします。これにより、アンマウントしたと思っていても、ファイルシステムの状態がEXT2_ERROR_FSのままだと安全にアンマウントできていない状態と考えられ、ファイルシステムを修復する必要があります。この場合、Linuxではfsckでファイルシステムの修復を行います。値は次のように定義されています。
s_stateの定義値
定数名 説明
EXT2_VALID_FS1 完全にアンマウントされています
EXT2_ERROR_FS2 エラーを検出しました

補足
実際のLinuxのExt2ファイルシステムの実装ではマウントした後にEXT2_ERROR_FS

フラグをセットするのではなく、EXT2_VALID_FSフラグをクリアーする処理を行います。


602s_errors エラー検出時にファイルシステムがどのように動作するのかを示します。次の値が定義されています。
s_errorsの定義値
定数名 説明
EXT2_ERRORS_CONTINUE1 何も起こっていないかのように続行してください
EXT2_ERRORS_RO2 リードオンリーで再マウントしてください
EXT2_ERRORS_PANIC3 カーネルパニックの原因となります

622s_minor_rev_level マイナーリビジョンレベルが格納されます。
644s_lastcheck ファイルシステムを最後にチェックした時間がPOSIXで定義されているUNIX時刻で格納されます。
684s_checkinterval ファイルシステムをチェックする間隔の最大許容間隔がPOSIXで定義されているUNIX時刻で格納されます。
724s_creator_os ファイルシステムを作成したOSを示す値が格納されます。値は次のように定義されています。
s_creator_osの定義値
定数名 説明
EXT2_OS_LINUX0 Linux
EXT2_OS_HURD1 GNU HURD
EXT2_OS_MASIX2 MASIX
EXT2_OS_FREEBSD3 FreeBSD
EXT2_OS_LITES4 Lites

764s_rev_level ファイルシステムのリビジョンレベルが格納されます。値は次のように定義されています。
s_rev_levelの定義値
定数名 説明
EXT2_GOOD_OLD_REV0 リビジョン0
EXT2_DYNAMIC_REV1 リビジョン1で可変inodeサイズ、拡張アトリビュートなどに対応

802s_def_resuid 予約ブロックを使用するデフォルトユーザーIDが格納されます。
補足
Linuxではデフォルト値としてEXT2_DEF_RESUIDが定義されています。この値は0となります。
822s_def_resgid 予約ブロックを使用するデフォルトグループIDが格納されます。
補足
Linuxではデフォルト値としてEXT2_DEF_RESGIDが定義されています。この値は0となります。
EXT2_DYNAMIC_REV仕様
844s_first_ino 通常ファイルに使用する最初のinode番号が格納されます。リビジョン0では固定値11(定義はEXT2_GOOD_OLD_FIRST_INO)となりますが、リビジョン1以降では可変値となります。
882s_inode_size inodeのサイズが格納されます。リビジョン0では128(定義はEXT2_GOOD_OLD_INODE_SIZE)となりますが、リビジョン1以降では2の累乗でブロックサイズ以下である必要があります。
902s_block_group_nr ブロックグループ数が格納されます。
924s_feature_compat 互換機能をビットマップで表します。ファイルシステムを実装する場合には、メタデータが壊れてしまう可能性もありますので、必ずしも互換機能を実装する必要はありません。次の値が定義されています。
s_feature_compatの定義値
定数名 説明
EXT2_FEATURE_COMPAT_DIR_PREALLOC0x0001 新しいディレクトリ用に前もってブロックを割り当てます。
EXT2_FEATURE_COMPAT_IMAGIC_INODES0x0002 AFS用
EXT3_FEATURE_COMPAT_HAS_JOURNAL0x0004 Ext3のジャーネル機能あり
EXT2_FEATURE_COMPAT_EXT_ATTR0x0008 拡張inodeアトリビュートあり
EXT2_FEATURE_COMPAT_RESIZE_INO0x0010 非標準inodeサイズ使用
EXT2_FEATURE_COMPAT_DIR_INDEX0x0020 ディレクトリインデキシング(HTree)

964s_feature_incompat 非互換機能をビットマップで表します。非互換機能を実装していないファイルシステム実装は、このファイルシステムをマウントしないようにしてください。
注意
非互換機能を実装していない場合、ファイルシステムを取り扱うことはできません。例えば、圧縮機能が使用されている場合、ディスクからファイルを読み出しても解凍処理を実装されていないためそのファイルが使用できないからです。
非互換機能には次の値が定義されています。
s_feature_incompatの定義値
定数名 説明
EXT2_FEATURE_INCOMPAT_COMPRESSION0x0001 ディスク/ファイル圧縮機能が使用されています。
EXT2_FEATURE_INCOMPAT_FILETYPE0x0002 非互換のファイルタイプ
EXT3_FEATURE_INCOMPAT_RECOVER0x0004 非互換の復元機能
EXT3_FEATURE_INCOMPAT_JOURNAL_DEV0x0008 非互換のジャーナリング
EXT2_FEATURE_INCOMPAT_META_BG0x0010 非互換のメタブロックグループ

1004s_feature_ro_compat リードオンリー機能をビットマップで表します。リードオンリー機能がセットされている場合、リードオンリーでファイルシステムをマウントしてください。
s_feature_ro_compatの定義値
定数名 説明
EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER0x0001 スパーブロック分割。
EXT2_FEATURE_RO_COMPAT_LARGE_FILE0x0002 ラージファイルサポート、64ビットのファイルサイズ。
EXT2_FEATURE_RO_COMPAT_BTREE_DIR0x0004 ディレクトリのファイルを2分木で格納。

10416s_uuid UUID(Univarsally Unique Identifier)が格納されます。各ファイルシステムでできる限り固有のIDをセットしてください。
12016s_volume_name ボリューム名が格納されます。通常使用しません。ボリューム名にはISO-Latin-1のみ使用します。終端が0の文字列です。
13664s_last_mounted ファイルシステムが最後にマウントされたディレクトリが格納されます。通常使用しません。マウントポイントが指定されていないときに、自動でマウントポイントを設定したい場合などに使用します。パス文字列の終端は0となります。ISO-Latin-1を使用してください。
2004s_algorithm_usage_bitmap 圧縮アルゴリズムを表すビットマップが格納されます。値は次のように定義されています。
s_algo_bitmapの定義値
定数名 説明
EXT2_LZV1_ALG0x00000001 LZV1アルゴリズム
EXT2_LZRW3A_ALG0x00000002 LZRW3Aアルゴリズム
EXT2_GZIP_ALG0x00000004 GZIPアルゴリズム
EXT2_BZIP2_ALG0x00000008 BZIP2アルゴリズム
EXT2_LZO_ALG0x00000010 LZOアルゴリズム

パフォーマンスヒント
2041s_prealloc_blocks 通常ファイルを新規作成するときに事前割り当てするブロック数が格納されます。
2051s_prealloc_dir_blocks ディレクトリを新規作成するときに事前割り当てするブロック数が格納されます。
2062s_padding1 使用しない予約領域となります。
ジャーナリングサポート
20816s_journal_uuid ジャーナルスーパーブロックのUUIDが格納されます。
2244s_journal_inum ジャーナルファイルのinode番号が格納されます。
2284s_journal_dev ジャーナルファイルのデバイス番号が格納されます。
2324s_last_orphan 削除するinodeリストの先頭のinodeとなります。
ディレクトリインデキシングサポート
2364 x 4s_hash_seed 4つの配列となります。ディレクトリインデキシングに使用するハッシュアルゴリズムのシード値が格納されます。
2521s_def_hash_version ディレクトリインデキシングに使用するデフォルトハッシュのバージョンが格納されます。
2531s_reserved_char_pad 予約領域です。(将来拡張用)
2542s_reserved_word_pad 予約領域です。(将来拡張用)
その他オプション
2564s_default_mount_opts このファイルシステムのデフォルトマウントオプションが格納されます。
2604s_first_meta_bg 最初のメタブロックグループのブロックグループ番号が格納されます。
264760s_reserved 予約領域です。(将来拡張用)


それでは、スーパーブロック構造体を作成します。me2fs.hに定義を作ります。

ここでExt2ファイルシステムのスーパーブロック、ブロックグループディスクリプターや

inodeなどの情報は全てリトルエンディアンで格納されていることが重要になってきます。

リトルエンディアンでは、例えばint型が4バイトであるシステムの場合下位のバイト

から順番に格納されていきます。逆にビッグエンディアンでは上位のバイトから順番に

格納されていきます。ここで、注意していただきたいのが、ARMなどのプロセッサーで

ビッグエンディアンの設定で動作させている場合でも、ディスクに格納されている

Ext2ファイルシステムの管理情報はすべてリトルエンディアンで格納されていると

いうことです。


例えば、人から見て0x12345678という4バイトのデータがメモリーに格納される

とするとリトルエンディアンとビッグエンディアンで次のようにバイト順序が異なります。

エンディアンの違い


このため、Linuxではリトルエンディアンであることをプログラム中に意図的に表記

するために、型を設けています。特にプログラム動作自体問題になることはありませんが

間違いのもとになってしまいますので、なるべくその型を使用するようにしておくとよい

です。


リトルエンディアンの型宣言は次のようになっています。次の型は全てその実態は

符号なしの型となります。

				
__le16
__le32
__le64



リトルエンディアンデータの型
説明
__le16符号なしの16ビットデータの型となります。ここでleはLittle Endianを意味しています。
__le32符号なしの32ビットデータの型となります。
__le64符号なしの64ビットデータの型となります。


補足
8ビットデータは1バイトとなりますので、エンディアンの影響はありません。

__u8の型(実体はunsigned charです)を使用してください。



				
__be16
__be32
__be64



ビッグエンディアンデータの型
説明
__be16符号なしの16ビットデータの型となります。ここでbeはBig Endianを意味しています。
__be32符号なしの32ビットデータの型となります。
__be64符号なしの64ビットデータの型となります。


この型を使ってスーパーブロックを定義していきましょう。次の定義はLinux

ソースを基に作成していて、一部__u32や__u16で定義されていますが、

コンパイルすると同じことになります。

[me2fs.h]

/********************************************************************************
    File            : me2fs.h
    Description     : Defines for my ext2 file system

********************************************************************************/
#ifndef __ME2FS_H__
#define __ME2FS_H__

/*
=================================================================================

    Prototype Statement

=================================================================================
*/

/*
=================================================================================

    DEFINES

=================================================================================
*/
/*
---------------------------------------------------------------------------------
    Ext2 Super Block
---------------------------------------------------------------------------------
*/
struct ext2_super_block
{
    __le32  s_inodes_count;
    __le32  s_blocks_count;
    __le32  s_r_blocks_count;               /* reserved blocks count            */
    __le32  s_free_blocks_count;
    __le32  s_free_inodes_count;
    __le32  s_first_data_block;
    __le32  s_log_block_size;
    __le32  s_log_frag_size;
    __le32  s_blocks_per_group;
    __le32  s_frags_per_group;
    __le32  s_inodes_per_group;
    __le32  s_mtime;                        /* mount time                       */
    __le32  s_wtime;                        /* write time                       */
    __le16  s_mnt_count;
    __le16  s_max_mnt_count;
    __le16  s_magic;
    __le16  s_state;                        /* file system state                */
    __le16  s_errors;                       /* behaviour when detecting error   */
    __le16  s_minor_rev_level;              /* minor revision level             */
    __le32  s_lastcheck;                    /* time of last check               */
    __le32  s_checkinterval;                /* max. time between checks         */
    __le32  s_creator_os;
    __le16  s_rev_level;                    /* revision level                   */
    __le16  s_def_resuid;                   /* default uid for reserved blocks  */
    __le16  s_def_resgid;                   /* default gid for reserved blocks  */
    /* ------------------------------------------------------------------------ */
    /* Dynamic Revision                                                         */
    /* ------------------------------------------------------------------------ */
    __le32  s_first_ino;                    /* first non-reserved inode         */
    __le16  s_inode_size;                   /* size of inode structure          */
    __le16  s_block_group_nr;               /* block group # of this superblock */
    __le32  s_feature_compat;               /* compatible feature set           */
    __le32  s_feature_incompat;             /* incompatible feature set         */
    __le32  s_feature_ro_compat;            /* readonly-compatible feature set  */
    __u8    s_uuid[ 16 ];                   /* 128-bit uuid for volume          */
    char    s_volume_name[ 16 ];            /* volume name                      */
    char    s_last_mounted[ 64 ];           /* directory where last mounted     */
    __le32  s_algorithm_usage_bitmap;       /* For compression                  */
    /* ------------------------------------------------------------------------ */
    /* Peformance Hints                                                         */
    /* ------------------------------------------------------------------------ */
    __u8    s_prealloc_blocks;              /* # of blocks to try to preallocate*/
    __u8    s_prealloc_dir_blocks;          /* # of preaalocat for dirs         */
    __u16   s_padding1;
    /* ------------------------------------------------------------------------ */
    /* Journaling Support                                                       */
    /* ------------------------------------------------------------------------ */
    __u8    s_journal_uuid[ 16 ];           /* uuid of journal superblock       */
    __u32   s_journal_inum;                 /* inode number of journal file     */
    __u32   s_journal_dev;                  /* device number of journal file    */
    __u32   s_last_orphan;                  /* start of list of inodes to delete*/
    /* ------------------------------------------------------------------------ */
    /* Directory Indexing Support                                               */
    /* ------------------------------------------------------------------------ */
    __u32   s_hash_seed[ 4 ];               /* HTREE hash seed                  */
    __u8    s_def_hash_version;             /* default hash version             */
    __u8    s_reserved_char_pad;
    __u16   s_reserved_word_pad;
    /* ------------------------------------------------------------------------ */
    /* Other options                                                            */
    /* ------------------------------------------------------------------------ */
    __le32  s_default_mount_opts;
    __le32  s_first_meta_bg;                /* first metablock block group      */
    __u32   s_reserved[ 190 ];              /* padding to the end               */

};

/*
==================================================================================

    Management

==================================================================================
*/

/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    < Open Functions >

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
#endif  // __ME2FS_H__



それでは、スーパーブロックの読み込み処理について見ていきましょう。

ページキャッシュとバッファーキャッシュ概要

スーパーブロックの読み込みは当然ですが、ブロックデバイスに格納されて

いるデータを読み出し、メモリーに格納する必要があります。しかし、データが

必要になるたびにディスクににアクセスしていては、システム全体の処理が

ディスクI/Oのために遅くなってしまいます。CPUの処理よりも、ディスクI/Oに

かかる処理時間が非常に遅いためにです。このため、いったんディスクから読み

こんだデータをメモリーに一定期間留めておいてキャッシュとして使用することで

システムパフォーマンスの向上を狙っています。


ここで、メモリーに格納するデータは、例えば今回の例ではスーパーブロックは1024

バイトだったり、inodeは128バイトでした。スーパーブロックやinodeをディスクから

読み出す時に単純に1024バイト分のデータや128バイト分のデータを読み込めば

いいではと思うのですが、単純に読み込んでしまうとメモリーの効率上よくありません。

汎用OSではページングで仮想メモリーを実現しています。このため、カーネルが

システムメモリーの管理を行う基本単位がページのサイズとなっています。(今回は

慣習的にページサイズを4096として考えてみましょう。ページサイズはシステムに

よっては、更に大きいサイズを扱うことができます。)ここでページサイズが4096バイトの

場合、スーパーブロック用のページを割り1024バイトのデータを読み込みメモリーに

読み込んでいては、ページの残りの部分は無駄になってしまいます。そこで、ページ内を

分割してデータを管理を行ったほうがよいことは自ずとわかります。このページ内の

分割した領域をバッファーキャッシュとしてカーネルが管理を行っていきます。

また、ディスクキャッシュとして使用しているメモリーをページキャッシュ

言います。


ファイルシステムでは基本的にページサイズより小さい管理データを持っていますので

バッファーキャッシュを意識して使用していくことになります。


バッファーキャッシュとページキャッシュ

そのページにバッファーキャッシュがあるということは、page構造体のflagsに

PG_privateが設定されていることで判断できます。PG_privateが設定された

page構造体のprivateにバッファーキャッシュの管理データの先頭アドレスが格納

されています。ここでは、更にバッファーキャッシュの管理データについて見ていきます。

バッファーキャッシュの管理データはLinuxではbuffer_head構造体になります。

buffer_head構造体

buffer_head構造体は1つのバッファーキャッシュについて管理を行います。

次のような構造体となります。


struct buffer_head {
        unsigned long        b_state;     /* buffer state bitmap (see above) */
        struct buffer_head   *b_this_page;/* circular list of page's buffers */
        struct page          *b_page;     /* the page this bh is mapped to   */

        sector_t             b_blocknr;   /* start block number              */
        size_t               b_size;      /* size of mapping                 */
        char                 *b_data;     /* pointer to data within the page */

        struct block_device  *b_bdev;
        bh_end_io_t          *b_end_io;   /* I/O completion                  */
        void                 *b_private;  /* reserved for b_end_io           */
        struct list_head b_assoc_buffers; /* associated with another mapping */
        struct address_space *b_assoc_map;/* mapping this buffer is
                                             associated with                 */
        atomic_t             b_count;     /* users using this buffer_head    */
};



buffer_head構造体
メンバー 説明
unsigned longb_state バッファーキャッシュの状態フラグです。次のような状態フラグがあります。(実際には次のenumの値で、状態を示すビットフラグを作成して使います。各フラグは状態が矛盾しない限りORで連結できます)。

enum bh_state_bits {
    BH_Uptodate,
    BH_Dirty,
    BH_Lock,
    BH_Req,
    BH_Uptodate_Lock,

    BH_Mapped,
    BH_New,
    BH_Async_Read,
    BH_Async_Write,
    BH_Delay,
    BH_Boundary,
    BH_Write_EIO,
    BH_Unwritten,
    BH_Quiet,
    BH_Meta,
    BH_Prio,
    BH_Defer_Completion,

    BH_PrivateStart,
};

バッファーキャッシュの状態フラグ
フラグ 説明
BH_Uptodate バッファーキャッシュは有効なデータ(最新のデータ)となります。
BH_Dirty バッファーキャッシュのデータは更新されています(ダーティ)。デバイスにデータを書き戻す必要があります。
BH_Lock バッファーキャッシュがロック中です。(ディスクI/O中のバッファーです)。
BH_Req バッファーキャッシュのデータを入力出力するようにI/Oレイヤーに要求を行いました。
BH_Uptodate_Lock このフラグはページキャッシュ内の最初のbuffer_headに適用されます。このフラグがセットされていると、ページ内のバッファーキャッシュのI/Oを順番に完了していきます。
BH_Mapped バッファーキャッシュをディスクにマッピングしています。このフラグがセットされている場合、buffer_headのb_devとb_blocknrがマッピング対象のブロックを表しています。
BH_New バッファーキャッシュとディスクのマッピングが新しく作られた状態です。(後で出てくるget_blockでディスクブロックとバッファーキャッシュを対応付けます)。
BH_Async_Read バッファーキャッシュは非同期読み込み処理中です。
BH_Async_Write バッファーキャッシュは非同期書き込み処理中です。
BH_Delay バッファーキャッシュに対応するディスクブロックはまだ割り当てられていません。
BH_Boundary この次のブロックは連続していないブロックです。(このバッファーキャッシュは、連続したブロックのなかで最後のブロックに対応したバッファーキャッシュです)。
BH_Write_EIO バッファーキャッシュを対応するブロックに書き込みしている最中にI/Oエラーが発生しました。
BH_Unwritten バッファーキャッシュに対応するブロックは割り当てられましたが、まだバッファーキャッシュの内容を書き戻していません。
BH_Quiet バッファーキャッシュの処理に関してprintkエラー出力を抑制します。
BH_Meta バッファーキャッシュのデータはメタデータとなります。
BH_Prio I/Oの要求を行うときにREQ_PRIOを指定します。
BH_Defer_Completion ワークキューに依頼していた非同期I/Oが完了しました。
BH_PrivateStart これ以降のビットは各モジュールでプライベートに使用することができます。

補足
BH_PrivateStart以降のビットを使用してファイルシステム独自のフラグを使用することもできます。
struct buffer_head *b_this_page ページキャッシュ内のバッファーキャッシュをつなげる連結リストです。
struct page *b_page このバッファーキャッシュが所属するページキャッシュのpage構造体です。
sector_tb_blocknr バッファーキャッシュに対応するブロックの論理ブロック番号です。デバイスの物理ブロック番号ではありません。
size_tb_size バッファーキャッシュに対応するブロックのサイズが格納されます。
char *b_data バッファーキャッシュのデータです。
struct block_device *b_bdev バッファーキャッシュのデータが書き戻されるブロックデバイスです。
bh_end_io_t *b_end_io I/Oの完了メソッドです。
void *b_private I/Oの完了メソッドで使用します。
struct list_headb_assoc_buffers inodeの関節参照ブロックに使用する連結リストです。
struct address_space *b_assoc_map このバッファーキャッシュが所属するブロックマッピング情報となります。
atomic_tb_count このバッファーキャッシュの参照カウンターです。


page構造体とbuffer_head構造体の関係は次のようになっています。

page構造体とbuffer_head構造体の関係

ファイルシステムモジュールではファイルシステムのメタデータをバッファーキャッシュに

読み込んでから処理を行います。これにより、ディスクデータがメモリーにキャッシュ

されることになります。また、データを書き込むときはバッファーキャッシュのデータを

更新します。ディスクデータの読み込みまたは書き込みはI/Oスケジューラーに

リクエストすることで行います。リクエストを受けるとI/Oスケジューラーは要求をキュー

に入れて、処理時間がかかるディスクのシーク動作をなるべく少なくなるようように、

I/Oの順番をスケジューリングします。

スーパーブロックをバッファーキャッシュに読み込む

それでは、スーパーブロックを読み込んでいきます。スーパーブロックの読み込み処理は

キャッシュレイヤーの共通処理として実装されいますので、これを使っていきます。

他にも関連する関数が定義されていますので、ついでに見ていきましょう。今回使用

しない関数もあります。次の関数となります。

				
# include <buffer_head.h>

struct buffer_head *
sb_bread(struct super_block *sb, sector_t block);

struct buffer_head *
sb_bread_unmovable(struct super_block *sb, sector_t block);

void
sb_breadahead(struct super_block *sb, sector_t block);

struct buffer_head *
sb_getblk(struct super_block *sb, sector_t block);

struct buffer_head *
sb_find_get_block(struct super_block *sb, sector_t block);



スーパーブロックを指定したディスクブロックの読み出し関数
関数 説明
sb_bread指定したスーパーブロックオブジェクトに関連付けられたブロックデバイスから、指定した論理ブロックのデータをバッファーキャッシュに読み込みます。
sb_bread_unmovablesb_breadと同じです。ただし、移動(マイグレーション)できないページのバッファーキャッシュに読み込まれます。
sb_breadahead指定した論理ブロックから事前読み込みを行います。
sb_getblk指定したスーパーブロックオブジェクトに関連付けられたブロックデバイスから、指定した論理ブロックのデータをバッファーキャッシュに読み込みます。sb_bread関数と違うのは割り当てられるページがMOVABLEとなります。つまりスワップ時にページを置換可能とします。
sb_find_get_block指定した論理ブロックをバッファーキャッシュから取得します。


sb_bread関数

sb_bread関数は仮想ファイルシステムのスーパーブロックオブジェクトを指定します。

スーパーブロックオブジェクトに関連付けされているブロックデバイスからブロックを読み込み

します。また、読み込みしたいブロック番号を論理ブロックで指定します。指定した

ブロックが既にバッファーキャッシュされている場合には、そのバッファーキャッシュの

buffer_headを返します。キャッシュが無ければディスクにアクセスしてデータを読み

ます。

補足
このステップで言う論理ブロックとは、実際のデバイス上のブロック番号ではありません。

例えば、ディスクのパーティションを区切っている場合、各パーティションの先頭

ブロックからの相対ブロック番号となります。



引数は次のようになります。

sb_read関数の引数
名前 説明
struct super_block *sb 仮想ファイルシステムのスーパーブロックオブジェクトを指定します。
sector_tblock 読み込みしたいブロックの論理ブロック番号を指定します。


戻り値は次のようになります。

sb_read関数の戻り値
説明
struct buffer_head * 論理ブロック番号に対応したbuffer_headを返します。失敗した場合はNULLを返します。


sb_bread_unremovable関数、sb_find_get_block関数の引数と

戻り値もsb_bread関数と同じです。sb_find_get_block関数は

sb_bread関数のディスク読み込みを行わない動作となります。つまり、

指定した論理ブロック番号に対応するバッファーキャッシュを探すときに、メモリー上に

既にキャッシュされているバッファーキャッシュから探します。

バッファーキャッシュの解放

また、バッファーキャッシュには参照カウンターがあります。これらの関数を使用して

バッファーキャッシュにデータを読み込んだときに参照カウンターを増やすことで

そのバッファーキャッシュが使用中であるこを示しておきます。バッファーキャッシュの

参照カウンターはbuffer_head構造体のb_countメンバーとなります。こうすることで、

勝手にページが解放されることがなくなります。一旦バッファーキャッシュのデータ

が必要でなくなったら、この参照カウンターを減らして解放できるようにしておかないと

いつまでたってもメモリーが解放されません。このため、不要になったバッファーキャッシュ

を解放する(参照カウンターを減らす)関数が用意されています。忘れずに呼び出して

おきましょう。

				
# include <buffer_head.h>

void brelse(struct buffer_head *bh);



brelseはmallocに対してfreeを呼びだすように使用します。

sb_bread関数を使ってスーパーブロックを読み込む

それでは、sb_bread関数を使用してスーパーブロックを読み込んでいきます。

Ext2ファイルシステムでは最初の1024バイト目にスーパーブロックが格納されて

いました。ここではブロックサイズが1024バイトと仮定すると次のようにsb_bread

関数を呼び出します。論理ブロック番号として1を指定しています。最初の1024

バイトは論理ブロック0となります。

				
struct buffer_head *bh;

bh = sb_bread( sb, 1 );



スーパーブロックオブジェクトのアドレスはfill_superコールバック関数の引数

として渡されますので、それを指定します。


ここで読みこんだスーパーブロックのデータはbuffer_headのb_dataに格納

されています。b_dataはvoid型のポインターとなりますので、先ほど定義しました

スーパーブロック型(struct ext2_super_block)のポインターにキャストして

簡単にデータアクセスすることができます。

				
struct buffer_head      *bh;
struct ext2_super_block *esb;

bh  = sb_bread( sb, 1 );

esb = ( struct ext2_super_block* )( bh->b_data );



デバッグ用に読みこんだスーパーブロックの内容を出力する関数を作ります。

特に必要無い方は作らなくてもよいです。次のように作成します。特に決まり

はありませんので、好きなように作ってみましょう。

処理内容としては、ext2_super_blockのメンバーの各値を出力するだけ

となります。


ここで、le32_to_cpuマクロを使用していきます。前述していますが、Ext2

ファイルシステムのデータはリトルエンディアンで格納されていると説明していた

かと思います。le32_to_cpuマクロは指定したデータをCPUのエンディアンに

変換してくれるマクロとなります。例えば、ビッグエンディアンのCPUの場合は、

指定した値(必ずリトルエンディアンのデータを指定してください)をビッグエンディアン

のデータに変換します。パソコンのIntelプロセッサーの場合は、リトルエンディアン

のプロセッサーとなりますので、le32_to_cpuマクロは特に何も行いません。

通常の変数へのアクセスと同じとなります。しかし、Linuxではビッグエンディアンの

プロセッサー上でも動作させるために、このように移植性あるコードにしています。


エンディアン変換マクロについて簡単に見ていきましょう。次のようなマクロが

あります。これから使用していきます。数字がデータのビット数を表しています。


格納されているデータをCPUのエンディアンに変換するマクロは次のようになります。

(疑似的にマクロを関数として表現しています)。

[リトルエンディアンのデータをCPUのエンディアンに変換するマクロ]

__u64 le64_to_cpu( __le64 val );
__u32 le32_to_cpu( __le32 val );
__u16 le16_to_cpu( __le16 val );

__u64 le64_to_cpup( const __le64 *p );
__u32 le32_to_cpup( const __le32 *p );
__u16 le16_to_cpup( const __le16 *p );

void le64_to_cpus( __le64 *s );
void le32_to_cpus( __le32 *s );
void le16_to_cpus( __le16 *s );



リトルエンディアンのデータをCPUのエンディアンに変換するマクロ
マクロ名 引数 戻り値 説明
le64_to_cpu__le64 val__u64 64ビットのリトルエンディアンのデータvalをCPUのエンディアンに変換して返します。
le32_to_cpu__le32 val__u32 32ビットのリトルエンディアンのデータvalをCPUのエンディアンに変換して返します。
le16_to_cpu__le16 val__u16 16ビットのリトルエンディアンのデータvalをCPUのエンディアンに変換して返します。
le64_to_cpupconst __le64 *p__u64 ポインターpが指す64ビットのリトルエンディアンのデータをCPUのエンディアンに変換して返します。
le32_to_cpupconst __le32 *p__u32 ポインターpが指す32ビットのリトルエンディアンのデータをCPUのエンディアンに変換して返します。
le16_to_cpupconst __le16 *p__u16 ポインターpが指す16ビットのリトルエンディアンのデータをCPUのエンディアンに変換して返します。
le64_to_cpus__le64 *svoid ポインターsが指す64ビットのリトルエンディアンのデータをCPUのエンディアンに変換して、*sに出力します。
le32_to_cpus__le32 *svoid ポインターsが指す32ビットのリトルエンディアンのデータをCPUのエンディアンに変換して、*sに出力します。
le16_to_cpus__le16 *svoid ポインターsが指す16ビットのリトルエンディアンのデータをCPUのエンディアンに変換して、*sに出力します。


ここで、マクロ名の最後にpがつくものはポインターを指定してください。ポインター

を指定するとポインターが指しているデータをCPUのエンディアンに変換してその

値を返します。ポインターが指すデータの内容は変更されません。また、マクロ名

の最後にsがつくものもポインターを指定します。この場合は、ポインターが指す

データをCPUのエンディアンに変換して上書きします。

今回はle32_to_cpuとle16_to_cpuを使用していきます。


逆に、CPUのエンディアンをリトルエンディアンのデータに変換するには次のマクロを

使用します。

[CPUのエンディアンをリトルエンディアンのデータに変換するマクロ]

__le64 cpu_to_le64( __u64 val );
__le32 cpu_to_le32( __u32 val );
__le16 cpu_to_le16( __u16 val );

__le64 cpu_to_le64p( const __u64 *p );
__le32 cpu_to_le32p( const __u32 *p );
__le16 cpu_to_le16p( const __u16 *p );

void cpu_to_le64s( __u64 *s );
void cpu_to_le32s( __u32 *s );
void cpu_to_le16s( __u16 *s );



CPUのエンディアンをリトルエンディアンのデータに変換するマクロ
マクロ名 引数 戻り値 説明
cpu_to_le64__u64 val__le64 64ビットのCPUのエンディアンのデータvalをリトルエンディアンに変換して返します。
cpu_to_le32__u32 val__le32 32ビットのCPUのエンディアンのデータvalをリトルエンディアンに変換して返します。
cpu_to_le16__u16 val__le16 16ビットのCPUのエンディアンのデータvalをリトルエンディアンに変換して返します。
cpu_to_le64pconst __u64 *p__le64 ポインターpが指す64ビットのCPUのエンディアンのデータをリトルエンディアンに変換して返します。
cpu_to_le32pconst __u32 *p__le32 ポインターpが指す32ビットのCPUのエンディアンのデータをリトルエンディアンに変換して返します。
cpu_to_le16pconst __u16 *p__le16 ポインターpが指す16ビットのCPUのエンディアンのデータをリトルエンディアンに変換して返します。
cpu_to_le64s__u64 *svoid ポインターsが指す64ビットのCPUのエンディアンのデータをリトルエンディアンに変換して、*sに出力します。
cpu_to_le32s__u32 *svoid ポインターsが指す32ビットのCPUのエンディアンのデータをリトルエンディアンに変換して、*sに出力します。
cpu_to_le16s__u16 *svoid ポインターsが指す16ビットのCPUのエンディアンのデータをリトルエンディアンに変換して、*sに出力します。


今回はcpu_to_le32とcpu_to_le16を使用していきます。



今回使いませんが補足としてビッグエンディアンを見ていきましょう。

[ビッグエンディアンのデータをCPUのエンディアンに変換するマクロ]

__u64 be64_to_cpu( __be64 val );
__u32 be32_to_cpu( __be32 val );
__u16 be16_to_cpu( __be16 val );

__u64 be64_to_cpup( const __be64 *p );
__u32 be32_to_cpup( const __be32 *p );
__u16 be16_to_cpup( const __be16 *p );

void cpu_to_be64s( __be64 *s );
void cpu_to_be32s( __be32 *s );
void cpu_to_be16s( __be16 *s );



ビッグエンディアンのデータをCPUのエンディアンに変換するマクロ
マクロ名 引数 戻り値 説明
be64_to_cpu__be64 val__u64 64ビットのビッグエンディアンのデータvalをCPUのエンディアンに変換して返します。
be32_to_cpu__be32 val__u32 32ビットのビッグエンディアンのデータvalをCPUのエンディアンに変換して返します。
be16_to_cpu__be16 val__u16 16ビットのビッグエンディアンのデータvalをCPUのエンディアンに変換して返します。
be64_to_cpupconst __be64 *p__u64 ポインターpが指す64ビットのビッグエンディアンのデータをCPUのエンディアンに変換して返します。
be32_to_cpupconst __be32 *p__u32 ポインターpが指す32ビットのビッグエンディアンのデータをCPUのエンディアンに変換して返します。
be16_to_cpupconst __be16 *p__u16 ポインターpが指す16ビットのビッグエンディアンのデータをCPUのエンディアンに変換して返します。
be64_to_cpus__be64 *svoid ポインターsが指す64ビットのビッグエンディアンのデータをCPUのエンディアンに変換して、*sに出力します。
be32_to_cpus__be32 *svoid ポインターsが指す32ビットのビッグエンディアンのデータをCPUのエンディアンに変換して、*sに出力します。
be16_to_cpus__be16 *svoid ポインターsが指す16ビットのビッグエンディアンのデータをCPUのエンディアンに変換して、*sに出力します。


[CPUのエンディアンをビッグエンディアンのデータに変換するマクロ]

__be64 cpu_to_be64( __u64 val );
__be32 cpu_to_be32( __u32 val );
__be16 cpu_to_be16( __u16 val );

__be64 cpu_to_be64p( const __u64 *p );
__be32 cpu_to_be32p( const __u32 *p );
__be16 cpu_to_be16p( const __u16 *p );

void be64_to_cpus( __u64 *s );
void be32_to_cpus( __u32 *s );
void be16_to_cpus( __u16 *s );



CPUのエンディアンをビッグエンディアンのデータに変換するマクロ
マクロ名 引数 戻り値 説明
cpu_to_be64__u64 val__be64 64ビットのCPUのエンディアンのデータvalをビッグエンディアンに変換して返します。
cpu_to_be32__u32 val__be32 32ビットのCPUのエンディアンのデータvalをビッグエンディアンに変換して返します。
cpu_to_be16__u16 val__be16 16ビットのCPUのエンディアンのデータvalをビッグエンディアンに変換して返します。
cpu_to_be64pconst __u64 *p__be64 ポインターpが指す64ビットのCPUのエンディアンのデータをビッグエンディアンに変換して返します。
cpu_to_be32pconst __u32 *p__be32 ポインターpが指す32ビットのCPUのエンディアンのデータをビッグエンディアンに変換して返します。
cpu_to_be16pconst __u16 *p__be16 ポインターpが指す16ビットのCPUのエンディアンのデータをビッグエンディアンに変換して返します。
cpu_to_be64s__u64 *svoid ポインターsが指す64ビットのCPUのエンディアンのデータをビッグエンディアンに変換して、*sに出力します。
cpu_to_be32s__u32 *svoid ポインターsが指す32ビットのCPUのエンディアンのデータをビッグエンディアンに変換して、*sに出力します。
cpu_to_be16s__u16 *svoid ポインターsが指す16ビットのCPUのエンディアンのデータをビッグエンディアンに変換して、*sに出力します。


それでは、デバッグ用に読みこんだスーパーブロックの内容を出力する関数を見ていきましょう。

次の例のように作成しました。

[me2fs_super.cの一部]

/*
=================================================================================

    Prototype Statement

=================================================================================
*/
static void dbgPrintExt2SB( struct ext2_super_block *esb );

/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    < Local Functions >

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/*
==================================================================================
    Function    :dbgPrintExt2SB
    Input       :struct ext2_super_block *esb
                 < super block of ext2 file system >
    Output      :void
    Return      :void

    Description :print ext2 super block
==================================================================================
*/
static void dbgPrintExt2SB( struct ext2_super_block *esb )
{
    unsigned int value;
    int i;
    value = ( unsigned int )( le32_to_cpu( esb->s_inodes_count ) );
    DBGPRINT( "<ME2FS>s_inode_count = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_blocks_count ) );
    DBGPRINT( "<ME2FS>s_blocks_count = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_r_blocks_count ) );
    DBGPRINT( "<ME2FS>s_r_blocks_count = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_free_blocks_count ) );
    DBGPRINT( "<ME2FS>s_free_blocks_count = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_free_inodes_count ) );
    DBGPRINT( "<ME2FS>s_free_inodes_count = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_first_data_block ) );
    DBGPRINT( "<ME2FS>s_first_data_block = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_log_block_size ) );
    DBGPRINT( "<ME2FS>s_log_block_size = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_log_frag_size ) );
    DBGPRINT( "<ME2FS>s_log_frag_size = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_blocks_per_group ) );
    DBGPRINT( "<ME2FS>s_blocks_per_group = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_frags_per_group ) );
    DBGPRINT( "<ME2FS>s_frags_per_group = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_inodes_per_group ) );
    DBGPRINT( "<ME2FS>s_inodes_per_group = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_mtime ) );
    DBGPRINT( "<ME2FS>s_mtime = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_wtime ) );
    DBGPRINT( "<ME2FS>s_wtime = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_mnt_count ) );
    DBGPRINT( "<ME2FS>s_mnt_count = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_max_mnt_count ) );
    DBGPRINT( "<ME2FS>s_max_mnt_count = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_magic ) );
    DBGPRINT( "<ME2FS>s_magic = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_state ) );
    DBGPRINT( "<ME2FS>s_state = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_errors ) );
    DBGPRINT( "<ME2FS>s_errors = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_minor_rev_level ) );
    DBGPRINT( "<ME2FS>s_minor_rev_level = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_lastcheck ) );
    DBGPRINT( "<ME2FS>s_lastcheck = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_creator_os ) );
    DBGPRINT( "<ME2FS>s_creator_os = %u\n", value );
    value = ( unsigned int )( le16_to_cpu( esb->s_rev_level ) );
    DBGPRINT( "<ME2FS>s_rev_level = %u\n", value );
    value = ( unsigned int )( le16_to_cpu( esb->s_def_resuid ) );
    DBGPRINT( "<ME2FS>s_def_resuid = %u\n", value );
    value = ( unsigned int )( le16_to_cpu( esb->s_def_resgid ) );
    DBGPRINT( "<ME2FS>s_def_resgid = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_first_ino ) );
    DBGPRINT( "<ME2FS>s_first_ino = %u\n", value );
    value = ( unsigned int )( le16_to_cpu( esb->s_inode_size ) );
    DBGPRINT( "<ME2FS>s_inode_size = %u\n", value );
    value = ( unsigned int )( le16_to_cpu( esb->s_block_group_nr ) );
    DBGPRINT( "<ME2FS>s_block_group_nr = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_feature_compat ) );
    DBGPRINT( "<ME2FS>s_feature_compat = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_feature_incompat ) );
    DBGPRINT( "<ME2FS>s_feature_incompat = %u\n", value );
    DBGPRINT( "<ME2FS>s_uuid[ 16 ] = " );
    for( i = 0 ; i < 16 ; i++ )
    {
        DBGPRINT( "%02X", esb->s_uuid[ i ] );
    }
    DBGPRINT( "\n" );
    value = ( unsigned int )( le32_to_cpu( esb->s_journal_inum ) );
    DBGPRINT( "<ME2FS>s_journal_inum = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_journal_dev ) );
    DBGPRINT( "<ME2FS>s_journal_dev = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_last_orphan ) );
    DBGPRINT( "<ME2FS>s_last_orphan = %u\n", value );
    DBGPRINT( "<ME2FS>s_hash_seed[ 4 ] = " );
    for( i = 0 ; i < 4 ; i++ )
    {
        DBGPRINT( "%16X", esb->s_hash_seed[ i ] );
    }
    DBGPRINT( "\n" );
    value = ( unsigned int )( esb->s_def_hash_version );
    DBGPRINT( "<ME2FS>s_def_hash_version = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_default_mount_opts ) );
    DBGPRINT( "<ME2FS>s_default_mount_opts = %u\n", value );
    value = ( unsigned int )( le32_to_cpu( esb->s_first_meta_bg ) );
    DBGPRINT( "<ME2FS>s_first_meta_bg = %u\n", value );
}




最後にfill_superコールバック関数のme2fsFillSuperBlock関数

について見ていきましょう。

[me2fs_super.cの一部]
				
/********************************************************************************
    File            : me2fs_super.c
    Description     : super block operations for my ext2 file sytem

********************************************************************************/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h>

#include "me2fs.h"
#include "me2fs_util.h"

/*
=================================================================================

    Prototype Statement

=================================================================================
*/
static int me2fsFillSuperBlock( struct super_block *sb,
                                void *data,
                                int silent );

static void dbgPrintExt2SB( struct ext2_super_block *esb );
/*
=================================================================================

    DEFINES

=================================================================================
*/

/*
==================================================================================

    Management

==================================================================================
*/

/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    < Open Functions >

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    Function    :me2fsMountBlockDev
    Input       :struct file_system_type *fs_type
                 < file system type >
                 int flags
                 < mount flags >
                 const char *dev_name
                 < device name >
                 void *data
                 < user data >
    Output      :void
    Return      :struct dentry *
                 < root dentry >

    Description :mount me2fs over block device
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
struct dentry*
me2fsMountBlockDev( struct file_system_type *fs_type,
                    int flags,
                    const char *dev_name,
                    void *data )
{
    return( mount_bdev( fs_type, flags, dev_name, data, me2fsFillSuperBlock ) );
}

/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    < Local Functions >

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/*
==================================================================================
    Function    :me2fsFillSuperBlock
    Input       :struct super_block *sb
                 < vfs super block object >
                 void *data
                 < user data >
                 int silent
                 < silent flag >
    Output      :void
    Return      :void

    Description :fill my ext2 super block object
==================================================================================
*/
static int me2fsFillSuperBlock( struct super_block *sb,
                                void *data,
                                int silent )
{
    struct buffer_head      *bh;
    struct ext2_super_block *esb;
    int                     block_size;
    int                     ret = -EINVAL;
    unsigned long           sb_block = 1;

    /* ------------------------------------------------------------------------ */
    /* set device's block size and size bits to super block                     */
    /* ------------------------------------------------------------------------ */
    block_size = sb_min_blocksize( sb, BLOCK_SIZE );

    DBGPRINT( "<ME2FS>Fill Super! block_size = %d\n", block_size );
    DBGPRINT( "<ME2FS>default block size is : %d\n", BLOCK_SIZE );

    if( !block_size )
    {
        DBGPRINT( "<ME2FS>error: unable to set blocksize\n" );
        return( ret );
    }

    if( !( bh = sb_bread( sb, 1 ) ) )
    {
        DBGPRINT( "<ME2FS>failed to bread super block\n" );
        return( ret );
    }

    esb = ( struct ext2_super_block* )( bh->b_data );

    dbgPrintExt2SB( esb );

    return( 0 );
}



ここで、sb_min_blocksize関数が出てきました。仮想ファイルシステムの

スーパーブロックオブジェクトにブロックデバイスのブロックサイズを設定します。

次のような関数となっています。指定したsizeがブロックデバイスのブロック

サイズより大きい場合はsizeの値をスーパーブロックsbにセットします。戻り値は

セットしたサイズを返します。ここではブロックサイズとしてデフォルトのBLOCK_SIZE

をセットするようにしています。BLOCK_SIZEは1024となっています。この処理は

1024バイトより大きいブロック(物理デバイスの最低単位のセクターサイズにあたり

ます)を扱うデバイス用の処理となります。今回例で使用するループバックデバイスには

あまり関係ありません。


また、sb_min_blocksize関数はs_blocksize_bitsにも値をセットします。


int sb_min_blocksize( struct super_block *sb, int size );



それでは、コンパイルしてモジュールをインストールしてからマウントしてみましょう。

				
$ make
$ make rmmod
$ make insmod
$ make mount
$ dmesg



これでスーパーブロックの読み込みが一応できました。まだマウント自体は失敗します。

dmesgでは次のように表示されました。マウント時のエラーメッセージも出力されています

が省略しています。

				
[57039.906844] (Me2FS)Hello, world
[57046.937175] <ME2FS>Fill Super! block_size = 1024
[57046.937178] <ME2FS>default block size is : 1024
[57046.942829] <ME2FS>s_inode_count = 5016
[57046.942831] <ME2FS>s_blocks_count = 20000
[57046.942832] <ME2FS>s_r_blocks_count = 1000
[57046.942833] <ME2FS>s_free_blocks_count = 19191
[57046.942833] <ME2FS>s_free_inodes_count = 5003
[57046.942834] <ME2FS>s_first_data_block = 1
[57046.942835] <ME2FS>s_log_block_size = 0
[57046.942835] <ME2FS>s_log_frag_size = 0
[57046.942836] <ME2FS>s_blocks_per_group = 8192
[57046.942836] <ME2FS>s_frags_per_group = 8192
[57046.942837] <ME2FS>s_inodes_per_group = 1672
[57046.942837] <ME2FS>s_mtime = 1411568763
[57046.942838] <ME2FS>s_wtime = 1411571984
[57046.942839] <ME2FS>s_mnt_count = 2
[57046.942839] <ME2FS>s_max_mnt_count = 65535
[57046.942840] <ME2FS>s_magic = 61267
[57046.942840] <ME2FS>s_state = 0
[57046.942841] <ME2FS>s_errors = 1
[57046.942841] <ME2FS>s_minor_rev_level = 0
[57046.942842] <ME2FS>s_lastcheck = 1411571984
[57046.942843] <ME2FS>s_creator_os = 0
[57046.942843] <ME2FS>s_rev_level = 1
[57046.942844] <ME2FS>s_def_resuid = 0
[57046.942844] <ME2FS>s_def_resgid = 0
[57046.942845] <ME2FS>s_first_ino = 11
[57046.942845] <ME2FS>s_inode_size = 128
[57046.942846] <ME2FS>s_block_group_nr = 0
[57046.942846] <ME2FS>s_feature_compat = 56
[57046.942847] <ME2FS>s_feature_incompat = 2
[57046.942848] <ME2FS>s_uuid[ 16 ] = 
[57046.942849] 583A
[57046.942850] EE89
[57046.942850] CBB1
[57046.942851] 4782
[57046.942852] 9569
[57046.942852] 5040
[57046.942853] F26E
[57046.942854] A4CC
[57046.942855] 
[57046.942855] <ME2FS>s_journal_inum = 0
[57046.942856] <ME2FS>s_journal_dev = 0
[57046.942856] <ME2FS>s_last_orphan = 0
[57046.942857] <ME2FS>s_hash_seed[ 4 ] = 
[57046.942857]         1431873D        D9471E33
[57046.942858]         CD408B9B         1066AFF
[57046.942859] 
[57046.942859] <ME2FS>s_def_hash_version = 1
[57046.942860] <ME2FS>s_default_mount_opts = 12
[57046.942861] <ME2FS>s_first_meta_bg = 0



これでディスクに格納されているスーパーブロックがとりあえず読み込める

ようになりました。


fill_superコールバック関数のme2fsFillSuperBlock関数に

ついて実装を続けていきます。

PR 説明

ディベロッパー・エクスペリエンス Linux Ext2ファイルシステム
0から作るLinuxプログラム Linux Ext2ファイルシステムの完全説明版(Kindleのみ)となります。実装するステップ数は36ステップあります。少しずつステップを実装して、実際に動かして学習していきます。ファイルシステムの機能を呼び出すシステムコールの実装から、仮想ファイルシステムが呼び出すExt2ファイルシステムの実装を行っていきます。ファイルシステムの開発体験を通すことで、実際にカーネルが行っているファイルシステムの基礎的な動作を理解することができます。ぜひお試しください!

inserted by FC2 system