linux文件系统

来源:互联网 发布:java 定义空的数组 编辑:程序博客网 时间:2024/06/05 18:19

通过前面内核的启动流程,可以知道内核启动时,要挂载文件系统,而文件系统包括根文件系统以及建立于Flash设备上的文件系统,里面包含了Linux系统能够运行所必需的应用程序和库,比如给用户提供操作Linux控制界面的shell程序和动态链接的程序运行时需要的库等等。还记得在烧录ARM最小系统的时候,如果没有烧文件系统,那么我们的系统就无法完整启动,而是卡在那里,那么,内核在等什么呢?

内核在找我们的根文件系统,在linux系统下,一切皆文件就是这么来的,文件系统用于用户与操作系统交互,就像windows下的各个磁盘一样,如果没有这些,我们的软件和文件存在哪里呢?而Linux启动时,第一个必须挂载的就是根文件系统,根文件系统首先是一种文件系统,该文件系统不仅具有普通文件系统的存储数据文件的功能,但是相对于普通的文件系统,它的特殊之处在于,它是内核启动时所挂载(mount)的第一个文件系统,内核代码的映像文件保存在根文件系统中,系统引导启动程序会在根文件系统挂载之后从中把一些初始化脚本(如rcS,inittab)和服务加载到内存中去运行。

至于内核是如何加载根文件系统,用户程序和系统空间有一个虚拟文件系统接口VFS,它完美地屏蔽了各种文件系统的差异性,我们在使用文件系统的时候,不必考虑这些差异性,正如我们的windows下,各种文件都能共存一样,一个磁盘里面可以放各种不同类型的文件,而根文件系统如何挂载,等我们做了根文件系统再聊。

下面就简单地谈一谈我们的根文件系统的制作过程

  1. 先谈一谈我们的根文件系统所需要的东西吧
/ 根目录 bin 存放所有用户都可以使用的基本命令 sbin 存放系统命令,只有超级权限可以使用的命令 usr 包含若干个子目录,用于存放用户程序及文档 dev 存放设备文件,设备文件是linux系统中特有的类型,在linux系统中,所有的设备都是通过访问设备文件来实现,例如串口的读就是接收数据,写就是发送数据 etc 存放各种配置文件,其中inittab是init进程的配置文件,fstab是我们执行挂载命令(mount -a)所用到的 配置文件 lib 存放基本的共享库,用于启动系统,运行根文件系统中的可执行程序 proc 一个空目录,常作为proc文件系统的挂载点,proc文件系统是虚拟的文件系统 ,没有实际的存储设备,里面的目录、文件都是内核临时生成的,用于表示系统的运行状态,也可以操作其中的文件来控制系统(挂载/卸载文件系统,mount/umount) sys 空目录,临时挂载某个文件系统的挂载点 tmp 存放临时文件,空目录 home 用户目录 var 用于存放系统日志及一些服务程序的临时文件

而对于嵌入式文件系统而言,根文件系统的结构都是相同的,因此这里谈一谈最小的根文件系统:bin、sbin、dev、lib、etc、proc。

对于这些文件,我们如何去创建以及如何写里面的内容,毋庸置疑非常苦恼,比如我们常用的命令ls,cd等等,我们可以自己去编程实现,但是纯属扯淡。幸运的是我们有瑞士军刀美誉的Busybox开源软件包帮我们制作这些东西,Busybox将众多的命令集合进了一个很小的可执行程序,与GNU工具相比,所提供的选项较少,但是已经能够满足一般应用了,为各种小型或者嵌入式系统提供了一个比较完全的工具集,按照模块进行设计,可以很容易地选择我们需要或者不需要的命令。

这里用到的Busybox版本是busybox-1.20.2,我们只需要修改解压后的Makefile文件中的交叉编译器的路径即可。

CROSS_COMPILE ?= /opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-

然后查看我们的安装路径,将安装路径设置为我们预先创建的根文件系统目录下即可。其他的命令那些选项根据自己的需求选择即可。

Busybox Settings  --->       General Configuration  --->             [*] Don't use /usr       Installation Options ("make install" behavior)  --->             What kind of applet links to install (as soft-links)  --->             (../rootfs) BusyBox installation prefix

这里要提一下的是有一个选项配置决定我们将所有命令以什么方式链接进busybox中:

Busybox Settings  --->                        Installation Options ("make install" behavior)  --->                        (X) as soft-links                        ( ) as hard-links                        ( ) as script wrappers                        ( ) not installed

我们使用软链接的方式将所有命令软链接到busybox中,但是有些设备不支持这种方式,比如我们将文件系统部署在SD卡这种设备中。

当我们的配置完成以后,就可make && make install了,成功以后我们的目录下会生成4个文件:bin、sbin、usr、linuxrc

  1. 接下来创建我们的dev目录,用于存放设备文件:字符设备(c),块设备(b),网络设备,在创建好文件之后,我们直接在这个目录执行创建设备节点的命令:
