转载:http://www.oschina.net/question/129540_116839

在当前的嵌入式操作系统开发中,Linux 操作系统通常被压缩成 Image 后存放在 Flash 设备中。在系统启动过程中,这些 Image 被直接挂载到根文件系统, 然而这时的根文件系统是只读的, 用户不能在这个文件系统中进行任何写的操作。 如果把 Image 解压后直接拷贝到内存中,也可以实现写的功能,但是嵌入式系统一直存在内存大小方面的限制,所以将整个 Linux 系统拷入内存是不可取的。 本文将介绍一种直接挂载 Image 到根目录下,同时实现文件系统可读写的功能。

嵌入式 Linux 启动过程

本文所描述的的 Linux Image 由 BootLoader、kernel、initrd、rootfs 组成,它们共同存在于一个可以启动的存储设备中(本文以 USB 为例)。组成架构如下:

图 1. 可启动 linux 镜像文件结构 

各个模块的作用如下:

  • Boot Loader:由 BIOS 加载,用于将后续的 Kernel 和 initrd 的装载到内存中
  • kernel:为 initrd 运行提供基础的运行环境
  • initrd:检测并加载各种驱动程序
  • rootfs:根文件系统,用户的各种操作都是基于这个被最后加载的文件系统

其调用顺序是 Boot Loader->kernel->initrd->rootfs。

当机器上电时首先 BIOS 会启动,然后装载 USB 设备中的 Boot Loader、kernel,、nitrd 到内存中,由于这些文件大小总和小于 10M,所以我们直接拷贝到内存中再执行不会有问题。

最后要加载的 rootfs 是用户最终进行读写操作的文件系统。

  • 在非嵌入式系统中,这部分文件通常储存在可直接读写的硬盘上,因此直接挂载到根目录后(例如:mount /dev/sda1 /mnt)就可以进行读写操作。
  • 在嵌入式系统中,它是一个压缩的文件系统,大小通常是好几百兆,解压后的大小都超过 1G,如果直接 mount 到系统目录,那么系统目录是只读的,不可进行写入操作。而如果把它加压到内存中可以实现读写的操作,但是这么大的文件直接解压到内存中对于嵌入式设备来说 是不可接受的。因此我们需要找到一种不拷贝 rootfs 到内存中,同时又可以对最终的根文件系统进行读写的方法。

只读式压缩文件系统介绍

在嵌入式的环境之下,内存和外存资源都需要节约使用。如果使用 RAMDISK(把内存当作 disk)方式来使用文件系统,那么在系统运行之后,首先要把外存 (Flash) 上的映像文件解压缩到内存中,构造起 RAMDISK 环境,才可以开始运行程序。但是它也有很致命的弱点。在正常情况下,同样的代码不仅在外存中占据了空间 ( 以压缩后的形式存在 ),而且还在内存中占用了更大的空间 ( 以解压缩之后的形式存在 ),这违背了嵌入式环境下尽量节省资源的要求。以下两种方案的诞生就是为了解决这个问题:

CramFS

CramFS 文件系统是专门针对闪存设计的只读压缩的文件系统,它并不需要一次性地将文件系统中的所有内容都解压缩到内存之中,而只是在系统需要访问某个位置的数据的 时侯,马上计算出该数据在 CramFS 中的位置,将其实时地解压缩到内存之中,然后通过对内存的访问来获取文件系统中需要读取的数据。CramFS 中的解压缩以及解压缩之后的内存中数据存放位置都是由 CramFS 文件系统本身进行维护的,用户并不需要了解具体的实现过程,因此这种方式增强了透明度,对开发人员来说,既方便,又节省了存储空间。

SquashFS

SquashFS 也是一个只读的文件系统,它可以将整个文件系统压缩在一起,存放在某个设备,某个分区或者普通的文件中。如果您将其压缩到一个设备中,那么您可以将其直接 mount 起来使用,而如果它仅仅是个文件的话,您可以将其当为一个 loopback 设备使用。

