基于 SquashFS 构建 Linux 可读写文件系统
嵌入式 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。接下来我们会在这个空的根文件系统中存放文件。
注释:
dd: 读取源文件的内容并创建一个新文件,if 指定源文件内容,of 指定新文件名字,bs 和 count 指定新文件的大小
mke2fs: 将新创建的 rootfs 格式化为 Linux 可识别的文件系统
清单 1. 创建空的根文件系统
dd if=/dev/zero of=rootfs bs=65536 count=24000 mke2fs -F rootfs
步骤 2:挂载空的根文件系统,将 1.5G 的文件系统挂载在 mnt 目录下,然后通过 mnt 目录将内容写入根文件系统。
清单 2. 挂载空的根文件系统
mkdir mnt
mount rootfs mnt -o loop
步骤 3:拷贝根文件目录的内容到文件系统
清单 3. 拷贝根文件目录统
cp -rp yourRootDir mnt
umount mnt
拷贝完后根文件系统的内容,如图 2 所示:
图 2. 根文件系统内容
步骤 4:完成根文件系统的创建,这时的 rootfs 没有被压缩,接下来我们用工具将其压缩成 Squash 格式的文件系统
注释:
mksquashfs-4.1 是在安装 Squash 工具的过程中生成的命令,用于将一个文件夹下的内容打包并压缩成一个文件系统。其后第一个参数为文件夹,第二个参数为生成的文件系统。
清单 4. 创建根文件系统
mkdir squashfs-dir
mv rootfs squshfs-dir
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 这个脚本文件,因此我们在这个脚本的最后加入以下代码逻辑即可。
清单 5. 构建可读写文件系统脚本
# 加载驱动 ,参见注释 1
modprobe dm-mirror
# 创建设备 ,参见注释 2
mknod /dev/zero c 1 5
mknod /dev/loop0 b 7 0
mknod /dev/loop1 b 7 1
mknod /dev/loop2 b 7 2
mkdir /dev/cow
mknod /dev/cow/ctl b 241 255
mknod /dev/cow/0 b 241 0
# 挂载 squash 根文件系统,挂载完后您可以在 /realroot/mnt/ 下找到 rootfs 文件 ,参见注释 3
mount /realroot/mnt/squashRootfs /realroot/mnt/ -o loop
# 设置 rootfs 为 loop 设备 ,参见注释 4
losetup /dev/loop1 /realroot/mnt/rootfs
# 创建 cowfile.out 并挂载为 loop 设备,我们将来的写操作都会写入 cowfile.out
dd if=/dev/zero of=/realroot/mnt/cowfile.out bs=2K count=137500
losetup /dev/loop2 /realroot/mnt/cowfile.out
# 将 /realroot/mnt/rootfs 和 /realroot/mnt/cowfile.out 结合起来创建一个逻辑根文件设备,
# 设备文件为 /dev/mapper/root_fs,,参见注释 5
echo "0 $(blockdev --getsize /dev/loop1) snapshot /dev/loop1 /dev/loop2 p 64" |
dmsetup create root_fs
# 将上面创建的逻辑根文件设备 /dev/mapper/root_fs 挂载就可以看到一个可读写的根文件系统
mount /dev/mapper/root_fs /realroot/mnt/Image
# 切换到最终可读写的根文件系统
cd /realroot/mnt/Image
chroot ./sbin/init -i
注释:
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 分别进行了绑定操作)
加下来您就可以看到 rootfs 的所有内容,如图 4 所示:
图 4. 被挂载的根文件系统内容
还可以在这个文件系统中进行写操作,如图 5 所示:
图 5. 根文件系统的写操作
最重要的是 rootfs 没有被拷贝到内存中。
结束语
由于篇幅的限制,本文只给出了基本的描述。希望有更一步了解的读者可以通过对以下 linux 命令的学习来深入了解。
构建 Squash 压缩文件系统构所使用的主要命令:
- mksquashfs:创建 Squash 压缩文件系统
使用 Squash 压缩文件系统构所使用的主要命令:
- mknod:创建 Squash 压缩文件系统
- losetup:设置并控制 Loop 设备
- chroot:改变根目录
- dmsetup:低水平逻辑卷管理
https://www.ibm.com/developerworks/cn/linux/1306_qinzl_squashfs/index.html
本文地址:https://blog.csdn.net/wzq2009/article/details/109643346
上一篇: 树莓派系列-1-初识树莓派,把它装起来
下一篇: 什么是5G前传、中传、回传?