sudo mknod name c 5 1 //创建一个主设备号5,次设备号1的块设备节点sudo mknod -m666 null c 1 3………………………………………………按需分配咯
  1. 创建lib库目录

因为我们编译生成的的busybox在bin目录下,其他的命令都要链接它,所以我们可以根据busybox所需的链接关系:

[tangyanjun@VM_216_80_centos busybox-1.20.2]$arm-linux-readelf  -d  bin/busyboxDynamic section at offset 0xdd00c contains 22 entries:  Tag        Type                         Name/Value 0x00000001 (NEEDED)                     Shared library: [libm.so.0] 0x00000001 (NEEDED)                     Shared library: [libc.so.0] 0x0000000c (INIT)                       0xbc08 0x0000000d (FINI)                       0xc3488

可以看见需要的是libm.so.0和libc.so.0两个库。通过find命令将这两个库找到,然后在我们的交叉编译器中可以看见,我们将交叉编译器下的链接关系文件和库文件都拷贝过来

[tangyanjun@VM_216_80_centos buildroot-2012.08]$ find -iname libm.so.0./arm920t/usr/arm-unknown-linux-uclibcgnueabi/sysroot/lib/libm.so.0./output/target/lib/libm.so.0./output/toolchain/uClibc-0.9.33.2/lib/libm.so.0[tangyanjun@VM_216_80_centos buildroot-2012.08]$ cd arm920t/usr/arm-unknown-linux-uclibcgnueabi/sysroot/lib/[tangyanjun@VM_216_80_centos lib]$ lsld-uClibc-0.9.33.2.so  libdl.so.0          libnsl.so.0             librt.so.0ld-uClibc.so.0         libgcc_s.so         libpthread-0.9.33.2.so  libuClibc-0.9.33.2.solibcrypt-0.9.33.2.so   libgcc_s.so.1       libpthread.so.0         libutil-0.9.33.2.solibcrypt.so.0          libm-0.9.33.2.so    libresolv-0.9.33.2.so   libutil.so.0libc.so.0              libm.so.0           libresolv.so.0libdl-0.9.33.2.so      libnsl-0.9.33.2.so  librt-0.9.33.2.so[tangyanjun@VM_216_80_centos lib]$
  1. 创建etc目录

当系统init进程启动后,会来这里找配置文件inittab

inittab的格式:<id>:<runlevels>:<action>:<process>


  • id:省略
  • runlevels:省略
  • action:程序执行的时间,分为8种

  • Sysinit:系统启动后最先执行,并且只执行一次,init进程等待它结束后继续执行其他操作,以这个修饰的程序最先执行。
  • wait:执行完sysinit进程之后执行,只执行一次
  • once:执行完wait进程之后,只执行一次
  • respawn:系统执行完once进程后,init进程监测,返现子进程退出后重新启动
  • askfirst:系统执行完respawn后执行,init进程监测,发现子进程退出时重新启动它,输出”please press enter to acticate this console”
  • shutdown:当关机时执行
  • restart:重启
  • ctrlaltdel:按下ctrl+alt+del组合键时执行
  • process:要执行的程序,也可以是脚本,如果有这个标志‘-’,表示程序可交互

来个例子:
::sysinit:echo       "hello embedded!"  //输出::askfirst:-/bin/sh     //循环执行这个脚本::shutdown:/bin/umount -a  //关机时执行

换成脚本:

::sysinit:/etc/init.d/rcS     //将具体的程序替换成脚本,在脚本中可以执行若干条命令::askfirst:-/bin/sh::shutdown:/bin/umount -a

先创建init.d目录,再写inittab脚本,再写rcS脚本,最后给其执行权限,我们在rcS中执行挂载命令,就会执行fstab配置文件,因此创建fstab文件

device   mount-point    type      options    dump  fsck orderproc      /proc          proc     defaults    0        0sysfs     /sys          sysfs     defaults    0        0

device:设备文件,要挂载的设备
mount-point:挂载点
type:要挂载的文件系统的类型
options:挂载的参数(默认)
dump:0
fsck order:0

系统启动后,执行inittab配置文件中的sysinit动作

::sysinit:/etc/init.d/rcS
-》执行/etc/init.d/rcS ->执行mount -a ->/etc/fstab 配置文件,定义要挂载的设备以及挂载方式 -》 askfirst:-/bin/sh ->启动shell
最后修改u-boot的参数bootargs

我们只要将系统启动要做的事情,写到rcS脚本中,让其启动就执行。

  1. proc

proc用于挂载/卸载文件系统

6.其余的空的文件自己创建就行了

而测试我们的根文件系统是否可行有几种方法:initramfs测试以及NFS测试。这里讲一下第一种,即我们在内核中选中initramfs并且制定我们的根文件系统路径即可。