更多信息请参考“SquashFS”和“CramFS”。

本文主要介绍基于 SquashFS 的可读写文件系统构建。

Squash 压缩文件系统的创建

"下载并安装 Squash 工具"

步骤 1:创建空的根文件系统,文件系统的大小为 65536 × 24000/1024/1024=1.5G。接下来我们会在这个空的根文件系统中存放文件。

dd: 读取源文件的内容并创建一个新文件,if 指定源文件内容,of 指定新文件名字,bs 和 count 指定新文件的大小 
mke2fs: 将新创建的 rootfs 格式化为 Linux 可识别的文件系统

清单 1. 创建空的根文件系统 

1 dd if=/dev/zero of=rootfs bs=65536 count=24000
2 mke2fs -F rootfs

步骤 2:挂载空的根文件系统,将 1.5G 的文件系统挂载在 mnt 目录下,然后通过 mnt 目录将内容写入根文件系统。 

清单 2. 挂载空的根文件系统

1 mkdir mnt
2 mount rootfs mnt -o loop

步骤 3:拷贝根文件目录的内容到文件系统 

清单 3. 拷贝根文件目录统

1 cp -rp yourRootDir mnt
2 umount mnt

拷贝完后根文件系统的内容,如图 2 所示: 

图 2. 根文件系统内容 

步骤 4:完成根文件系统的创建,这时的 rootfs 没有被压缩,接下来我们用工具将其压缩成 Squash 格式的文件系统

mksquashfs-4.1 是在安装 Squash 工具的过程中生成的命令,用于将一个文件夹下的内容打包并压缩成一个文件系统。其后第一个参数为文件夹,第二个参数为生成的文件系统。

清单 4. 创建根文件系统

1 mkdir squashfs-dir
2 mv rootfs squshfs-dir
3 mksquashfs-4.1 squashfs-dir squashRootfs

到这里我们就完成了 Squash 压缩文件系统的创建。接下来我们将讨论如何在 Linux 启动的过程中加载这个压缩文件系统。

加载压缩文件系统所使用的工具

在加载压缩文件系统之前,我们需要确定您的 Linux 内核支持这种文件系统。 Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机制下,用户可以很方便的根据自己的需要制定实现存储资源的管理策略。

确保在 initrd 中已经集成“device-mapper

加载 Squash 压缩文件系统

可读写文件系统原理如图 3 所示:

图 3. 可读写文件系统原理 

squashRootfs 里面存储了我们原始的根文件系统,我们在根文件系统中所有的写操作会直接写入 cowfile.out(cow:copy-on-write), 当我们读取根文件系统时,如果读取的内容没有变化,将直接从 squashRootfs 中读取,如果读取的内容被更新过,将从 cowfile.out 中读取。cowfile.out 文件的大小一般要比 squshRootfs 小,如果 cowfile.out 被写满,根文件系统的读写操作将会出错,因此有必要给 cowfile.out 设置一个合理的大小以防止被写满。

由于 rootfs 是被 initrd 加载的,因此我们需要在 initrd 里面加入装载 rootfs 的代码。initrd 整个的执行过程是调用 /sbin/init 这个脚本文件,因此我们在这个脚本的最后加入以下代码逻辑即可。

1. dm-mirror 为 device mapper 工作所需要的驱动

2. mknod: 创建一些设备文件,参数 1:设备文件名 参数 2:设备类型 参数 3:主设备号 参数 4:次设备号 
3. 将 squashRootfs 通过 loop 设备的形式挂载在目录 /realroot/mnt/ 下 
4. 将 /realroot/mnt/rootfs 设置为 loop 设备,并和 /dev/loop1 绑定 
5. "|"之前的部分是构建 dmsetup 的参数,其中 $(blockdev --getsize /dev/loop1) 表示创建镜像文件的大小,/dev/loop1 /dev/loop2 表示镜像文件是以 /realroot/mnt/rootfs 和 /realroot/mnt/cowfile.out 为蓝本进行创建的(在前面的操作中 loop1 和 loop2 分别进行了绑定操作)

