Linux 支持多种文件系统,包括ext2、ext3、vfat、ntfs、iso9660、jffs、romfs和nfs等,为了对各类文件系统进行统一管理,Linux引入了虚拟文件系统VFS(Virtual File System),为各类文件系统提供一个统一的操作界面和应用编程接口。
Linux启动时,第一个必须挂载的是根文件系统;若系统不能从指定设备上挂载根文件系统,则系统会出错而退出启动。之后可以自动或手动挂载其他的文件系统。因此,一个系统中可以同时存在不同的文件系统。
不同的文件系统类型有不同的特点,因而根据存储设备的硬件特性、系统需求等有不同的应用场合。在嵌入式Linux应用中,主要的存储设备为 RAM(DRAM, SDRAM)和ROM(常采用FLASH存储器),常用的基于存储设备的文件系统类型包括:jffs2, yaffs, cramfs, romfs, ramdisk, ramfs/tmpfs等。
1、基于Flash的文件系统
Flash(闪存)作为嵌入式系统的主要存储媒介,有其自身的特性。Flash的写入操作只能把对应位置的1修改为0,而不能把0修改为1(擦除Flash就是把对应存储块的内容恢复为1),因此,一般情况下,向Flash写入内容时,需要先擦除对应的存储区间,这种擦除是以块(block)为 单位进行的。
闪存主要有NOR和NAND两种技术。Flash存储器的擦写次数是有限的,NAND闪存还有特殊的硬件接口和读写时序。因此,必须针对Flash的硬件特性设计符合应用要求的文件系统;传统的文件系统如ext2等,用作Flash的文件系统会有诸多弊端。
在嵌入式Linux下,MTD(Memory Technology Device,存储技术设备)为底层硬件(闪存)和上层(文件系统)之间提供一个统一的抽象接口,即Flash的文件系统都是基于MTD驱动层的(参见上面的Linux下的文件系统结构图)。使用MTD驱动程序的主要优点在于,它是专门针对各种非易失性存储器(以闪存为主)而设计的,因而它对Flash有更好的支持、管理和基于扇区的擦除、读/写操作接口。
顺便一提,一块Flash芯片可以被划分为多个分区,各分区可以采用不同的文件系统;两块Flash芯片也可以合并为一个分区使用,采用一个文件系统。即文件系统是针对于存储器分区而言的,而非存储芯片。
支持在Flash上运行的常用文件系统有cramfs、jffs、jffs2、yaffs、yaffs2等。
如果想在Flash上实现读写操作,通常在NorFlash上我们会选取jffs及jffs2文件系统,在NandFlash上选用yaffs或yaffs2文件系统。Yaffs2文件系统支持大页(大于512字节/页)的NandFlash存储器。
它们也都是基于文件系统+mtd+flash设备的架构。linux-2.6.27后,内核加入了一种新型的flash文件系统UBI(Unsorted Block Images)。
(1) jffs2
JFFS文件系统最早是由瑞典 Axis Communications公司基于Linux2.0的内核为嵌入式系统开发的文件系统。JFFS2是RedHat公司基于JFFS开发的闪存文件系统,最初是针对RedHat公司的嵌入式产品eCos开发的嵌入式文件系统,所以JFFS2也可以用在Linux, uCLinux中。
Jffs2: 日志闪存文件系统版本2 (Journalling Flash FileSystem v2)
主要用于NOR型闪存,基于MTD驱动层,特点是:可读写的、支持数据压缩的、基于哈希表的日志型文件系统,并提供了崩溃/掉电安全保护,提供“写平衡”支持等。缺点主要是当文件系统已满或接近满时,因为垃圾收集的关系而使jffs2的运行速度大大放慢。
目前jffs3正在开发中。关于jffs系列文件系统的使用详细文档,可参考MTD补丁包中mtd-jffs-HOWTO.txt。
jffsx不适合用于NAND闪存主要是因为NAND闪存的容量一般较大,这样导致jffs为维护日志节点所占用的内存空间迅速增大,另外,jffsx文件系统在挂载时需要扫描整个FLASH的内容,以找出所有的日志节点,建立文件结构,对于大容量的NAND闪存会耗费大量时间。
(2)yaffs/yaffs2
yaffs/yaffs2是专为嵌入式系统使用NAND型闪存而设计的一种日志型文件系统。与jffs2相比,它减少了一些功能(例如不支持数据压缩),所以速度更快,挂载时间很短,对内存的占用较小。另外,它还是跨平台的文件系统,除了Linux和eCos,还支持WinCE, pSOS和ThreadX等。
yaffs/yaffs2自带NAND芯片的驱动,并且为嵌入式系统提供了直接访问文件系统的API,用户可以不使用Linux中的MTD与VFS,直接对文件系统操作。当然,yaffs也可与MTD驱动程序配合使用。
yaffs与yaffs2的主要区别在于,前者仅支持小页(512 Bytes) NAND闪存,后者则可支持大页(2KB) NAND闪存。同时,yaffs2在内存空间占用、垃圾回收速度、读/写速度等方面均有大幅提升。
yaffs制作工具:mkyaffsimage
yaffs2制作工具:mkyaffs2image(适合64M)、mkyaffs2image-128(适合128M以上)
命令:
生成yaffs2镜像文件:./mkyaffs2image-128M rootfs/ rootfs.yaffs2
生成yaffs镜像文件:./mkyaffsimage rootfs/ rootfs.yaffs
烧写:
tftp 0x30008000 rootfs.yaffs2
nand erase 0x580000 7a80000
nand write.yaffs 0x30008000 0x580000 dbb040
设置内核启动参数:
setenv bootargs "noinitrd root=/dev/mtdblock3 rootfstyle=yaffs2 console=ttySAC0,115200 init=/linuxrc mem=64M"
setenv bootcmd "nand read 0x30008000 0x80000 0x500000;bootm 0x30008000"
(3) Cramfs:Compressed ROM File System
Cramfs是Linux的创始人Linus Torvalds参与开发的一种只读的压缩文件系统。它也基于MTD驱动程序。
在cramfs文件系统中,每一页(4KB)被单独压缩,可以随机页访问,其压缩比高达2:1,为嵌入式系统节省大量的Flash存储空间,使系统可通过更低容量的FLASH存储相同的文件,从而降低系统成本。
Cramfs文件系统以压缩方式存储,在运行时解压缩,所以不支持应用程序以XIP(eXecute In Place,芯片内执行)方式运行,所有的应用程序要求被拷到RAM里去运行,但这并不代表比Ramfs需求的RAM空间要大一点,因为Cramfs是采用分页压缩的方式存放档案,在读取档案时,不会一下子就耗用过多的内存空间,只针对目前实际读取的部分分配内存,尚没有读取的部分不分配内存空间,当我们读取的档案不在内存时,Cramfs文件系统自动计算压缩后的资料所存的位置,再即时解压缩到RAM中。
另外,它的速度快,效率高,其只读的特点有利于保护文件系统免受破坏,提高了系统的可靠性。
由于以上特性,Cramfs在嵌入式系统中应用广泛。
但是它的只读属性同时又是它的一大缺陷,使得用户无法对其内容对进扩充。Cramfs映像通常是放在Flash中,但是也能放在别的文件系统里,使用loopback 设备可以把它安装别的文件系统里。
(4) Romfs
传统型的Romfs文件系统是一种简单的、紧凑的、只读的文件系统,不支持动态擦写保存,按顺序存放数据,因而支持应用程序以 XIP(eXecute In Place,片内运行)方式运行,在系统运行时,节省RAM空间。uClinux系统通常采用Romfs文件系统。
(5)ubifs
UBIFS:基于UBI的FLASH日志文件系统。
a)配置内核支持UBIFS
Device Drivers --->Memory Technology Device (MTD) support --->UBI - Unsorted block images --->Enable UBI
配置mtd支持UBI接口
File systems --->Miscellaneous filesystems --->UBIFS file system support
配置内核支持UBIFS文件系统
b)将一个MTD分区4挂载为UBIFS格式
● flash_eraseall /dev/mtd4 //擦除mtd4
● ubiattach /dev/ubi_ctrl -m 4 //和mtd4关联
● ubimkvol /dev/ubi0 -N rootfs -s 100MiB //设定volume 大小(不是固定值,可以用工具改变)及名称
● mount -t ubifs ubi0_0 /mnt/ubi或mount -t ubifs ubi0:rootfs /mnt/ubi
c)制作UBIFS文件系统
在制作UBI镜像时,需要首先确定以下几个参数:
MTD partition size; //对应的FLASH分区大小
flash physical eraseblock size; // FLASH物理擦除块大小
minimum flash input/output unit size; //最小的FLASH输入输出单元大小
for NAND flashes - sub-page size; //对于nand flash来说,子页大小
logical eraseblock size.//逻辑擦除块大小
参数可以由几种方式得到
a)如果使用的是2.6.30以后的内核,这些信息可以通过工具从内核获得,如:mtdinfo –u。
b)之前的内核可以通过以下方法:
● MTD partition size:从内核的分区表或cat /proc/mtd获得
● flash physical eraseblock size:从flash芯片手册中可以得到FLASH物理擦除块大小,或cat /proc/mtd
● minimum flash input/output unit size:
1)nor flash:通常是1个字节
2)nand falsh:一个页面
● sub-page size:通过flash手册获得
● logical eraseblock size:对于有子页的NAND FLASH来说,等于“物理擦除块大小-1页的大小”
c)也可以通过ubi和mtd连接时的产生的信息获取,如:
#modprobe ubi mtd=4 //ubi作为模块加载
或
#ubiattach /dev/ubi_ctrl -m 4 //通过ubiattach关联MTD
UBI: attaching mtd4 to ubi0
UBI: physical eraseblock size: 131072 bytes (128 KiB)
UBI: logical eraseblock size: 129024 bytes
UBI: smallest flash I/O unit: 2048
UBI: sub-page size: 512
UBI: VID header offset: 512 (aligned 512)
UBI: data offset: 2048
UBI: attached mtd4 to ubi0
更详细的解释参见http://www.linux-mtd.infradead.org/doc/ubi.html#L_overhead
#mkfs.ubifs -r rootfs -m 2048 -e 129024 -c 812 -o ubifs.img
#ubinize -o ubi.img -m 2048 -p 128KiB -s 512 /home/lht/omap3530/tools/ubinize.cfg
-r:制定文件内容的位置
-m:页面大小
-e:逻辑擦除块大小
-p:物理擦除块大小
-c:最大的逻辑擦除块数量
对我们这种情况,文件系统最多可以访问卷上的129024*812=100M空间
-s:最小的硬件输入输出页面大小,如:k9f1208为256(上下半页访问)
其中,ubinize.cfg的内容为:
[ubifs]
mode=ubi
image=ubifs.img
vol_id=0
vol_size=100MiB
vol_type=dynamic
vol_name=rootfs
vol_flags=autoresize
利用uboot烧写、启动UBIFS镜像
a)烧写UBIFS镜像
OMAP3 DevKit8000 # mmcinit
OMAP3 DevKit8000 # fatload mmc 0:1 81000000 ubi.img
reading ubi.img
12845056 bytes read
OMAP3 DevKit8000 # nand unlock
device 0 whole chip
nand_unlock: start: 00000000, length: 268435456!
NAND flash successfully unlocked
OMAP3 DevKit8000 # nand ecc sw
OMAP3 DevKit8000 # nand erase 680000 7980000
NAND erase: device 0 offset 0x680000, size 0x7980000
Erasing at 0x7fe0000 -- 100% complete.
OK
OMAP3 DevKit8000 # nand write.i 81000000 680000 $(filesize)
NAND write: device 0 offset 0x680000, size 0xc40000
Writing data at 0x12bf800 -- 100% complete.
12845056 bytes written: OK
烧写过程和烧写内核镜像的过程一致,所以UBI文件系统应该不像yaffs文件系统那样用到了nand的OOB区域。
b)设置UBIFS文件系统作为根文件系统启动的参数
OMAP3 DevKit8000 # setenv bootargs console=ttyS2,115200n8 ubi.mtd=4 root=ubi0:rootfs
rootfstype=ubifs video=omapfb:mode:4.3inch_LCD
OMAP3 DevKit8000 # setenv bootcmd nand read.i 80300000 280000 200000\;bootm 80300000
其他文件系 统:fat/fat32也可用于实际嵌入式系统的扩展存储器(例如PDA, Smartphone, 数码相机等的SD卡),这主要是为了更好的与最流行的Windows桌面操作系统相兼容。ext2也可以作为嵌入式Linux的文件系统,不过将它用于 FLASH闪存会有诸多弊端。
2. 基于RAM的文件系统
(1) Ramdisk
RamDisk 就是将内存中的一块区域作为物理磁盘来使用的一种技术。它并非一个实际的文件系统,而是一种将实际的文件系统装入内存的机制,并且可以作为根文件系统。将一些经常被访问而又不会更改的文件(如只读的根文件系统)通过Ramdisk放在内存中,可以明显地提高系统的性能。在Linux的启动阶段,initrd提供了一套机制,可以将内核映像和根文件系统一起载入内存。
为了能够使用RamDisk 你的内核必须要支持:在编译内核时,要选中RAM disk support这一选项,会在配置文件中定义CONFIG_BLK_DEV_RAM。
为了让内核有能力在内核加载阶段就能装入RAMDISK,并运行其中的内容,要选中initial RAM disk(initrd) support 选项,会在配置文件中定义CONFIG_BLK_DEV_INITRD。
RamDisk是临时性的,所以没有带日志的文件系统的必要,所以我们一般做 ext2 就可以了。制作方式:
genext2fs -b 81920 -d rootfs ramdisk
gzip -9 -f ramdisk
mkimage -A arm -O linux -T ramdisk -C none -a 0x88080000 -n "ramdisk" -d ramdisk.gz ramdisk.img
在设置启动参数时:
setenv bootargs console=${console} ${optargs} root=/dev/ram0 rw ramdisk_size=81920 initrd=${rdaddr},80M rootfstype=${ramrootfstype}
其中,ramdisk_size=N这个参数告诉RAM磁盘驱动将RAM磁盘的大小设置为N k。RAM磁盘的大小会根据需要动态的增长,因此其大小有个上限加以限制以免它用光所有可用的内存而坏事。ramdisk_size这个参数实际是设置这个上限值的。
我们可以通过命令:dmesg | grep RAMDISK来查看这个上限值。要注意的是,这个值在系统运行阶段是不能再被修改的。尽管RAM磁盘的大小有个最大值,但我们可以指定需要使用的RAM磁盘的容量。当我们没有指定需要使用的RAM磁盘的容量,而直接在其上挂载文件系统时,它的容量是其上限值。
(2)ramfs/tmpfs
Ramfs是Linus Torvalds开发的一种基于内存的文件系统,工作于虚拟文件系统(VFS)层,不能格式化,可以创建多个,在创建时可以指定其最大能使用的内存大 小。(实际上,VFS本质上可看成一种内存文件系统,它统一了文件在内核中的表示方式,并对磁盘文件系统进行缓冲。)
Ramfs/tmpfs文件系统把所有的文件都放在RAM中,所以读/写操作发生在RAM中,可以用ramfs/tmpfs来存储一些临时性或经常要修改的数据,例如/tmp和/var目录,这样既避免了对Flash存储器的读写损耗,也提高了数据读写速度。
Ramfs/tmpfs相对于传统的Ramdisk的不同之处主要在于:不能格式化,文件系统大小可随所含文件内容大小变化。
Tmpfs的一个缺点是当系统重新引导时会丢失所有数据。
3. 网络文件系统NFS (Network File System)
NFS是由Sun开发并发展起来的一项在不同机器、不同操作系统之间通过网络共享文件的技术。在嵌入式Linux系统的开发调试阶段,可以利用该技术在主机上建立基于NFS的根文件系统,挂载到嵌入式设备,可以很方便地修改根文件系统的内容。
以上讨论的都是基于存储设备的文件系统(memory-based file system),它们都可用作Linux的根文件系统。实际上,Linux还支持逻辑的或伪文件系统(logical or pseudo file system),例如procfs(proc文件系统),用于获取系统信息,以及devfs(设备文件系统)和sysfs,用于维护设备文件。