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

日々勉強中。。。

Tips
リンク

ファイルシステム >

0から作るLinuxプログラム Ext2ファイルシステム ステップ0 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 ディスククォータ

Ext2ファイルシステムについて

今更ながらですが、Ext2はSecond Extended Filesystemの略となります。

最初にRemy Cardさん、Theodore Ts'oさん、Stephen Tweedieさんが主として

Extended Filesystemを描きなおし、1993年1月にLinuxカーネルに組み込みました。

更に、Ext2ファイルシステムからファイルシステムのデータ構造はそのままえに、機能を追加

してExt3やExt4ファイルシステムが作られています。現在の実装ではExt4ファイルシステム

ですが、内部的にExt2やExt3として動作することもできるようになっています。Linux以外

のOSにも実装されていてNetBSD、FreeBSD、GNU HURD、windows、IBM OS/2でも

使用することができます。現在でもExt2が使用される場面があり、書き込み操作をあまり

行いたくないフラッシュドライブなどに使用されています。Ext3やExt4はExt2をベースに

ジャーナリング機能などが追加されていますので、Ext2でファイルシステムの基本機能を

学ぶことでExt3やExt4で追加された機能も、あとは差分を学習していくことになります

ので、ぜひこの機会に挑戦していきましょう!

Ext2ファイルシステムの実装範囲について

ここまでで、ユーザーアプリケーションからのファイルシステム関連のシステムコールに

より、仮想ファイルシステムの処理が行われ、仮想ファイルシステムの共通オブジェクト

のメソッドで各ファイルシステム固有の処理が行われることがわかりました。ここでは

ファイルシステム固有の処理、つまりExt2ファイルシステムの処理の実装について

見ていきます。実装していくExt2の実装範囲は次の図のようになっています。

Ext2ファイルシステムの実装範囲

簡単なモジュールの実装

仮想ファイルシステムでは説明ばかりとなっていましたので、ここで一旦実装について

見ていきたいと思います。今回の実装では、Ext2ファイルシステムをモジュールとして

ステップバイステップで実装していきます。



ここで作成していくファイルシステムの名前はME2FS (My Ext2 FileSyste)

しています。既存のファイルシステム名と違う名前であれば、どんな名前にしていただいて

問題ありませんので、自分で好きな名前を付けてみましょう。その場合、今後出てくる

ME2FSを自分でつけたファイルシステム名に適宜置き換えていってください。


それではまず、簡単なカーネルモジュールを作って見ましょう。

簡単なモジュールの実装方法について

ここでは、ほぼ何もしないカーネルモジュールを作って、実際にカーネルにロードして

動かしていきます。

まず、モジュールのメインプログラムを作成します。モジュールのプログラムには次の

ヘッダーが必要となります。


#include <linux/module.h>



このヘッダーファイルはカーネルモジュールを作成するのに必要な定義が行われています。

たくさんの定義が行われていて、それぞれ細かく見ていきたいところですが、今回はファイル

システムの説明となりますので、モジュール作成については最低限のところを見ていきます。

モジュールの初期化関数と終了関数

linux/module.hをインクルードすると次のマクロがモジュールで使用できるようになります。


module_init( init_function );
module_exit( exit_function );



module_initマクロのinit_functionにはモジュールがロードされたときに呼び出して

欲しい関数アドレスを指定します。これにより、モジュールがロードされたときにモジュールで

使用する変数を初期化したり、メモリーを確保したりできます。便利です。また、module_exit

マクロはその反対でモジュールをカーネルから削除したときに、削除される前に呼び出して

欲しい関数アドレスを指定します。例えば、モジュールで確保していたメモリーを解放したい

場合に定義しておきます。

それぞれの関数アドレスに指定する関数の型は決まっていて次のような関数を作ってください。

モジュール初期化関数は次のように引数なしでint型を返す関数となります。


// モジュール初期化関数を次の例のように作成します。
static int __init initMe2fs( void )
{
    // 初期化処理
}
module_init( initMe2fs ); // 作成した関数を登録します。



モジュール初期化関数の戻り値はint型です。正常終了時には0を返すようにしてください。

異常時には0以外を返します。


モジュールが削除された時の関数は次のように引数・戻り値もvoid型の関数となります。


// モジュールが削除されたときに呼び出される関数を次の例のように作成します。
static void __exit exitMe2fs( void )
{
    // モジュールが削除される前に行う処理
}
module_exit( exitMe2fs );