General setup  --->        [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support        (/opt/rootfs) Initramfs source file(s)

重新编译内核,烧进开发板看能否启动即可。

这样一个最简单的根文件系统就算完成了,但是还有很多可以扩展的东西,比如用户的登录用户名和登录密码、网络的配置等,这里就不细说了,有兴趣的朋友可自行创作。

既然最小根文件系统已经完成,下面谈谈其他文件系统

有一个问题,既然我们的系统能跑了,为啥还要其他的文件系统呢?首先,这些文件系统都需要在根文件系统挂在起来的前提下才能挂载,然后我们可以发现利用initramfs起来的文件系统是掉电丢失的,每次重启板子就没了,很烦,而使用其他的文件系统最重要的一个作用就是备份,没错,相当于windows下的非系统盘,万一我们的根文件系统炸了,那么不会影响其他分区挂载的文件系统。

基于RAM的文件系统:initramfs、ramdisk、ramfs/tmpfs、NFS
基于FLASH的文件系统(一块Flash芯片可以被划分为多个分区,各分区可以采用不同的文件系统;两块Flash芯片也可以合并为一个分区使用,采用一个文件系统。即文件系统是针对于存储器分区而言的,而非存储芯片。):jffs2、yaffs、ubifs、cramfs、romfs

这里介绍一下本人做过的一些文件系统:
jffs2:

JFFS文件系统最早是由瑞典Axis Communications公司基于Linux2.0的内核为嵌入式系统开发的文件系统。JFFS2(Journalling Flash FileSystem v2,日志闪存文件系统版本2 )是RedHat公司基于JFFS开发的闪存文件系统,最初是针对RedHat公司的嵌入式产品eCos开发的嵌入式文件系统,所JFFS2也可以用在Linux, uCLinux中。它主要用于NOR型闪存,基于MTD驱动层,特点是:可读写的、支持数据压缩的、基于哈希表的日志型文件系统,并提供了崩溃/掉电安全保护,提供“写平衡”支持等。缺点主要是当文件系统已满或接近满时,因为垃圾收集的关系而使jffs2的运行速度大大放慢。
Jffs2不适合用于NAND闪存主要是因为NAND闪存的容量一般较大,这样导致jffs2为维护日志节点所占用的内存空间迅速增大,另外,jffs2文件系统在挂载时需要扫描整个FLASH的内容,以找出所有的日志节点,建立文件结构,对于大容量的NAND闪存会耗费大量时间。
目前jffs3正在开发中,关于jffs2系列文件系统的使用详细文档,可参考MTD补丁包中mtd-jffs-HOWTO.txt。

ubifs

无排序区块图像文件系统(Unsorted Block Image File System, UBIFS)是用于固态硬盘存储设备上,并与LogFS相互竞争,作为JFFS2的后继文件系统之一。真正开始开发于2007年,并于2008年10月第一次加入稳定版本于Linux核心2.6.27版。UBIFS最早在2006年由IBM与Nokia的工程师Thomas Gleixner,Artem Bityutskiy所设计,专门为了解决MTD(Memory Technology Device)设备所遇到的瓶颈。由于Nand Flash容量的暴涨,YAFFS等皆无法再去控制Nand Flash的空间。UBIFS通过子系统UBI处理与MTD device之间的动作。与JFFS2一样,UBIFS 建构于MTD device 之上,因而与一般的block device不兼容。
JFFS2运行在MTD设备之上,而UBIFS则只能工作于UBI volume之上。也可以说,UBIFS涉及了三个子系统:
1. MTD 子系统, 提供对flash芯片的访问接口, MTD子系统提供了MTD device的概念,比如/dev/mtdx,MTD可以认为是raw flash
2. UBI subsystem,为flash device提供了wear-leveling和 volume management功能; UBI工作在MTD设备之上,提供了UBI volume;UBI是MTD设备的高层次表示,对上层屏蔽了一些MTD不得不处理的问题,比如wearing以及坏块管理
3. UBIFS文件系统,工作于UBI之上
以下是UBIFS的一些特点:
可扩展性:UBIFS对flash 尺寸有着很好的扩展性; 也就是说mount时间,内存消耗以及I/O速度都不依赖与flash 尺寸(对于内存消耗并不是完全准确的,但是依赖性非常的低); UBIFS可以很好的适应GB flashes; 当然UBI本身还有扩展性的问题,无论如何 UBI/UBIFS都比JFFS2的可扩展性好,此外如果UBI成为瓶颈,还可以通过升级UBI而不需改变UBIFS
快速mount:不像JFFS2,UBIFS在mount阶段不需要扫描整个文件系统,UBIFS mount介质的时间只是毫秒级,时间不依赖与flash的尺寸;然而UBI的初始化时间是依赖flash的尺寸的,因此必须把这个时间考虑在内
write-back 支持: 回写或者叫延迟写更准确些吧,同JFFS2的write-through(立即写入内存)相比可以显著的提高文件系统的吞吐量。
异常unmount适应度:UBIFS是一个日志文件系统可以容忍突然掉电以及unclean重启; UBIFS 通过replay 日志来恢复unclean unmount

相对于其他的文件系统,ubifs文件系统是较好的。