清单 5. 构建可读写文件系统脚本

01 # 加载驱动 ,参见注释 1 modprobe dm-mirror
02  
03 # 创建设备 ,参见注释 2 mknod /dev/zero c 1 5
04 mknod /dev/loop0 b 7 0
05 mknod /dev/loop1 b 7 1
06 mknod /dev/loop2 b 7 2
07 mkdir /dev/cow
08 mknod /dev/cow/ctl b 241 255
09 mknod /dev/cow/0 b 241 0
10  
11 # 挂载 squash 根文件系统,挂载完后您可以在 /realroot/mnt/ 下找到 rootfs 文件 ,参见注释 3 mount /realroot/mnt/squashRootfs /realroot/mnt/ -o loop
12  
13 # 设置 rootfs 为 loop 设备 ,参见注释 4 losetup /dev/loop1 /realroot/mnt/rootfs
14  
15 # 创建 cowfile.out 并挂载为 loop 设备,我们将来的写操作都会写入 cowfile.out
16 dd if=/dev/zero of=/realroot/mnt/cowfile.out bs=2K count=137500
17 losetup /dev/loop2 /realroot/mnt/cowfile.out
18  
19 # 将 /realroot/mnt/rootfs 和 /realroot/mnt/cowfile.out 结合起来创建一个逻辑根文件设备,
20 # 设备文件为 /dev/mapper/root_fs,,参见注释 5 echo "0 $(blockdev --getsize /dev/loop1) snapshot /dev/loop1 /dev/loop2 p 64" |
21 dmsetup create root_fs
22  
23 # 将上面创建的逻辑根文件设备 /dev/mapper/root_fs 挂载就可以看到一个可读写的根文件系统
24 mount /dev/mapper/root_fs /realroot/mnt/Image
25  
26 # 切换到最终可读写的根文件系统
27 cd /realroot/mnt/Image
28 chroot ./sbin/init -i

加下来您就可以看到 rootfs 的所有内容,如图 4 所示: 
图 4. 被挂载的根文件系统内容

还可以在这个文件系统中进行写操作,如图 5 所示:

图 5. 根文件系统的写操作

最重要的是 rootfs 没有被拷贝到内存中。

结束语

由于篇幅的限制,本文只给出了基本的描述。希望有更一步了解的读者可以通过对以下 linux 命令的学习来深入了解。

构建 Squash 压缩文件系统构所使用的主要命令:

  • mksquashfs:创建 Squash 压缩文件系统

使用 Squash 压缩文件系统构所使用的主要命令:

    • mknod:创建 Squash 压缩文件系统
    • losetup:设置并控制 Loop 设备
    • chroot:改变根目录
    • dmsetup:低水平逻辑卷管理

