Linux文件系统

在Unix业界有一句话叫做“一切皆文件”,这是对Linux文件系统的一个很好抽象。
我们说“Linux中一切皆文件”直观的可以这样理解:所有的东西,设备、内存都模拟成文件。

而Windows中,我们则可以极端的认为“一切皆设备”。

常用的文件系统

第二代扩展文件系统(EXT2)

ext2fs由Rey Card设计,其目标是为Linux提供一个强大的可扩展文件系统。

  • 支持标准unix文件类型
  • 管理大的分区,达4TB
  • 支持长文件名,255字符
  • 为超级用户保留5%数据块

基于FLASH的文件系统

FLASH(闪存)作为嵌入式系统的主要存储媒介,有其自身的特性。FLASH的写入操作只能把对应位置的1修改为0,而不能把0修改为1(擦除FLASH就是把对应存储块的内容恢复为1),因此,一般情况下,向FLASH写入内容时,需要先擦除对应的存储区间,这种擦除是以块(block)为单位进行的。在往 Flash 擦除块写数据(Write)之前必须将擦除块 (Erase Block)先进行擦除(Erase)操作。Flash 擦除操作的最小单位是擦除块(Erase Block), 写操作的最小单位是page。

正是因为Flash这样的特点,为Flash 存储器设计的文件系统需要实现“不在原地更新”(Out-Of-Place update)。
如果擦除块很小,而且可以快速的擦除,那样的话擦除块就可以被视为块设备的 disk sectors,但是事实往往并非如此。读取一个擦除块中的数据,擦除这个擦除块,然后再将更新的数据写回这个擦除块往往比简单的直接将更新的数据写到另外一个已经被擦除过的擦除块多花费100倍的时间。也就是说,在原地更新数据(In-Place updates)往往比“不在原地更新”(Out-Of-Place Update)多用100倍的时间。

当然,Out-Of-Place update 也随之引入了其他的要求– 垃圾回收(Garbage Collection)。 正是因为更新数据采用的Out-Of-Place, 擦除块就会既包含有效数据也包含被废弃的无效数据(有效数据已经被更新到其他地方)。 这样,最后文件系统会用光所有的擦除块,而一个擦除块将是有效数据和无效数据的结合体。 识别一个擦除块中含有很多废弃数据,并将其中的有效数据转移到另外的擦除块的过程被称为垃圾回收(Garbage Collection)。

垃圾回收得益于文件系统的节点结构(Node structure)。文件系统的垃圾回收器如果想回收一个擦除块,这个文件系统必须能够识别存在擦除块上的数据。这正是文件系统面临的查找索引的反向问题。文件系统往往通过文件名开始查找所有属于这个文件的数据。垃圾回收正好相反,垃圾回收是以数据作为起始线索,查找它所属于的文件(如果有的话)。解决这一问题的一种方式是把元数据(meta data)和文件数据(file data)一起存储。这种文件数据和数据的组合体称之为节点(node)。每个Node记录了这个node 中的元数据隶属于哪个文件(更确切的说是inode number),以及在文件中的offset 以及数据长度等。 JFFS2 和 UBIFS 文件系统都遵循了节点结构(node-structured)的设计模式,这使得它们的垃圾收集器可以直接读取擦除块就能可以知道哪些数据需要移动,哪些数据可以丢弃,并一一更新它们的索引。

闪存主要有NOR和NAND两种技术。
NAND和NOR的区别:

  • 都属于闪存,操作单位是字节,而不是扇区;
  • NOR,更适合存储程序代码,容量一般比较小(比如32M),可擦写次数几十万次;
  • NAND,适合存储数据,容量比较大,与NOR相比,NAND不是很可靠,出厂时会有一定比例的坏块,对数据的存取不是通过地址映射,而是通过寄存器操作,串行读取数据,可擦写次数数百万;
  • JFFS与YAFFS比较,两者各有长处. 一般来说,对于容量较小的NOR Flash,可以选用JFFS;对于容量较大的NAND Flash,比如超过64MBytes,用YAFFS比较合适。

FLASH的擦写次数是有限的,NAND闪存还有特殊的硬件接口和读写时序。因此,必须针对Flash的硬件特性设计符合应用要求的文件系统;传统的文件系统如ext2等,用作Flash的文件系统会有诸多弊端。

