overlayfs是一种叠加文件系统,在openwrt和安卓系统中都有很广泛的应用,overlayfs通常用于将只读根文件系统(rootfs)和可写文件系统(jffs2)进行叠加后形成一个新的文件系统,这个新的文件系统“看起来”是可读写的,这种做法的好处是:
- 对这个新文件系统的修改(删除也属于修改)都只保存在可写文件系统中,只读根文件系统不受任何影响
- 将可写文件系统格式化后,可以将整个文件系统恢复到初始状态(相当于只有只读根文件系统的状态)
- 减少flash擦写次数,延长设备使用寿命
下面就开始介绍openwrt系统中的overlayfs是如何挂载的,挂载过程可以分为2个部分:
- 只读根文件系统(rootfs)挂载过程
- overlayfs 挂载过程(包括可写文件系统(rootfs_data)挂载过程)
只读根文件系统(rootfs)挂载过程
kernel的启动流程大致如下:

prepare_namespace
prepare_namespace负责根文件系统的挂载
void __init prepare_namespace(void)
{
int is_floppy;
if (root_delay) {
printk(KERN_INFO "Waiting %d sec before mounting root device...\n",
root_delay);
ssleep(root_delay);
}
wait_for_device_probe(); // 等待所有的设备初始化完成
md_run_setup();
if (saved_root_name[0]) {
root_device_name = saved_root_name;
if (!strncmp(root_device_name, "mtd", 3) ||
!strncmp(root_device_name, "ubi", 3)) {
mount_block_root(root_device_name, root_mountflags);
goto out;
}
ROOT_DEV = name_to_dev_t(root_device_name);
if (strncmp(root_device_name, "/dev/", 5) == 0)
root_device_name += 5;
}
if (initrd_load())
goto out;
/* wait for any asynchronous scanning to complete */
if ((ROOT_DEV == 0) && root_wait) {
printk(KERN_INFO "Waiting for root device %s...\n",
saved_root_name);
while (driver_probe_done() != 0 ||
(ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
msleep(5);
async_synchronize_full();
}
is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
if (is_floppy && rd_doload && rd_load_disk(0))
ROOT_DEV = Root_RAM0;
mount_root();
out:
devtmpfs_mount("dev");
ksys_mount(".", "/", NULL, MS_MOVE, NULL);
ksys_chroot(".");
}
- root_delay:如果cmdline中有“rootdelay=xxx”,则调用ssleep延迟xxx秒,例如“rootdelay=10”,则延时10s
- wait_for_device_probe()是等待所有的设备probe完成,
- saved_root_name:如果cmdline中有“root=xxx”,则saved_root_name=xxx。在我当前的系统中
root=/dev/mmcblk1p65,所以saved_root_name=/dev/mmcblk1p65 - mount_block_root: 如果
root_device_name前三个字符是“ubi”或者“mtd”,则调用mount_block_root进行挂载根文件系统,这里主要是针对ubifs这一种文件系统进行特殊处理,因为ubifs根文件系统对应cmdline的参数一般是:ubi.mtd=1 root=ubi0:rootfs rootfstype=ubifs,而其他文件系统一般是root=/dev/xxx.saved_root_name会被赋值给root_device_name。 - ROOT_DEV:root_device_name设备对应的设备号(/dev/mmcblk1p65设备对应主设备号是259,次设备号是0)
- while (driver_probe_done() != 0): 等待所有
saved_root_name设备probe完成(dev/xxx节点被创建) - mount_root:开始挂载根文件系统
mount_root(内核层)
void __init mount_root(void)
{
#ifdef CONFIG_ROOT_NFS
if (ROOT_DEV == Root_NFS) {
if (!mount_nfs_root())
printk(KERN_ERR "VFS: Unable to mount root fs via NFS.\n");
return;
}
#endif
#ifdef CONFIG_CIFS_ROOT
if (ROOT_DEV == Root_CIFS) {
if (!mount_cifs_root())
printk(KERN_ERR "VFS: Unable to mount root fs via SMB.\n");
return;
}
#endif
#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV
if (!mount_ubi_rootfs())
return;
#endif
#ifdef CONFIG_BLOCK
{
int err = create_dev("/dev/root", ROOT_DEV);
if (err < 0)
pr_emerg("Failed to create /dev/root: %d\n", err);
mount_block_root("/dev/root", root_mountflags);
}
#endif
}
- CONFIG_MTD_ROOTFS_ROOT_DEV 的作用是告诉 Linux 内核在引导过程中从哪个 MTD 设备加载根文件系统,一般支持NANDFlash的设备都会开启这个选项
- mount_ubi_rootfs() 尝试挂载
ubifs,如果挂载成功,则不再继续后续的挂载步骤。 - CONFIG_BLOCK是开启块设备子系统,对于绝大多数文件系统(EXT4、yaffs2、XFS、FAT等)都需要块设备子系统的支持。
- create_dev(“/dev/root”, ROOT_DEV) 创建了一个设备节点
/dev/root,/dev/root是根文件系统设备的抽象,因为它的设备号也是ROOT_DEV,前面有提到ROOT_DEV就是实际的根文件系统设备节点。这样做的好处是不用关心实际的根文件系统设备是什么,直接对/dev/root进行mount就可以实现根文件系统的挂载。 - mount_block_root(“/dev/root”, root_mountflags) 挂载
/dev/root
void __init mount_block_root(char *name, int flags)
{
struct page *page = alloc_page(GFP_KERNEL);
char *fs_names = page_address(page);
char *p;
<

本文介绍了overlayfs在openwrt系统中的挂载过程。overlayfs是叠加文件系统,可将只读和可写文件系统叠加,修改只存于可写系统。挂载分两部分:只读根文件系统挂载和overlayfs挂载。前者涉及内核层操作,后者需fstools工具,在应用层完成。
最低0.47元/天 解锁文章
2076

被折叠的 条评论
为什么被折叠?