基于 SquashFS 构建 Linux 可读写文件系统的更多相关文章

  1. 基于mini2440嵌入式Linux根文件系统制作(Initramfs和nfs两种跟文件系统)

    嵌入式系统由三部分构成: 1.bootoader---bootparameters---2.kernel 3.Root-filesysytem 一个内核可以挂载多个文件系统,但是有一个根文件系统所以叫 ...

  2. 构建Linux根文件系统(未完待续)

          所谓制作根文件系统, 就是创建各种目录, 并且在里面创建各种文件. 比如在/bin ./sbin 目录下存放各种可执行程序, 在/etc 目录下存放配置文件, 在/lib 目录下存放库文件 ...

  3. 18.14 构建Linux根文件系统

    18.14.1 Busybox1.7.0之init程序分析 1.读取配置文件 2.解析配置文件 3.执行用户程序(根据配置文件中指定的内容) 配置文件: 1.指定应用程序 2.何时执行 busybox ...

  4. 理解Linux虚拟文件系统VFS

    当前,除了linux标准的文件系统Ext2/Ext3/Ext4外,还有很多种文件系统,比如reiserfs, xfs, Windows的vfat NTFS,网络文件系统nfs 以及flash 文件系统 ...

  5. Linux及文件系统基本介绍

    Linux及文件系统基本介绍 1   互联网行业现状 在服务器端市场: 超级计算机 2014年11月的数据显示前500系统中的485个系统都在运行着 Linux 的发布系统,而仅仅只有一台运行着 Wi ...

  6. 构建LINUX下的入侵检测系统——LIDS 系统管理命令--vlock

    构建LINUX下的入侵检测系统——LIDS   系统管理命令--vlock http://blog.chinaunix.net/uid-306663-id-2440200.html LIDS官方网站: ...

  7. Linux根文件系统的制作

    转载:http://www.cnblogs.com/hnrainll/archive/2011/06/09/2076655.html 1. 根文件系统 文件系统是包括在一个磁盘(包括光盘.软盘.闪盘及 ...

  8. 阿里云oss挂载到linux本地文件系统

    对象存储 OSS 阿里云对象存储服务 (OSS) 是一种高度可伸缩且安全可靠的云对象存储服务,让您可以存储.备份和归档大量数据.阿里云 OSS 是一种简单易用的服务,让您每秒能处理数百万请求,它还支持 ...

  9. Linux虚拟文件系统

    从文件 I/O 看 Linux 的虚拟文件系统 1 引言 Linux 中允许众多不同的文件系统共存,如 ext2, ext3, vfat 等.通过使用同一套文件 I/O 系统 调用即可对 Linux ...

随机推荐

  1. 【数据结构与算法分析——C语言描述】第一章总结 引论

    这一章主要复习了一些数学知识,像指数.对数.模运算.级数公式:还有2种证明方法,归纳假设法和反证法.所幸以前学过,重新拾捡起来也比较轻松. 简要地复习了递归,提出了编写递归例程的四条基本法则: 基准情 ...

  2. Android调用系统自带的文件管理器进行文件选择并读取

    先调用: intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("*/*"); //设置类型,我这里是任意类 ...

  3. openstack-dbs

    真正的服务器派生出线程 和子进程处理多个连接当允许客户端加入聊天室,他发送的任何一条文本都将广播给聊天室中的每个用户,除非文本是服务器CLI当广播一条消息,消息前面将加上发送者的昵称 以尖括号括住昵称 ...

  4. perf

  5. 各种边框样式。。本以为border不是这么用的。

    关于文本框样式□ 文本框样式 □ 显示虚线边框的文本框(IE5.5才可看到效果)  <INPUT style="border-width: 1px,1px,1px,1px;border ...

  6. LightOJ 1341 - Aladdin and the Flying Carpet (唯一分解定理 + 素数筛选)

    http://lightoj.com/volume_showproblem.php?problem=1341 Aladdin and the Flying Carpet Time Limit:3000 ...

  7. hdu 1576 A/B (扩展欧几里德简单运用)

    http://acm.hdu.edu.cn/showproblem.php?pid=1576 A/B Time Limit: 1000/1000 MS (Java/Others) Memory Lim ...

  8. C# JackLib系列之如何获取地球上两经纬度坐标点间的距离

    获取地球上两经纬度坐标点间的距离,利用[大圆距离公式]   A diagram illustrating great-circle distance (drawn in red) between tw ...

  9. 使用HTML5的十大原因

    你难道还没有考虑使用HTML5? 当然我猜想你可能有自己的原因:它现在还没有被广泛的支持,在IE中不好使,或者你就是喜欢写比较严格的XHTML代码.HTML5是web开发世界的一次重大的改变,事实上不 ...

  10. setTimeout setInterval 区别 javascript线程解释

    原文:http://www.iamued.com/qianduan/1645.html 今天看到这篇文章,学到了不少东西 特此发出来 和大家分享 JavaScript的setTimeout与setIn ...