HOME > ソフテックだより > 第223号(2014年12月3日発行) 技術レポート「組み込みLinux向けファイルシステム“JFFS2”をNAND型フラッシュメモリに書き込む方法」

「ソフテックだより」では、ソフトウェア開発に関する情報や開発現場における社員の取り組みなどを定期的にお知らせしています。
さまざまなテーマを取り上げていますので、他のソフテックだよりも、ぜひご覧下さい。

ソフテックだより(発行日順)のページへ
ソフテックだより 技術レポート(技術分野別)のページへ
ソフテックだより 現場の声(シーン別)のページへ


ソフテックだより 第223号(2014年2月5日発行)
技術レポート

「組み込みLinux向けファイルシステム“JFFS2”をNAND型フラッシュメモリに書き込む方法」

1. はじめに

最近、あるお客様からTexas Instrument社(以下、TI社と称します)のDMSoC(Digital Media System-on-Chip)を用いたアプリケーション開発のご依頼をいただきました。この開発では、TI社が提供する組み込みLinux ディストリビューション(以下、Linux OSと称します)を含む開発環境を使用して、アプリケーションの開発を行いました。

開発の要件として、ターゲット基板上のNAND型フラッシュメモリ(以下、NANDと称します)から、Linux OSとアプリケーションをブートさせる必要がありましたが、弊社ではそのようなノウハウに乏しかったため、Linux OSをブートさせるまでに、少なからず調査を行う必要がありました。今回のソフテックだよりでは、その際の調査結果をもとに、組み込みLinux向けファイルシステム「JFFS2」をNANDに書き込む方法について紹介したいと思います。

2. 組み込みLinuxで用いられるJFFS2とは

一般的に、NANDからLinux OSをブートさせるにあたって、ソフト的に考慮すべき点として主に以下の2点が挙げられます。これらの問題点を踏まえた上で、最適なファイルシステムを選択する必要があります。

  • 突然の電源断のような組み込みシステム特有の問題を考慮すること
  • NANDを含む、フラッシュメモリの書き換え可能回数は有限であること

前者はジャーナリングやログ構造ファイルシステムという方法を用いることで解決できます。これは、ファイルシステムの変更内容を、変更が完了するまでログに記録することで、バックアップする仕組みです。システム障害が発生しても、ログをもとに変更を完了させるか、変更を取り消すかを判断し、ファイルシステム全体を守ります。

一方、後者の対策としてはウェアレベリングという、フラッシュメモリの寿命を延長させる技術があります。一般的には、書き込み先の記憶素子を分散することで、特定の記憶素子に書き換えが集中しないような対策が取られます。

今回はこの2つの問題への対策が施されているファイルシステムの中から、TI社の開発環境との相性を踏まえて、Journaling Flash File System, version 2(以下、JFFS2と称します)と呼ばれるファイルシステムを採用しました。

3. JFFS2とは?その利点と欠点とは?

JFFS2は、JFFSと呼ばれるファイルシステムの発展系です。ベースになるJFFSは、ログ構造ファイルシステムを用いて、電源断に対する耐性を備えていましたが、ウェアレベリング機能が最適な仕組みにはなっていませんでした。これは、フラッシュデバイス全体を循環ログとしたことで、頻繁に消去が発生するためです。これを改善したのがJFFS2です。

JFFS2の利点と欠点は下表 1のとおりです。

利点 欠点
  • 突然の電源断に対して耐性を有している
  • JFFSに比べ、ウェアレベリング機能が優れている
  • 圧縮率が高い
  • ファイルサイズが大きいほどマウントに時間がかかる
  • ガベージコレクションプロセスが動作するとシステム全体の動作に影響を及ぼす

表1. JFFS2の利点と欠点

JFFS2は組み込みデバイス向けに優れた特徴を有しましたが、一方で、大きいファイルシステムほど、マウントに時間がかかるという欠点があります。加えて、空き容量が少なくなると、不要なデータを削除するガベージコレクションプロセスが実行され、他のプロセスに影響を及ぼす可能性があります。

以上のように、JFFS2も一長一短の特徴を持ったファイルシステムです。

4. JFFS2作成とNANDへの書き込み手順

以降では、JFFS2形式のファイルシステムを作成し、ターゲット基板上のNANDへ書き込む手順を紹介します。今回の紹介例では、480MBと、大きめのファイルシステムを作成しています。
開発環境などの前提条件は以下のとおりとなります。

  • 開発環境(ホストPC)はUbuntu 10.04
  • 開発環境に1GB以上のメモリ容量
  • ブートローダはu-bootを使用
  • カーネルイメージはuImageを使用
  • 対象の基板にSDカードスロットを有しているか、NFS(Network File System)を使用可能なこと