在嵌入式Linux下,MTD(Memory Technology Device,存储技术设备)为底层硬件(闪存)和上层(文件系统)之间提供一个统一的抽象接口,即Flash的文件系统都是基于MTD驱动层的。使用MTD驱动程序的主要优点在于,它是专门针对各种非易失性存储器(以闪存为主)而设计的,因而它对Flash有更好的支持、管理和基于扇区的擦除、读写操作接口。

JFFS2

日志闪存文件系统版本2(Journalling Flash File System v2)

JFFS最早是由瑞典Axis Communications公司基于Linux2.0的内核为嵌入式系统开发的文件系统。
JFFS2是RedHat公司基于JFFS开发的闪存文件系统,最初是针对RedHat公司的嵌入式产品eCos开发的嵌入式文件系统,所以JFFS2也可以用在Linux,uclinux中。

JFFS2 Overview
JFFS2, the Journalling Flash File System version 2 is widely used in the embedded systems world. JFFS2 was originally designed for small NOR flashes (less then about 32MB) and the first device with JFFS2 file system was a small bar-code scanner. Later,when NAND flashes became widely used, NAND support was added to JFFS2. The firstNAND flashes were also small enough, but grew in size very quickly and are currently much larger then 32MB.JFFS2 has log-structured design, which basically means, that the whole file system may be regarded as one large log. Any file system modification (i.e., file change, directory
creation, changing files attributes, etc) is appended to the log. The log is the only data structure on the flash media. Modifications are encapsulated into small data structures called nodes.So, JFFS2 is roughly a log, the log consists of nodes, each node contains a file system modification. And this is basically all JFFS2 file system is. It is very simple from the physical layouts standpoint.

The index is a crucial part of any file system as it is used to keep track of everything that is stored in the file system. For example, the index may help to quickly locate the addresses of physical blocks which correspond to the specified file at the specified offset,or it helps to quickly find all the directory entries in a specified directory and so on.For example, in case of ext2, the inode table, the bitmap and the set of direct, indirect,doubly indirect and triply indirect pointers may be considered the index. In case of the FAT file system, the File Allocation Table may be considered as the index, etc.In traditional file systems the index is usually kept and maintained on the media, but unfortunately, this is not the case for JFFS2. In JFFS2, the index is maintained in RAM,not on the flash media. And this is the root of all the JFFS2 scalability problems.Of course, as the index in kept in RAM, JFFS2 achieves extremely high throughput,just because it does not need to update the index on flash after something has been changed in the file system. And this works very well for relatively small flashes, for which JFFS2 was originally designed. But as soon as one tries to use JFFS2 on large flashes (starting from about 128MB), many problems come up.At first, it is obvious that JFFS2 needs to build the index in RAM when it mounts the file system. For this reason, it needs to scan the entire flash partition in order to locate all the nodes which are present there. So, the larger is JFFS2 partition, the more
nodes it has, the longer it takes to mount it.

The second, it is evidently that the index consumes some RAM. And the larger is the JFFS2 file system, the more nodes it has, the more memory is consumed.
To put it differently, if S is the size of the JFFS3 flash partition,

  • JFFS2 mount time scales as O(S) (linearly);
  • JFFS2 memory consumption scales as O(S) (linearly).

JFFS2 has many advantages, for example:

  • very economical flash usage. data usually take as much flash space as it actuallyneed, without wasting a lot space as in case of traditional file systems for block devices;
  • admitting of on-flight compression which allows to fit a great deal of data to the flash; note, there are few file systems which support compression;
  • very good file system write throughput (no need to update any on-flash indexing information as it simply does not exist there);
  • unclean reboots robustness;
  • good enough wear-leveling.

It is also worth noting here that there is a patch which is usually referred to as the summary patch, that was implemented by Ferenc Havasi and was recently committed to the JFFS2 CVS. This patch speeds up the JFFS2 mount greatly, especially in case of NAND flashes. What the patch basically does is that it puts a small summary node at the end of each flash erasable block. This node, roughly speaking, contains the copy of headers of all the nodes in this eraseblocks. So, when JFFS2 mounts the file system,it needs to glance to the end of each eraseblock and read the summary node. This results in that JFFS2 only needs to read one or few NAND pages from the end of each eraseblock. Instead, when there is no summary, JFFS2 reads almost every NAND page in each eraseblock, because node headers are spread more or less evenly over eraseblocks.Although the patch helps a lot, it is still a not scalable solution and it only relaxes the coefficient of the JFFS2 mount time liner dependency. Let alone that it does not lessen JFFS2 memory consumption.

