前書き
jffs2はLinux用のファイルシステムの一種で、Journaling Flash File System2の略である事からも分かるように、ジャーナリング機能を持ったフラッシュメモリ用のファイルシステムです。
jffs2は、組込み機器の用途で広く使われていますが、日本語での解説がほとんど無いため、私の調査と経験をここに書き残しておこうと思い立ちました。
ここに書いてある事は、jffs2をただ使うだけならば必要ない知識かもしれませんが、トラブル解決や中身をいじる際にはきっと役に立つでしょう。
早速jffs2の仕組みの説明、と行きたい所ですが、その前にフラッシュメモリの特徴を知っていないと、なぜjffs2がそんな事をするのか理解できません。そこでまず、フラッシュメモリの特徴を次に説明します。
フラッシュメモリの特徴
全般的な特徴
不揮発性のメモリ
フラッシュメモリは現在、容量あたりの価格がどんどん下がっている事から注目を集めている不揮発性の半導体メモリです。2007年現在ではまだHDDよりも容量単価は高いのですが、いずれHDDにとって代わるかもしれません。HDDに比べて衝撃に強く、可動部分が無いので耐久性も高くなります。海の底に1年以上沈んでいても、引き上げたら中身が読めたという例もあります。
フラッシュメモリはこのように安さと耐久性が注目されていますが、使う上では次のような事を考慮しないとなりません。
消去操作が要る
フラッシュメモリ内のビットは、初期状態で1になっています。つまり初期状態で1バイトを読み出すと0xFFです。そして、必要なビットを0に反転させることでデータを書き込みます。書込み操作では1を0にすることしか出来ません。0を1に戻すには、消去操作が要ります。消去は、あるまとまった単位(消去単位=ブロック)を一度に消します。1バイトのみ消す、と言った事は出来ません。まとめていっぺんに消えるのでフラッシュと呼ばれるのですね。
そして、消去には数m秒の時間がかかります。フラッシュメモリチップ全体を消すには数m秒×ブロック数だけ時間がかかります。
消去回数が有限
時間よりも問題なのは、消去できる回数に限度がある事です。だいたい10万回くらい消去すると寿命になる品種が多いようです。ブロックが寿命になったことは、そのブロックの消去操作に対してチップから消去失敗の応答が返ってくることで分かります。内部的には、ブロック内に1に戻らないビットが出てきます。中身がブロックに区切られている
なぜ、ブロックに区切っているかは、次のように考えれば分かりやすいでしょう。もしフラッシュメモリの中身を区切らず、全体を一度に消去するとしたら、極端な場合10万文字書き換えたら寿命になることが有り得ます。それでは困るので、チップの中身をブロックに区切って、変更のあったブロックだけ消去、書き換えをします。1つのブロックは10万回消去するとやはり寿命になってしまいますが、チップ全体ではブロック数×10万回だけ書き換えできる事になります。だからブロックサイズを小さくして数を増やした方がチップ全体の書き換え回数を増やせますが、極端に小さくすると逆に困る事もあります。例えば1ブロック1バイトにしたら、3バイトを消去するのに数ms×3ブロックの時間がかかってしまいます。一度にある程度まとまったサイズを消せないと実用的に使えないのです。
そういった事を考慮し、ブロック=消去単位のサイズが決まるわけですが、NOR、NANDと言ったフラッシュメモリの種別(というよりは使い方?)の違いにも最適なブロックのサイズは影響されます。次にこのフラッシュメモリの種別について説明します。
NOR型とNAND型の違い
フラッシュメモリには大きく分けてNOR型とNAND型の区別があり、それぞれつぎのような特徴があります。NOR型
読み出しはメモリマップド
NOR型のフラッシュメモリの読み出しは、通常のROMと同様にアドレスごとに1バイト(または1ワード)を読み出せます。よって、NORのメモリはブートローダーを書き込んでおけば、システムの起動に使えます。
書込みは1バイト(または1ワード)単位
NORの書込みは、まずフラッシュメモリチップの制御レジスタに特定のコマンドを書き込んで、次にデータレジスタに1バイト(または1ワード)を書き込むことで行います。NORのチップには、データを読み出すメモリ空間と別に、チップを制御するレジスタを割り付けるアドレスが数バイトあります。前述した通り、消去無しに書き換えは出来ないので、CD-R/RWなどと同様に一般的にはブロックの先頭から順に追記して使って行きます。しかし、やろうと思えば、書込み済みの1バイトのうち1だったビットを0にする書込みは出来ます。例えば、一度0x01を書き込んだアドレスに0x00を書き込むと言った事が出来るので、8ビット使えば8カウントのカウントダウンタイマなどに使えますね。
NORは1バイト(1ワード)ずつ書き込み、書込みごとにかなり時間がかかるので、あまり頻繁に書き換える用途には向いていません。HDDの代替にしてファイルシステムに使うよりは、ブートROMにするのが良いでしょう。
ブロックは比較的大きい
NORはあまり頻繁に書き換えないので、消去単位も大きくて構いません。1例として1ブロックは128kバイトくらいになります。また、先頭や末尾に近いアドレスのブロックだけサイズを小さくした品種もあります。容量あたりの値段が高い
NORはNANDに比べると、容量あたりの値段が高くなります。NAND型
読み出しはシリアル
NANDフラッシュメモリの読み出し方は、NORやROM/RAMとは違います。I/Oを通して周辺機器を読み出す感じです。いきなり読み出すことはできず、制御レジスタに特定のコマンドを送り、NANDチップの処理の終了を待ち、その後で読み出し用のレジスタのアドレスを読み出すと最初の1バイト(1ワード)め、もう一度読み出すと2バイト(2ワード)めが読める、という具合です。こんな具合ですから、基本的に起動用のBIOSにすることはできません。なお、mDOCやOneNANDのように、NANDメモリに読み出し用のロジックを追加して起動用BIOSに使えるようにしている品種もありますが、起動用の領域以外はやはりROM/RAMのように読む事は出来ません。
書込みはページ単位
NANDへの書込みは、消去単位よりも小さな書込み単位(ページ)で行います。ページのサイズは品種により違い、256〜2048バイト程度ですが、512バイトが多いでしょう。1ページをまとめて書き込むので、NORより格段に速くなります。ページは、実は書く時だけではなく、読む時も意識しなければならず、まず読み出し対象はページで指定して、1ページ読んだ後で続きのページを読むときも、基本的にコマンド送り出しからまたやり直しです。また、NANDはストレージの代替を狙っているので、頻繁な書き換えに対応して消去単位もNORよりは小さめです。
ECCを必要とする
上記のようなNANDの特性を決めているのは、おそらくこのECC(エラー訂正コード)が必要なためでしょう。なんと、NANDフラッシュメモリは書き込んだ内容が誤っていることがあるので、ECCを使ったエラー訂正が必須なのです。これは、HDDのように時間が経つとビットが化ける、という意味ではありません。(*1)
そうではなくて、デバイスの特性として、ある程度の確率で間違って書き込んでしまうビットが出て来るという事のようです。だから、ストレージとして満足な品質を保証するために次の手順でECCの操作が必要となります。
(1)まず書込みむデータの内容を元にECCを計算する。
(2)ECCをデータとともにフラッシュメモリに書き込む。
(3)読み出す時にはデータと一緒にECCも読み出す。
(4)ECCを計算してデータに誤りがあれば訂正する。
ECCは「1ビット誤りは確実に訂正でき、2ビット誤りは確実に検出できる」という仕様の物が多いので、3ビット以上の誤りの発生が確率的に無視できるようなデータサイズに対して適用しなければなりません。(*2)
ということで、ページの読み書きの際にはECC処理も必要なので、NANDの読み書き手順はいっそう複雑になります。読み出し速度をNORと比較する場合は、このECC処理にかかる時間を含んでいるかどうかに注意する必要があります。なお、ECC処理は普通、ドライバソフトが(CPUを使って)やりますが、NANDチップ自身が処理する回路を持った品種もあります。
データにある程度、誤りがあっても良い用途ならば、ECCは要らず、ページ単位で読み書きする必要も無いので、ページの途中から追記する事も出来ます。ただし、追記は1ページに対して累積3回程度に制限している品種が多いようです。
OOBをもつ
フラッシュメモリ上のECCを格納する領域をOOB(Out Of Band)と言います。1ページにつき16バイト程度、ページの容量とは別に存在します。1Mビットのチップならちゃんと1Mビットのデータを書き込めるだけのページがあり、OOBはそれとは別に確保してあるので(だからOut Of Band)、ECCを書き込むために容量が目減りする事はありません。ちなみに、ブロックを消去すると、その中のページとOOBも一緒に消去されます。「ページのみ」や「OOBのみ」の消去は出来ません。

