# FatFsモジュール アプリケーション・ノート

### ポーティングの際に配慮すべきこと

FatFsモジュールは移植性に関して次の点を前提としています。

- 処理系はANSI C準拠であること。
  FatFsモジュールはANSI C準拠で記述されているので、ANSI C準拠のコンパイラなら特に処理系依存な点はありません。
- char/short/longのサイズは、それぞれ8/16/32ビットで、intは16または32ビットであること。
  使用される整数の型は integer.h 内で typedef されています。整数の型とサイズに関しては、まっとうな処理系なら問題ないはずですが、既存の定義と衝突した場合はユーザによって解決されなければなりません。

### メモリ使用量 (R0.07c)

| AVR | H8/300H | PIC | TLCS-870/C | V850ES | SH2 | ARM7TDMI | IA-32
--- | --- | --- | --- | --- | --- | --- | --- | ---
Compiler | WinAVR(gcc) | CH38 | C30(gcc) | CC870C | CA850 | SHC | WinARM(gcc) | MSC
_WORD_ACCESS | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1
ROM (Full, R/W) | 11962 | 10433 | 10753 | 15153 | 7747 | 8707 | 10584 | 7337
ROM (Min, R/W) | 7466 | 6799 | 6772 | 9906 | 4901 | 5599 | 6548 | 4787
ROM (Full, R/O) | 5400 | 4687 | 4804 | 6744 | 3539 | 3799 | 4676 | 3380
ROM (Min, R/O) | 3804 | 3527 | 3421 | 5040 | 2561 | 2867 | 3276 | 2533
RAM (Static) | D*2 + 2 | D*4 + 2 | D*2 + 2 | D*2 + 2 | D*4 + 2 | D*4 + 2 | D*4 + 2 | D*4 + 2
RAM (Dynamic) (_FS_TINY == 0) | D*560 + F*544 | D*560 + F*550 | D*560 + F*544 |  | D*560 + F*550 | D*560 + F*550 | D*560 + F*550 | D*560 + F*550
RAM (Dynamic) (_FS_TINY == 1) | D*560 + F*32 | D*560 + F*36 | D*560 + F*32 | D*560 + F*32 | D*560 + F*36 | D*560 + F*36 | D*560 + F*36 | D*560 + F*36

上の表にいくつかのターゲットにおけるメモリ使用量の例を示します。テスト時の構成オプションは次の通りです。数値の単位はバイトで、Dは論理ドライブ数、Fは同時オープン・ファイル数を示します。コンパイラの最適化オプションはコード・サイズとしています。

    _FS_READONLY     0 (R/W), 1 (R/O)
    _FS_MINIMIZE     0 (Full function), 3 (Minimized function)
    _USE_STRFUNC     0 (Disable string functions)
    _USE_MKFS        0 (Disable f_mkfs function)
    _USE_FORWARD     0 (Disable f_forward function)
    _CODE_PAGE       932 (Japanese Shift-JIS)
    _USE_LFN         0 (Disable LFN)
    _MAX_SS          512 (Single sector size)
    _FS_RPATH        0 (Disable relative path)
    _MULTI_PARTITION 0 (Single partition per drive)
    _FS_REENTRANT    0 (Disable reentrancy)

### モジュール・サイズの縮小

次の表は構成オプションの設定値によりどの機能が削除されるかを示します。

Function | _FS_MINIMIZE | _FS_READONLY | _USE_STRFUNC | _FS_RPATH | _USE_MKFS | _USE_FORWARD
1 | 2 | 3 | 1 | 0 | 0 | 0 | 0
f_mount |  |  |  |  |  |  |  |
f_open |  |  |  |  |  |  |  |
f_close |  |  |  |  |  |  |  |
f_read |  |  |  |  |  |  |  |
f_write |  |  |  | x |  |  |  |
f_sync |  |  |  | x |  |  |  |
f_lseek |  |  | x |  |  |  |  |
f_opendir |  | x | x |  |  |  |  |
f_readdir |  | x | x |  |  |  |  |
f_stat | x | x | x |  |  |  |  |
f_getfree | x | x | x | x |  |  |  |
f_truncate | x | x | x | x |  |  |  |
f_unlink | x | x | x | x |  |  |  |
f_mkdir | x | x | x | x |  |  |  |
f_chmod | x | x | x | x |  |  |  |
f_utime | x | x | x | x |  |  |  |
f_rename | x | x | x | x |  |  |  |
f_chdir |  |  |  |  |  | x |  |
f_chdrive |  |  |  |  |  | x |  |
f_mkfs |  |  |  | x |  |  | x |
f_forward |  |  |  |  |  |  |  | x
f_putc |  |  |  | x | x |  |  |
f_puts |  |  |  | x | x |  |  |
f_printf |  |  |  | x | x |  |  |
f_gets |  |  |  |  | x |  |  |