JFFS2主要用于NOR型闪存
基于MTD驱动层,特点是:
1.可读写的、
2.支持数据压缩的、
3.基于哈希表的日志型文件系统
4.提供崩溃/掉电安全保护
5.提供“写平衡”支持等。
缺点是:
当文件系统已满或接近满时,因为垃圾收集的关系而使jffs2的运行速度大大放慢。
jffsx不适合用于NAND闪存主要是因为NAND闪存的容量一般较大,这样导致jffs为维护日志节点所占用的内存空间迅速增大,另外,jffsx文件系统在挂载时需要扫描整个FLASH的内容,以找出所有的日志节点,建立文件结构,对于大容量的NAND闪存会耗费大量时间。

jffs系列文件系统的使用详细文档,可参考MTD补丁包中mtd-jffs-HOWTO.txt

JFFS3
The main idea how to fix in JFFS2 to make it scalable is to move the index from RAM to flash. Unfortunately, this requires complete JFFS2 redesign and re-implementation and the design of JFFS3 is largely different to the design of JFFS2.

Indexing problem:

There is a large difference between block devices and flash devices in how they allow to update the contents of a sector. Block devices admit of so-called in-place updates, i.e.the update may be written straight to the sector. Flash devices do not allow this unless the whole eraseblock has been erased before.Obviously, it is unacceptable to erase the whole eraseblock each time a sector is updated. Instead, so-called out-of-place updates technique is usually used. This simply means that no attempts to update sectors in-place are made but instead, updates are written to some other sector and the contents of the previous sector is afterwords regarded as garbage.

This out-of-place writes property of flash devices assumes that JFFS3 also has
log-structured design as in JFFS3 any update is written out-of-place. And it seems that this is natural for any flash file system to have log-structured design.It is interesting to notice that in log-structured file systems for block devices not any update is out-of-place. There are always some fixed-position sectors present. These sectors usually refer the file system index, admit of quick file system mount and they are updated in-place.
But flash devices have limited number of erase cycles for each eraseblock and it is impossible to guaranty good wear-levelling if some eraseblocks are reserved for similar purposes. So, it is important that in JFFS3 there are no in-place updates as good wear-levelling is one of the main requirements to JFFS3.

wear-leveling这项技术在flash设备的微控制器上使用了一种算法,来跟踪flash上存储空间的使用情况。这使得数据每次能够重写到flash中的不同地方,而不是一直写入到同一个位置。在理想状态下,wear-leveling可以保证在重新使用空间之前,所有的物理flash存储空间都已经被使用过。使用这种方式,最大限度的使用了flash存储单元,而尽量减少了损耗的程度,所以可以保证flash设备的最长使用期限。但如果最终达到了最大使用次数,整个flash还是将会损坏。

The out-of-place updates property makes it difficult to maintain the index on the flash media.

YAFFS

Yet Another Flash File System
yaffs/yaffs2是专为嵌入式系统使用NAND型闪存而设计的一种日志型文件系统,提供磨损平衡和掉电恢复的健壮性。它还为大容量的Flash 芯片做了很好的调整,针对启动时间和RAM 的使用做了优化。与jffs2相比,它减少了一些功能(例如不支持数据压缩),所以速度更快,挂载时间很短,对内存的占用较小。另外,它还是跨平台的文件系统,除了Linux和eCos,还支持WinCE, pSOS和ThreadX等。

yaffs/yaffs2自带NAND芯片的驱动,并且为嵌入式系统提供了直接访问文件系统的API,用户可以不使用Linux中的MTD与VFS,直接对文件系统操作。当然,yaffs也可与MTD驱动程序配合使用。

