基于 SquashFS 构建 Linux 可读写文件系统
转载: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 的可读写文件系统构建。
Squash 压缩文件系统的创建
步骤 1:创建空的根文件系统,文件系统的大小为 65536 × 24000/1024/1024=1.5G。接下来我们会在这个空的根文件系统中存放文件。
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 格式的文件系统
清单 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 这个脚本文件,因此我们在这个脚本的最后加入以下代码逻辑即可。
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 可读写文件系统的更多相关文章
- 基于mini2440嵌入式Linux根文件系统制作(Initramfs和nfs两种跟文件系统)
嵌入式系统由三部分构成: 1.bootoader---bootparameters---2.kernel 3.Root-filesysytem 一个内核可以挂载多个文件系统,但是有一个根文件系统所以叫 ...
- 构建Linux根文件系统(未完待续)
所谓制作根文件系统, 就是创建各种目录, 并且在里面创建各种文件. 比如在/bin ./sbin 目录下存放各种可执行程序, 在/etc 目录下存放配置文件, 在/lib 目录下存放库文件 ...
- 18.14 构建Linux根文件系统
18.14.1 Busybox1.7.0之init程序分析 1.读取配置文件 2.解析配置文件 3.执行用户程序(根据配置文件中指定的内容) 配置文件: 1.指定应用程序 2.何时执行 busybox ...
- 理解Linux虚拟文件系统VFS
当前,除了linux标准的文件系统Ext2/Ext3/Ext4外,还有很多种文件系统,比如reiserfs, xfs, Windows的vfat NTFS,网络文件系统nfs 以及flash 文件系统 ...
- Linux及文件系统基本介绍
Linux及文件系统基本介绍 1 互联网行业现状 在服务器端市场: 超级计算机 2014年11月的数据显示前500系统中的485个系统都在运行着 Linux 的发布系统,而仅仅只有一台运行着 Wi ...
- 构建LINUX下的入侵检测系统——LIDS 系统管理命令--vlock
构建LINUX下的入侵检测系统——LIDS 系统管理命令--vlock http://blog.chinaunix.net/uid-306663-id-2440200.html LIDS官方网站: ...
- Linux根文件系统的制作
转载:http://www.cnblogs.com/hnrainll/archive/2011/06/09/2076655.html 1. 根文件系统 文件系统是包括在一个磁盘(包括光盘.软盘.闪盘及 ...
- 阿里云oss挂载到linux本地文件系统
对象存储 OSS 阿里云对象存储服务 (OSS) 是一种高度可伸缩且安全可靠的云对象存储服务,让您可以存储.备份和归档大量数据.阿里云 OSS 是一种简单易用的服务,让您每秒能处理数百万请求,它还支持 ...
- Linux虚拟文件系统
从文件 I/O 看 Linux 的虚拟文件系统 1 引言 Linux 中允许众多不同的文件系统共存,如 ext2, ext3, vfat 等.通过使用同一套文件 I/O 系统 调用即可对 Linux ...
随机推荐
- 【数据结构与算法分析——C语言描述】第一章总结 引论
这一章主要复习了一些数学知识,像指数.对数.模运算.级数公式:还有2种证明方法,归纳假设法和反证法.所幸以前学过,重新拾捡起来也比较轻松. 简要地复习了递归,提出了编写递归例程的四条基本法则: 基准情 ...
- Android调用系统自带的文件管理器进行文件选择并读取
先调用: intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("*/*"); //设置类型,我这里是任意类 ...
- openstack-dbs
真正的服务器派生出线程 和子进程处理多个连接当允许客户端加入聊天室,他发送的任何一条文本都将广播给聊天室中的每个用户,除非文本是服务器CLI当广播一条消息,消息前面将加上发送者的昵称 以尖括号括住昵称 ...
- perf
- 各种边框样式。。本以为border不是这么用的。
关于文本框样式□ 文本框样式 □ 显示虚线边框的文本框(IE5.5才可看到效果) <INPUT style="border-width: 1px,1px,1px,1px;border ...
- LightOJ 1341 - Aladdin and the Flying Carpet (唯一分解定理 + 素数筛选)
http://lightoj.com/volume_showproblem.php?problem=1341 Aladdin and the Flying Carpet Time Limit:3000 ...
- hdu 1576 A/B (扩展欧几里德简单运用)
http://acm.hdu.edu.cn/showproblem.php?pid=1576 A/B Time Limit: 1000/1000 MS (Java/Others) Memory Lim ...
- C# JackLib系列之如何获取地球上两经纬度坐标点间的距离
获取地球上两经纬度坐标点间的距离,利用[大圆距离公式] A diagram illustrating great-circle distance (drawn in red) between tw ...
- 使用HTML5的十大原因
你难道还没有考虑使用HTML5? 当然我猜想你可能有自己的原因:它现在还没有被广泛的支持,在IE中不好使,或者你就是喜欢写比较严格的XHTML代码.HTML5是web开发世界的一次重大的改变,事实上不 ...
- setTimeout setInterval 区别 javascript线程解释
原文:http://www.iamued.com/qianduan/1645.html 今天看到这篇文章,学到了不少东西 特此发出来 和大家分享 JavaScript的setTimeout与setIn ...