### 長いファイル名

FatFsモジュールはR0.07から長いファイル名(LFN)をサポートしました。ファイルに付けられた2つの異なる名前(短いファル名と長いファイル名)は、f_readdir関数を除くファイル操作関数において透過です。LFN機能を有効にするには、`_USE_LFN`を1または2に設定し、Unicode変換関数 ff_convert, ff_wtoupper をプロジェクトに追加します。これらの関数は、`cc*.c`に含まれています。LFN機能は、加えてある程度のワーク・エリア(LFN操作バッファ)を必要とします。バッファ長は使用できるメモリに応じて`_MAX_LFN`オプションで構成されることができます。LFNの長さは最大255文字に達するので、LFN完全対応のためには`_MAX_LFN`は255に設定されるべきです。与えられたファイル名に対してバッファ長が不足した場合、ファイル関数は`FR_INVALID_NAME`で失敗します。

LFN機能をリエントラント構成で使用する場合は、`_USE_LFN`は2に設定されなければなりません。この場合、ファイル関数はバッファをスタックに確保します。バッファ・サイズは、`_MAX_LFN` * 2 + 1 バイトになるので、呼び出し側スタックのサイズはそれを考慮した十分なサイズでなければなりません。

LFN cfg on ARM7
コードページ | ROMサイズ[bytes]
--- | ---
SBCS | +3721
932(Shift-JIS) | +62609
936(GBK) | +177797
949(Korean) | +139857
950(Big5) | +111497

LFNを有効にすると、選択されたコード・ページに応じてモジュール・サイズが増大されます。右の表に各コード・ページにおけるLFNを有効にしたときのモジュール・サイズの違いを示します。私たち日本人、中国人および韓国人は数万の文字を持ちます。不幸なことに、それは巨大なOEM−Unicode相互変換テーブルを要求し、モジュール・サイズは劇的に増大されます。その結果、LFNを有効にしたFatFsモジュールは、AVRを含む殆どの8ビット・マイコンにインプリメントされることができません。これは長い間私がLFNをインプリメントすることに興味を持ってこなかった理由です。

注: FATファイル・システム上のLFN機能はマイクロソフト社の特許です。商用製品でそれを有効にするときは、最終仕向地によってはライセンスが必要かも知れません。

### リエントランシー

異なるボリューム(論理ドライブ)に対するファイル操作は、リエントラント設定によらず常に同時平行に動作できます。同じボリュームに対するリエントランシーは`_FS_REENTRANT`オプションで有効にされることができます。この場合、OS依存の同期オブジェクト操作関数 ff_cre_syncobj, ff_del_syncobj, ff_req_grant と ff_rel_grant もまたプロジェクトに追加されなければなりません。サンプル・コードと解説は`syncobj.c`にあります。

他のタスクがそのボリュームを使用中にファイル関数が呼び出されると、そのアクセスはそのタスクがファイル関数を抜けるまでブロックされます。もし、待ち時間が`_TIMEOUT`で指定された期間を越すと、その関数は`FR_TIMEOUT`でアボートします。いくつかのRTOSではタイムアウト機能はサポートされないかも知れません。

ひとつの例外がf_mountとf_mkfs関数にあります。これらの関数は同じボリュームに対してリエントラントではありません。これらの関数を使用するときは、他のスレッドは関連するファイルを閉じ、そのボリュームへのアクセスを避けなければなりません。

注: このセクションはFatFsモジュールそれ自体のリエントランシーについて説明しています。ディスクI/Oモジュールのリエントランシーに関しては何の前提もありません。

### 多重ファイル・アクセス

FatFsモジュールでは多重アクセス機能はサポートされません。ファイルに対する多重アクセスは、そのアクセス・モードによって制限されます。一つのファイルに対する多重オープンは、それらが全てリード・モードのとき許可されます。書き込みモードを含む多重オープン、また開かれているファイルに対するリネームや消去を行ってはなりません。さもないと、そのボリュームのFAT構造が破壊される可能性があります。