YAFFS中,文件是以固定大小的数据块进行存储的,块的大小可以是512字节、1 024字节或者2 048字节。这种实现依赖于它能够将一个数据块头和每个数据块关联起来。每个文件(包括目录)都有一个数据块头与之相对应,数据块头中保存了ECC(Error Correction Code)和文件系统的组织信息,用于错误检测和坏块处理。充分考虑了NAND Flash的特点,YAFFS把这个数据块头存储在Flash的16字节备用空间中。当文件系统被挂载时,只须扫描存储器的备用空间就能将文件系统信息读入内存,并且驻留在内存中,不仅加快了文件系统的加载速度,也提高了文件的访问速度,但是增加了内存的消耗。

为了在节省内存的同时提高文件数据块的查找速度,YAFFS利用更高效的映射结构把文件位置映射到物理位置。文件的数据段被组织成树型结构,这个树型结构具有32字节的节点,每个内部节点都包括8个指向其他节点的指针,叶节点包括16个2字节的指向物理地址的指针。YAFFS在文件进行改写时总是先写入新的数据块,然后将旧的数据块从文件中删除。这样即使在修改文件时意外掉电,丢失的也只是这一次修改数据的最小写入单位,从而实现了掉电保护,保证了数据完整性。

结合贪心算法的高效性和随机选择的平均性,YAFFS实现了兼顾损耗平均和减小系统开销的目的。当满足特定的小概率条件时,就会尝试随机选择一个可回收的页面;而在其他情况下,则使用贪心算法来回收最“脏”的块。

yaffs与yaffs2的主要区别在于,前者仅支持小页(512 Bytes) NAND闪存,后者则可支持大页(2KB) NAND闪存。同时,yaffs2在内存空间占用、垃圾回收速度、读/写速度等方面均有大幅提升。

CRAMFS

由Linux Torvalds参与开发的小型只读压缩文件系统。

  • Inode、文件名称和目录信息不压缩
  • 单个文件最大为16MB
  • 数据压缩存放
  • 适合不需要写、且体积较大的文件系统,如/lib,/opt等
  • 与JFFS2、Cloop相比,读取速度快
  • 压缩率可以超过50%
  • 读取文件时,每次读取4k内容,解压缩到cache中
  • Linux内核已提供了对cramfs的支持,只要编译时选中

创建文件系统(生成image文件)

1
2
mkcramfs /lib lib.cramfs
mkcramfs /usr usr.cramfs

挂载文件系统

1
2
mount -t cramfs lib.cramfs /lib -o loop
mount -t cramfs usr.cramfs /usr -o loop

proc文件系统

/proc:一个虚拟文件系统,只存在于内存中,通过它可以查询、设置系统的运行情况及各种系统参数。

  • 系统中的很多应用都依赖于proc文件系统,如命令lsmod等同于cat /proc/modules。
  • 文件的大小为0
  • 很多文件名体现了内核的相应参数,可以通过这个文件名修改参数值。如:
    1
    2
    echo 2048 > /proc/sys/shmmni
    修改共享内存段的限制
  • /proc下的“数字目录”指代了相应pid的进程,如目录“1”下的内容就是1进程的各种信息。

Linux根文件系统

首先要明白的是“什么是文件系统”,文件系统是对一个存储设备上的数据和元数据进行组织的机制。这种机制有利于用户和操作系统的交互。

在Linux没有文件系统的话,用户和操作系统的交互也就断开了。

根文件系统首先是一种文件系统,该文件系统不仅具有普通文件系统的存储文件的功能,但是相对于普通的文件系统,它的特殊之处在于,它是内核启动时所mount的第一个文件系统。Linux内核在系统启动期间进行的最后操作之一就是安装根文件系统。根文件系统一直是所有类UNIX系统不可或缺的组件。

根文件系统的基本结构

  • bin 必要的用户命令(二进制文件)
  • boot 引导加载程序使用的静态文件
  • dev 设备文件及其他特殊文件
  • etc 系统配置文件
  • home 用户主目录
  • lib 必要的链接库,例如:C链接库、内核模块
  • mnt 临时挂载的文件系统的挂载点

BusyBox简介

BusyBox是很多标准Linux工具的一个单个可执行实现。BusyBox包含了一些简单的工具,例如cat和echo,还包含了一些更大、更复杂的工具,例如grep、find、mount以及telnet;有些人将BusyBox称为Linux工具里的“瑞士军刀”。可以使用Busybox制作根文件系统。

文章部分内容整理自:
www.cie-eec.org 相关文档

JFFS3 design issues