開発環境のメモリ容量が不足すると、システムが不安定になる場合がありますので、以降で説明するRAMディスクを作る際に注意してください。

なお、ターゲット基板のLinux環境は以下のバージョンを使用しています。

  • Linux kernel 2.6.32.17
  • u-boot 2010.12-rc2

また、以後の説明では、MTD(Memory Technology Device)という言葉を使用します。MTDデバイスとは、NANDやNOR型フラッシュメモリのようなフラッシュメモリ全般を指し、これらの種類が異なっても可能な限り共通の機能を提供できるようにしたのがMTDサブシステムです。

4-1. ramdisk.imgの作成

JFFS2を作成するために、Linux OSの必要なファイル全てコピーした、単一のバイナリファイル「ramdisk.img」を作成します。今回はtmpfsという可変サイズのRAMディスク作成コマンドを使用し、そこに必要なファイル一式を書き込み、「ramdisk.img」として出力します。手順詳細については、表2をご覧ください。

host$ cd temp/
host$ dd if=/dev/zero of=disk bs=1M count=480
host$ losetup /dev/loop0 disk
host$ mke2fs -vm0 /dev/loop0
host$ mount -t ext2 /dev/loop0 /mnt
host$ cp -r /(コピー元パス)/bin /mnt/bin
host$ cp -r /(コピー元パス)/sbin /mnt/sbin
host$ cp -r /(コピー元パス)/etc /mnt/etc
host$ cp -r /(コピー元パス)/dev /mnt/dev
・
(必要なディレクトリ全てをコピーする)
・
host$ umount /mnt
host$ dd if=/dev/loop0 bs=1M count=480 of=ramdisk.img

※ ”host$” は、ホストPCターミナル上での入力操作を意味します。

手順 コマンド コマンドの説明
1 host$ mount -t tmpfs -o size=480M tmpfs temp/ メモリ上に480MBのファイルシステムを作成し、それをtemp/というパスにマウントします
2 host$ cd temp/ temp/に移動します
3 host$ dd if=/dev/zero of=disk bs=1M count=480 ゼロフィルした480MBのバイナリファイル「disk」を作成します
4 host$ losetup /dev/loop0 disk 上記で作成したdiskを、ループデバイスに関連づけます
5 host$ mke2fs -vm0 /dev/loop0 ext2形式のファイルシステムを/dev/loop0に作成します
6 host$ mount -t ext2 /dev/loop0 /mnt /dev/loop0に作成したファイルシステムをext2形式で/mntにマウントします
7 host$ cp ?r /(コピー元パス)/bin /mnt/bin
host$ cp ?r /(コピー元パス)/sbin /mnt/sbin
host$ cp ?r /(コピー元パス)/etc /mnt/etc
host$ cp ?r /(コピー元パス)/dev /mnt/dev

(必要なディレクトリ全てをコピーする)
/mntに、ターゲットボードを動かすために必要なファイルを全てコピーします。この時、階層構造なども保持したままコピーするようにします
8 host$ umount /mnt /mntのマウントを解除します
9 host$ dd if=/dev/loop0 bs=1M count=480 of=ramdisk.img /dev/loop0上のファイルを単一のバイナリファイル「ramdisk.img」に書き出します

表2. ramdisk.imgの作成手順

これで、ramdisk.imgが生成されます。なお、ramdisk.imgという名称は決まったものではありませんので、認識しやすい名称(拡張子も)であれば何でも結構です。

4-2. ramdisk.imgをJFFS2へ変換

4.1で作成したramdisk.imgを適当なディレクトリ(ここではworkdirとします)にループバックデバイスとしてマウントし、「rootfs.jffs2」というファイルシステムへ変換します。詳細は表 3をご覧ください。なお、mkfs.jffs2というアプリケーションを必要としますので、お使いの環境にインストールされていない場合には、インストールをする必要があります。

host$ mount ramdisk.img workdir -o loop
host$ mkfs.jffs2 -n -r workdir -e 128 -o rootfs.jffs2g

※ ”host$” は、ホストPCターミナル上での入力操作を意味します。

手順 コマンド コマンドの説明
1 host$ mount ramdisk.img workdir -o loop mountコマンドを用いて、4.1で作成したramdisk.imgをworkdirにループバックデバイスとしてマウントします
2 host$ mkfs.jffs2 -n -r workdir -e 128 -o rootfs.jffs2 mkfs.jffs2というアプリケーションを用いて、workdirに展開したファイルをrootfs.jffs2という名のJFFS2に変換します