### 効率的なファイル・アクセス

小規模な組込システムでのファイルの読み書きにおける効率の良いアクセスのため、アプリケーション・プログラマはFatFsモジュールの中でどのような処理が行われているか考慮すべきです。ディスク上のデータはf_read関数により次のシーケンスで転送されます。

図1. セクタ・ミスアラインド・リード (ショート)

図2. セクタ・ミスアラインド・リード (ロング)

図3. セクタ・アラインド・リード

ファイルI/Oバッファはセクタの一部のデータを読み書きするためのセクタ・バッファを意味します。セクタ・バッファは、それぞれのファイル・オブジェクト内のプライベート・セクタ・バッファまたはファイル・システム・オブジェクト内の共有セクタ・バッファのどちらかです。バッファ構成オプションの`_FS_TINY`は、データ転送にどちらを使うかを決定します。タイニー・バッファ(1)が選択されるとデータ・メモリの消費はそれぞれのファイル・オブジェクトで512バイト減少されます。この場合、FatFsモジュールはファイル・データの転送とFAT/ディレクトリ・アクセスにファイル・システム・オブジェクト内のセクタ・バッファだけを使用します。タイニー・バッファの欠点は、セクタ・バッファにキャッシュされたFATデータがファイル・データの転送により失われ、クラスタ境界の毎にリロードされなければならないことです。でも、悪くない性能と少ないメモリ消費の視点から多くのアプリケーションに適するでしょう。

図1はセクタの一部のデータがファイルI/Oバッファを経由で転送されることを示します。図2に示される長いデータの転送では、転送データの中間の1セクタまたはそれ以上のセクタにまたがる転送データがアプリケーション・バッファに直接転送されています。図3は転送データ全体がセクタ境界にアライメントされている場合を示しています。この場合、ファイルI/Oバッファは使用されません。直接転送においては最大の範囲のセクタがdisk_read関数で一度に読み込まれますが、クラスタ境界を越えるマルチ・セクタ転送はそれが隣接であっても行われません。

このように、セクタにアライメントしたファイルの読み書きへの配慮はバッファ経由のデータ転送を避け、読み書き性能は改善されるでしょう。その効果に加え、タイニー構成でキャッシュされたFATデータがファイル・データの転送によりフラッシュされず、非タイニー構成と同じ性能を小さなメモリ・フットプリントで達成できます。

### クリチカル・セクション

ディスク上のFAT構造を操作している途中で、停電、不正なメディアの取り外し、回復不能なデータ・エラー等の障害が発生すると、処理が中途半端な状態で中断され、その結果としてFAT構造が破壊される可能性があります。次にFatFsモジュールにおけるクリチカル・セクションと、その間の障害により起きうるエラーの状態を示します。

図4. 長いクリチカル・セクション
図5. 最小化したクリチカル・セクション

赤で示したセクションを実行中に障害が発生した場合、クロス・リンクが発生して操作対象のファイル・ディレクトリが失われる可能性があります。黄色で示したセクションを実行中に障害が発生した場合、つぎのうちいずれかまたは複数の結果が生じる可能性があります。

- 書き換え中のファイルの内容が破壊される。
- 追記中のファイルがオープン前の状態に戻る。
- 新規に作成されたファイルが消える。
- 新規または上書きで作成されたファイルの長さがゼロになって残る。
- ロストチェーンの発生によりディスクの利用効率が悪化する。

いずれも書き込み中や操作対象でないファイルには影響はありません。これらのクリチカル・セクションは、ファイルを書き込みモードで開いている時間を最小限にするか、f_sync()を適宜使用することで図5のようにリスクを最小化することができます。

### ファイル名の大文字変換

CP932(Shift_JIS)でかつ非LFN構成のときは、拡張文字(2バイトの英字・キリル文字・ギリシャ文字)に対して大文字変換を行わず、小文字は小文字でディレクトリに記録・比較されます。これは日本語MSDOSと同じ仕様となります。このため、全角小文字を含むファイルを作成すると、Windows環境でそのファイルを開けなくなります。LFN構成では大文字変換を行います(Windows仕様)。

### Unicode対応

長いファイル名が有効のとき`_LFN_UNICODE`の定義を 1にすると APIのLFN名入出力が Unicodeになります(`char→WCHAR`)。この機能はテストしていないため、現在はアンドキュメンテッドとしています。

戻る