補足
関数名の前の__initはその関数をモジュールの.init.textセクションに

配置するためのリンカーに向けた宣言となります。また、__exitはその関数を

モジュールの.exit.textセクションに配置するための宣言となります。カーネル

このセクションから関数を探して呼び出してくれます。モジュールの初期化関数と

削除時呼び出される関数には必ず指定するようにしてください。

定義自体はlinux/init.hにあります。



補足
linux/module.hをインクルードしておくとlinux/init.hも(linux/

moduleparam.hより)インクルードされます。これによりmodule_initマクロと

module_exitマクロが使用できるようになります。module_initマクロは

その関数名をinit_module関数の別名にするマクロとなります。また、

module_exitマクロはその関数名をcleanup_module関数の別名に

するマクロとなります。カーネルの実装が変更となる可能性がありますので、

init_module関数やmodule_exit関数をそのまま関数実装するの

ではなくmodule_initマクロとmodule_exitマクロを使用するように

します。



モジュールのライセンス、作成者、説明

作成したモジュールにはライセンスを付与しておく必要があります。ライセンスは次のように

定義することができます。

				
MODULE_LICENSE( "GPL" );



MODULE_LICENSEに文字列"GPL"を指定します。モジュールには必ずGPLライセンスを

定義しておく必要があります。"GPL"を定義するとではGNUパブリックライセンスバージョン2

以降のライセンスとなります。また、そのほかにBSDとのデュアルライセンスにしたいときには

指定する文字列として"Dual BSD/GPL"を指定します。MITやMPLなどのライセンスとも

デュアルラインセンスを定義することができます。

"GPL"ライセンスの無いモジュールはカーネルが実行することができません。


ライセンスの他にモジュール作成者やモジュールの説明を定義することもできます。モジュール

の作成者は次のように定義します。

				
MODULE_AUTHOR( "author" );



"author"の箇所を好きなように定義することができます。

また、モジュールの説明は次のように定義します。

				
MODULE_DESCRIPTION( "description" );



これも同じように"description"に好きな説明を書いてください。

補足
その他にもモジュールバージョン(MODULE_VERSION)を定義したり、

モジュールをロードするときにコマンドラインからパラメーターを指定する

こともできます。(module_paramやmodule_param_arrayを

使います)。




ここで定義しておいた文字列はmodinfoコマンドで確認することができます。後で試して

みましょう。

カーネルモジュールのデバッグ情報出力

ファイルシステムモジュールを実装していくなかでやっぱりデバッグが必要に

なってきます。ここでは、C言語アプリケーションを作るときによく使うprintf関数

のように使用できるprintkを使用してデバッグ情報を簡単に出力することが

できます。カーネルはユーザー空間で使用している標準ライブラリーとはリンク

されていませんので、printf関数を使用することはできません。そこでカーネル

空間ではカーネル専用のprintk関数が実装されています。

printkは次のようなインターフェースとなっています。


#include <linux/kernel.h>
			
int printk(const char *fmt, ...);



printkでは最初にメッセージレベルを指定します。そのあとはprintf関数のように

使用することができます。次の例の様に使用します。


printk( KERN_INFO "Example of printk\n" );



この例ではKERN_INFOというレベルを指定しています。printk関数で指定する

KERN_INFOなどのメッセージレベルはlinux/kern_levels.hに定義して」あり

ます。メッセージレベルには次のようなレベルがあり、文字リテラルとして定義されています。

printkのメッセージレベル
レベル 定義値 説明
KERN_EMERG"" システムが使用できないレベルです。
KERN_ALERT"1" 直ぐに何らかの対応策が必要なレベルです。
KERN_CRIT"2" 危機的な状況です。
KERN_ERR"3" エラーが発生しました。
KERN_WARNING"4" 警告レベルの状況です。
KERN_NOTICE"5" 通常レベルですが重要な状況です。
KERN_INFO"6" 情報提供レベルです。
KERN_DEBUG"7" デバッグレベルです。


通常のデッバッグではKERN_DEBUGを使用します。また状況に応じてKERN_INFO

を使います。致命的なエラーの場合は更に上位レベルのメッセージを使用してください。

今回の実装ではKERN_INFOを使用していきたいと思います。

補足
カーネルのデバッグにはその他にもprocfs、sysfs、debugfs、configfsなどを

