嵌入式开发者所做的最重要的决定之一就是部署哪种文件系统。有些文件系统性能比较高有些文件系统空间利用率比较高,还有一些文件系统设备故障或者意外断电后恢复数据比较方便。
linux文件系统概念
分区
分区是对物理设备的逻辑划分,而文件系统就存在于这个设备上。一个物理设备可以只包含一个分区,占据所有可用空间,或者,它可以被分成多个分区,以适合某个特定任务的要求。一个分区可以被看成一个逻辑盘,它上面可以存储一个完整的文件系统。一个分区就是物理媒介的一个逻辑部分,这个分区中数据的组织形式遵循此分区类型的相应规定。在最高的层次上,数据存储于物理设备的分区中。
linux采用fdisk工具来操控块设备上的分区。最新的fdisk工具能够识别90多种不同类型的分区。
下面清单显示了针对一个连接到USB端口的CompactFlash设备使用fdisk工具时的输出信息。在这个特定的目标系统中,物理CompactFlash设备分配的设备节点为/dev/sdb。
#fdisk /dev/sdb
Command (m for help):p
Disk /dev/sdb:49 MB,49349120 bytes
4 heads, 32 sectors/track, 753 cylinders
Units = cylinders of 128*512 = 65536 bytes
Device Boot Start End Blocks Id system
/dev/sdb1 * 1 180 11504 83 linux
/dev/sdb2 181 360 11520 83 linux
/dev/sdb3 361 540 11520 83 linux
/dev/sdb4 541 753 13632 83 linux
为了这里的讨论,我们已经使用fdisk工具在这个设备上创建了4个分区。其中之一被标记为可引导分区。这反映出在代表设备分区表的数据结构中有一个引导标识符的标志开关。按照这种方式对CompactFlash进行分区之后,每个分区都由一个设备节点表示,并且可以选择一种文件系统对其进行格式化。如果已经用某种文件系统对一个分区进行了格式化,linux就可以从那个分区挂载相应的文件系统了。
ext2
上面已经分好区了,接下来需要对fdisk创建的分区进行格式化。mkfs.ext2在指定的分区上创建一个ext2类型的文件系统。mkfs.ext2与ext2文件系统具体相关,其他类型的文件系统有自己相应的工具。下面显示了在格式化过程中的输出信息。
#mkfs.ext2 /dev/sdb1 -L CFlash_Boot_Vol
mke2fs 1.40.8 (13-Mar-2008)
Filesystem label=CFlash_Boot_Vol
OS type:linux
Block size=1024(log=0)
Fragment size=1024(log=0)
2880 inodes, 11504 blocks
575 blocks (5.00%) reserved for the super user
First data block=1
Maximum filesystem blocks=11796480
2 block groups
8192 blocks per group,8192 fragements per group
1440 inodes per group
Superblock backups stored on blocks:
8193
Writing inode tables:done
Writing superblocks and filesystem accounting information:done
This filesystem will be automatically checked every 33 mounts or 180
days, whichever comes first, Use tune2fs -c or -i to override
上述代码清单包含了许多有关ext2文件系统的详细信息。从这里开始理解ext2的运行是个不错的方法。这个分区被格式化为ext2类型(因为我们使用了ext2的mkfs工具),卷标(volume_label)为CFlash_Boot_Vol。它是在一个Linux分区上(OS Type:)上创建的,块大小为1024B。它为2880个inode分配了空间,共占用11504个块。inode这个数据结构代表一个文件。
看一下上述代码清单中mkfs.ext2的输出,我们可以确定存储设备的一些组织特征。我们已经知道了块大小为1024B。如果你的应用程序有特殊需求,可以让mkfs.ext2采用不同的块大小来格式化一个ext2文件系统。当前的mkfs.ext2实现支持1024、2048和4096B的块。
块大小总会为了达到最优性能而妥协。一方面,如果磁盘上有很多小文件,较大的块大小会浪费很多的空间,因为每个文件都必须由整数个块来存放。如果文件的大小超过了block_size*n,超出的部分必须用另一个完整的块,即使只是超出了1B。另一方面,如果采用较小的块大小,这会增加文件系统管理元数据的开销,元数据描述了块和文件之间的映射关系。你需要对特殊硬件实现和数据格式做一些基准测试,只有通过这个方法才能确定是否选择了最佳的块大小。
挂载文件系统
文件系统创建之后,我们可以将它挂载到一个运行的linux系统上。内核在编译时必须支持我们特定的文件系统的类型,可以将这个支持功能编译进内核,或是编译成一个动态可加载的模块。使用mount命令来挂载
mount /dev/sdb1 /mnt/flash
/mnt/flash是在目标linux设备上已有的一个目录,简称为挂载点。我们要挂载的设备是一个闪存设备,为其分配的设备节点为/dev/sdb1。挂载点可以是文件系统中任意一个目录路径,这个目录会成为新挂载设备的顶层目录。在上述例子中,为了访问闪存设备中的文件,必须在路径前加上/mnt/flash。
一个闪存设备的目录内容有bin、boot、dev、etc、home、lib、lost+found、proc、root、sbin、tmp、usr、var。这个设备是针对一个嵌入式系统配置的。从这个可以看出来,一个嵌入式设备上的根文件系统的顶层内容是什么样子。
检查文件系统的完整性
e2fsck命令用于检查一个文件系统的完整性。由几个原因会造成文件系统的损坏。最常见的原因是系统意外断电。linux发行版在关机时会关闭所有已打开的文并卸载文件系统(假设系统是正常有序的关闭的)。然而,对于嵌入式系统意外断电是很常见的事情,所以我们需要某些防范措施以应对这些情况。e2fsck是第一道防线。
下面显示了CompactFlash上运行e2fsck时的输出信息。他已经被正确卸载了所以没有错误发生
#e2fsck /dev/sdb1
e2fsck 1.40.8 (13-Mar-2008)
CFlash_Boot_Vol:clean,11/2880 files, 471/11504 blocks
e2fsck工具会对文件系统的几个方面做一致性检查。如果没有发现问题,e2fsck会输出一条类似于上述的输出信息。注意,e2fsck应该运行于一个未挂载的文件系统上。虽然有可能将它运行于一个已挂载的文件系统上,但这样做会对磁盘或闪存设备的内部文件系统结构造成严重损害。
我们将处于挂载状态的CompactFlash设备从插槽中拔除,下面显示了此时文件系统进行检测时的输出信息。
检查一个损坏了的文件系统
#e2fsck -y /dev/sdb1
e2fsck 1.40.8(13-Mar-2008)
/dev/sdb1 was not cleanly unmounted, check forced.
Pass 1: Checking inodes, blocks, and sizes
Inode 13, i_blocks is 16, should be 8.Fix?yes
/dev/sdb1: ********FILE SYSTEM WAS MODIFIED**********
/dev/sdb1: 25/2880 files (4% non-contiguous), 488/11504 blocks
检测到了没有正确卸载,e2fsck对文件系统进行了5轮遍历,检查了内部文件系统数据结构的各个成员。Inode 13被修正了,因为包含了-y选项。
嵌入式设计者应该注意一下情况:如果系统没有正确关机就意外断电,再次启动时会花费更长的启动时间,因为她要花时间扫描引导设备并修复发现的错误。实际上,如果这些错误是不可修复的,系统的启动就会终止,并且需要人为干预。此外,如果你的文件系统比较大,文件系统检测(fsck)会花费几分钟甚至几小时的时间。
另一种抵御文件系统损坏的方法是保证写造作能够立刻提交到磁盘上,即刚一写完就体现在硬件设备上。sync工具可用于强行将所有排队中的I/O请求提交到所对应的设备上。有一个策略可用于降低意外断电时数据损坏的可能性,就是在每次写文件之后执行一下sync命令,或是按照应用程序的需求有计划地执行sync命令。当然,代价是性能会受到影响。在所有现代操作系统中,延迟磁盘写操作是一种优化系统性能的方法。使用sync命令实际上抵消了这一优化。
ext3文件系统
ext3是一个强大、高性能和健壮的日志文件系统。ext3是对ext2文件系统的一个扩展,添加了日志功能。
那么什么是日志呢?日志是一项技术,采用这种技术,对文件系统所做的每次改变都会记录到一个特殊的文件中,这样文件系统就有可能从已知的日志点恢复过来。ext3有一搭优势,正如前边提到过,当系统意外关闭时,比如发生供电故障,系统会强行对文件系统进行一致性检查,而这个操作很费时间。如果使用ext3文件系统的话,就不需要一致性检查了,因为简单的回放一下日志就可以保证文件系统的一致性、
下面简单解释一下日志文件系统的工作方式。一个日志文件系统包含一个特殊的文件,通常对用户是隐藏的,他用于存储文件系统元数据(元数据是关于文件的数据不同于文件系统中存储的数据,元数据的例子包括文件的访问日期、时间、大小,使用的块量等)和文件数据本身,这个特殊的文件被称为日志。一旦文件系统被修改(比如一个写操作),这些修改首先会被写入日志中。文件系统的驱动程序保证该写操作首先被提交到日志中,之后实际的数据改变才会被提交到存储媒介上(比如磁盘或闪存)。当这些改变被记录到日志中后,驱动程序才会改变媒介上的实际文件和元数据。如果在向媒体写数据的过程中发生供电故障,之后系统重启,为了恢复文件系统的一致性,我们所要做的不过是回放一下日志中记录下来的改变。
可以将一个ext2文件系统转换成ext3文件系统,再转换回去,而不用重新格式化和重写磁盘上的所有数据、
#mount /dev/sdb1 /mnt/flash (挂载ext2文件系统)
#tune2fs -j /dev/sdb1 (创建日志)
tune2fs 1.37(21-Mar-2005,2881064151)
Creating journal inode:done
This filesystem will be automatically checked every 23 mounts or 180
days, whichever comes first.Use tune2fs -c or -i to override
#
先将文件系统挂载到/mnt/flash(正常情况下,我们在一个为挂载的ext2分区上执行这条命令)。如果文件系统已经挂载,tune2fs会创建一个名为.journal的日志文件,这是个隐藏文件。ls -al 可以显示包含隐藏文件的所有文件。
此时我们已在闪存模块上创建了日志文件,实际上他已经被格式化成了一个ext3文件系统。下一次系统重启或是在这个分区上运行e2fsck工具时,这个日志文件会自动不可见。他的元数据会被存储到一个专门为此保留的inode中。只要我们还能在目录的列表中看到.journal,修改或删除这个文件都是很危险的。
可以在另一台设备上创建日志文件,这样做有时还会带来好处。例如,你的系统有一个以上的物理设备,可以将你的ext3日志文件系统放置在第一个驱动器上,并将其日志文件放置在第二个驱动器上。无论物理设备是基于闪存还是旋转型媒体(磁盘),这个方法都是可行的。如果现有的ext2文件系统位于一个分区上,而日志文件存放在另一个分区上,为了从这个文件系统创建出日志文件系统,需要用下面这种方式来执行tune2fs命令:
#tune2fs -J device=/dev/sda1 -j /dev/sdb1