出荷時から不良ブロックがある
先ほど、1Mビットのチップなら1Mビット書き込めると言いましたが、ウソです。NANDは買った時点で読み書きできない不良ブロックが数%程度あります(昔のHDDもそうでしたね)。最大で何%足りないかは仕様書に明記してありますので、あらかじめそれを見込んで必要な容量を決めてください。また、NANDは、HDDと違って不良ブロックを自動で代替することはしません。ドライバかアプリケーションで管理しなければなりません。(*3)その際には、OOBの空いているビットを不良ブロックのフラグとして使います。
容量あたりの値段が安い
上記のようなもろもろの面倒くさい事を容認する代わりに、NANDでは大容量が安価に手に入ります。世間で「フラッシュメモリが安くなった」と言ってるのは、たいがいNORではなくて、NANDの事です。続いてjffs2の解説に移ります。(次へ)
*1 データの保持期間は公称で10年程度あります。実際は多値記録の物で2、3年、という意見も見ますが。
*2 ちなみに、書き込んだECCもデータ誤りを含んでいる可能性がありますが、ECCのアルゴリズムはそういう事も考慮してあります。
*3 jffs2は不良ブロックの管理機能を持っているのでjffs2に任せられます。不良ブロックを管理する機能を持たないファイルシステムを使いたい場合や、ファイルシステムを使わない場合は、UBI(http://www.linux-mtd.infradead.org/index.html)を使うと良いそうです。