使ってユーザー空間からカーネルのリソースにアクセスして行う方法もあります。



次の例のようにdefineで定義しておくとDBGPRINTを使用すると通常のprintf関数を

使用するように書くことができます。(定義名は自分の好きなように変更していただいて

問題ありません。)


#define DBGPRINT( msg, args... ) do {                                       \
    printk( KERN_INFO msg, ##args );                                        \
} while( 0 )



補足
更に、__FILE__や__LINE__や__func__を出力するようにしておけば、どこから

デバッグ情報が出力されているのかが、よりわかりやすくなります。



例えば、変数iの値を出力したい場合には、次のようにすることができます。


DBGPRINT( "Sample=%d\n", i );



printkが出力はデフォルトでは/var/log/dmesgに格納されていきます。

その都度デバッグ情報が出力を確認するにはtail -Fコマンドを使用して

確認を行います。また単に、dmesgコマンドでデバッグ情報の確認を行う

ことができます。

シンプルカーネルモジュールの実装

それでは、実際にモジュールを実装していきたいと思います。ここで作成するプログラム

ファイルは次の3つを作っています。(これはあくまで例となりますので、自分の好きなように

作って頂いて問題ありません)


me2fs_main.c
me2fs.h
me2fs_util.h



シンプルカーネルモジュールの実装で作成するプログラムファイル
ファイル 説明
me2fs_main.c モジュール本体のプログラムを実装するプログラムファイルです。
me2fs.h 今回は中身は特にありません。これからExt2の定義などをこのファイルに実装していきます。
me2fs_util.h デバッグ情報出力用の定義を行っています。これ以降定義の追加はありません。必要無ければこのファイルを削除してme2fs.hに定義を書いてもいいと思います。


それではそれぞれのプログラムファイルについて見ていきましょう。

順番が前後しますが、まずはme2fs_util.hです。

me2fs_util.h

このファイルはME2FSのユーティリティ関連の定義をしようと思って例として作りました。

(作ったのはいいのですが、実際にはこれ以降活用してません。好きなように実装してください)。

このファイルではprintkをDBGPRINTで定義を行っています。この定義により、

デバッグモードでコンパイルしたときにはprintk出力を行い、通常モードでコンパイル

したときには何もしないようになっています。モードの切り替えはME2FS_DEBUGを

定義しているときにはデバッグモードで、定義していないときには通常モードでコンパイル

します。通常モードにするときは次の行をコメントアウトしてください。


#define ME2FS_DEBUG



実装自体は次のようになっています。


/********************************************************************************
    File            : me2fs_util.h
    Description     : Defines for my ext2 filesystem utilities

********************************************************************************/
#ifndef __ME2FS_UTIL_H__
#define __ME2FS_UTIL_H__

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

    Prototype Statement

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

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

    DEFINES

=================================================================================
*/
#define ME2FS_DEBUG

#ifdef  ME2FS_DEBUG
    #define DBGPRINT( msg, args... ) do {                                       \
        printk( KERN_INFO msg, ##args );                                        \
    } while( 0 )
#else
    #define DBGPRINT( msg, args... ) do { } while( 0 )
#endif

#define ME2FS_ERROR( msg, args... ) do {                                        \
    printk( KERN_ERR msg, ##args );                                             \
} while( 0 )

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

    Management

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

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

    < Open Functions >

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

#endif  // __ME2FS_UTIL_H__



#ifdef ME2FS_DEBUGで切り替えを行っています。ME2FS_DEBUGを定義

していないときにはDBGPRINTは次のように展開されます。


do { } while( 0 )



コードを見ればわかりますが、特に何もしない処理となります。ある程度デバッグ

ができたらME2FS_DEBUGの定義をコメントアウトしてみてください。dmesgで

表示されるデバッグ情報が表示されなくなります。


同じようにME2FS_ERRORの定義ではファイルシステムがエラーを検出したときに

エラー情報を出力するときに使用します。デバッグ情報ではなくエラーの出力と

なります。


また、最初と最後の


#ifndef __ME2FS_UTIL_H__
#define __ME2FS_UTIL_H__
// 省略
#endif  // __ME2FS_UTIL_H__



は、インクルードガードと呼ばれる技法となります。ヘッダーファイルが2重に

インクルードされるのを防ぎます。ヘッダーファイルにはなるべく定義しておく

ようにしてください。


次にme2fs.hです。

me2fs.h