表3. ramdisk.imgをJFFS2に変換する手順

ここで生成したrootfs.jffs2をNANDに書き込むことでNANDブートが可能になります。なお、こちらも認識しやすいファイル名であれば何でも良いです。

4-3.Linux OS起動前にJFFS2をNANDへ書き込む方法

上記で作成したrootfs.jffs2をNANDへ書き込む2つの方法をご紹介します。お使いの環境やお好みに応じて、どちらかひとつの方法でNANDへの書き込みを行ってください。操作の途中でNANDの消去も行いますので、操作には十分ご留意ください。

なお、以降の説明では、NAND書き込みを行うターゲットデバイスのパーティション構成を下図の前提としています。下図の例は、TI社が提供する開発環境をベースとしたものです。

ターゲットデバイスのパーティション構成例
図1. ターゲットデバイスのパーティション構成例

1つめの方法では、ターゲット基板ブート直後にu-bootのコマンドを用いて、SDカードに書き込んだrootfs.jffs2をNANDに書き込みます。詳細は表 4をご覧ください。

u-boot# mmc rescan 0
u-boot# fatload mmc 0 0x80000000 rootfs.jffs2
u-boot# nand erase 0x20800000 0x1f800000
u-boot# nand write 0x80000000 0x20800000 0x1f800000

※ ”u-boot#” は、u-boot実行中のターゲット基板に接続されたターミナルへの入力操作を意味します。

手順 コマンド コマンドの説明
1 NANDかSDカード経由でブートさせ、Linux OSの自動起動を止める -
2 u-boot# mmc rescan 0 SDカードをマウントします
3 u-boot# fatload mmc 0 0x80000000 rootfs.jffs2 rootfs.jffs2をメモリアドレス(0x80000000)に読み込みます
4 u-boot# nand erase 0x20800000 0x1f800000 書き込み先のMTDパーティションに当たる部分を削除します
5 u-boot# nand write 0x80000000 0x20800000 0x1f800000 メモリ上のrootfs.jffs2をMTDパーティションの指定アドレス(0x20800000)から指定サイズ分(0x1f800000)書き込みます

表4. u-boot経由でNANDにrootfs.jffs2を書き込む方法

この方法では、NANDの書き込み先アドレスを直接入力する必要がありますので、書き込み先が適切か、注意を払う必要があります。

4-4. Linux OS起動後にJFFS2をNANDへ書き込む方法

2つめの方法では、ターゲット基板でNFS等を用いてLinux OSを起動させ、ログイン後にターミナルから任意のMTDパーティションにrootfs.jffs2を書き込むものです。詳細は表 5をご覧ください。

target# flash_erase /dev/mtd4 0 0
target# nandwrite -p /dev/mtd4 /(参照元パス)/rootfs.jffs2

※ ”target#” は、Linux OS起動後のターゲット基板ターミナルへの入力操作を意味します。

手順 コマンド コマンドの説明
1 target# flash_erase /dev/mtd4 0 0 指定したMTDパーティションのデータを削除します
2 target# nandwrite -p /dev/mtd4 /(参照元パス)/rootfs.jffs2 指定したMTDパーティションにroot.jffs2を書き込みます

表5. Linuxコンソール経由でNANDにrootfs.jffs2を書き込む方法

こちらの方が、ターゲットボードを外部から起動させる必要がありますが、比較的わかりやすい手順です。

4-5. JFFS2ファイルシステムのマウントを確認する

書き込みを終えたらターゲット基板を起動し、NAND上のJFFS2ファイルシステムのマウントを確認します。起動途中で、

Empty flash at 0xXXXXXXXX ends at 0xXXXXXXXX

というメッセージが表示される場合がありますが、マウントが成功すればコンソール画面まで立ち上がります。

ただし、マウントに2分以上かかると、下記メッセージとともにマウントに失敗する場合があります。

INFO: task mount:XXXX blocked for more than 120 seconds.

これはマウントに時間がかかりすぎて、カーネルに「プロセスが異常である」と判断されたためです。MTDパーティションのサイズを小さくすることで、マウント時間も短くすることができます。開発するアプリケーションによっては、マウント時間(起動時間)の短縮が必須条件となるケースもあるため、rootfs.jffs2のサイズ最適化は重要な作業となります。

5. おわりに

今回は、組み込みLinux向けファイルシステム「JFFS2」をNANDに書き込む方法についてご紹介しました。今号が皆様のソフト開発のお役にたちましたら幸いです。
最後までお読みいただきありがとうございました。

(Y. T. & T. S.)


関連ページへのリンク

関連するソフテックだより

ページTOPへ