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


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

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

メモリ使用量 (R0.07)

AVRH8/300HPICTLCS-870/CV850ESSH2ARM7TDMIIA-32
Compilergcc(WinAVR)CH38gcc(C30)CC870CCA850SHCgcc(WinARM)MSC
_WORD_ACCESS10011001
ROM (Full, R/W)1113610356108381516776828654106287232
ROM (Min, R/W)70726696700798004634557065644647
ROM (Full, R/O)52184626494967863528382646763267
ROM (Min, R/O)36263418353649412558287432722397
RAM (Static)D*2 + 2D*4 + 2D*2 + 2D*2 + 2D*4 + 2D*4 + 2D*4 + 2D*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)
_MULTI_PARTITION 0 (Single partition per drive)
_FS_REENTRANT    0 (Disable reentrancy)

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

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

Function_FS_MINIMIZE_FS_READONLY_USE_STRFUNC_USE_MKFS_USE_FORWARD
1231000
f_mount
f_open
f_close
f_read
f_writex
f_syncx
f_lseekx
f_opendirxx
f_readdirxx
f_statxxx
f_getfreexxxx
f_truncatexxxx
f_unlinkxxxx
f_mkdirxxxx
f_chmodxxxx
f_utimexxxx
f_renamexxxx
f_mkfsxx
f_forwardx
f_putcxx
f_putsxx
f_printfxx
f_getsx

長いファイル名

FatFsモジュールはR0.07から長いファイル名(LFN)をサポートしました。ファイルに付けられた2つの異なる名前(短いファル名と長いファイル名)は、f_readdir関数を除くファイル操作関数において透過です。LFN機能を有効にするには、_USE_LFNを1または2に設定し、OEM-Unicode相互変換関数 ff_convert をプロジェクトに追加します。これらの関数は、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 ARM7DMI
コードページROMサイズ[bytes]
SBCS+4719
932(Shift-JIS)+63755
936(GBK)+178943
949(Korean)+141003
950(Big5)+112631

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. セクタ・ミスアラインド・リード (ショート)
fig.1

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

図3. セクタ・アラインド・リード
fig.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. 長いクリチカル・セクション
fig.4
図5. 最小化したクリチカル・セクション
fig.5

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

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

戻る