me2fs.hは次のようにヘッダーファイルを作成していますが、これから実装して

いきますので、今回は何も定義はありません。


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

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

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

    Prototype Statement

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

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

    DEFINES

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

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

    Management

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

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

    < Open Functions >

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



最後に、メインプログラムme2fs_main.cを見ていきます。

me2fs_main.c

メインプログラムは次のような実装をします。コメントなどのは適宜削除して頂いて

問題ありません。


me2fs_main.cではモジュールの初期化関数と終了関数を実装しています。

それぞれ、関数が呼び出されたときにprintkを行うだけとなります。

それぞれの関数をmodule_initとmodule_exitで登録します。

また、モジュールの作成者や説明を定義していますので、適宜変更して

ください。


/********************************************************************************
    File            : me2fs_main.c
    Description     : my ext2 file sytem main routine

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

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


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

    Prototype Statement

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

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

    DEFINES

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

#define ME2FS_VERSION_STRING        "me2fs_0.01"

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

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

    Management

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

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

    < Open Functions >

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

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

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

    < Local Functions >

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

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

    return( 0 );
}

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

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

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

    < 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を作成します。

[Makefileファイル]

ME2FS_SOURCE = me2fs_main.c

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

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



これをMakefileというファイル名にして先ほど作成したプログラムと同じディレクトリ

に置きます。


これからプログラムファイル(ソースファイル)が増えたときには次の行に追加してきます。

今はme2fs_main.cの1つだけとなります。


ME2FS_SOURCE = me2fs_main.c



また、me2fs以外のファイルシステム名にしたいときには次の行を適宜変更してください。

出力するオブジェクトファイルや、makeの生成ルールとなります。


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



最後の行はmakeのコマンドとなります。現在使用しているカーネルバージョンのビルド

ディレクトリに移動してから、カレントディレクトリのモジュールをコンパイルするコマンドと

なっています。この行は特に変更する必要はありません。


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



注意
"make -C"の行は空白ではなく必ずタブで始まるようにしてください。

Makefileのコマンドにタブを入力していないとコマンドとして認識されない場合があります。



コンパイルしたオブジェクトファイルを削除するコマンドも一応用意しておきます。次のように

書いておきます。


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



ここまででMakefileをまとめてみます。

[Makefile]

ME2FS_SOURCE = me2fs_main.c

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

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

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



Makefileができたらmakeコマンドを実行します。


$ make



これで、正常にコンパイルが終了すると最終的にme2fs.koというモジュールが

出力されます。後はme2fs.koをカーネルにロードすればモジュールの初期化

関数が呼び出されます。


それでは、作成したモジュールをロードしてみましょう。

シンプルカーネルモジュールのロードと削除とインフォメーション表示

モジュールのロードから見ていきます。

シンプルカーネルモジュールのロード

カーネルモジュールのロードにはinsmodコマンドを使用します。insmodコマンドは

ルート権限が必要となります。次のように実行してください。


$ sudo insmod me2fs.ko



ここでdmesgでログを確認してみましょう。次のように表示されていれば正常に動作

しています。(表示される数字は違います)。


$ dmesg
〜省略〜
[420806.363254] (Me2FS)Hello, world



モジュールがロードされたときにmodule_initで登録しておいたinitMe2fs関数が

呼び出されて、printkを実行して出力を行っています。

シンプルカーネルモジュールの削除

ロードしたモジュールをカーネルから削除するにはrmmodコマンドを使用します。

insmodと同様にルート権限が必要となります。


$ sudo rmmod me2fs



ここでもdmesgでログを確認してみましょう。次のように表示されているかと思います。


$ dmesg
〜省略〜
[443984.519379] (Me2FS)Good-bye!



これらのコマンドはMakefileに追加しておいてもいいかと思います。

シンプルカーネルモジュールのインフォメーション表示

モジュール実装の説明で作成者などを定義していました。ここではその情報を確認

してみたいと思います。次のようにコマンドを実行します。


$ modinfo me2fs.ko
filename:       /home/user/program/fs/000_load_mod/me2fs.ko
license:        GPL
description:    My ex2 file system (me2fs)
author:         yabusame2001 <mail@address>
srcversion:     A6AA12573B8CD0C7115603F
depends:        
vermagic:       3.13.11.6 SMP mod_unload modversions 





それでは、いよいよファイルシステムとしての動作を実装していきましょう!

PR 説明

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

inserted by FC2 system