嵌入式 Linux根文件系统移植(二)——根文件系统简介
2019-07-12 18:35 发布
生成海报
嵌入式 Linux 根文件系统移植(二)——根文件系统简介 根文件系统是内核启动时挂载的第一个文件系统,内核代码映像 文件 保存在根文件系统中,而系统引导启动程序会在根文件系统挂载之后从中把一些基本的初始化脚本和服务等加载到内存中去运行。 一、嵌入式设备文件系统 在嵌入式 Linux 应用中,主要的存储设备为 RAM(DRAM, SDRAM) 和 ROM( 常采用 FLASH 存储器 ) ,常用的基于存储设备的文件系统类型包括: jffs2, yaffs, cramfs, romfs, ramdisk, ramfs/tmpfs 等。 Linux的文件系统结构如下: 1 、 基于FLASH 的文件系统 Flash(闪存 ) 作为嵌入式系统的主要存储媒介,有其自身的特性。 Flash 的写入操作只能把对应位置的 1 修改为 0 ,而不能把 0 修改为 1( 擦除 Flash 就是把对应存储块的内容恢复为 1) 。 一般情况下 ,向Flash 写入内容时,需要先擦除对应的存储区间,擦除是以块 (block) 为单位进行的。 闪存主要有 NOR 和 NAND 两种技术。 Flash 存储器的擦写次数是有限的, NAND 闪存还有特殊的硬件接口和读写时序。传统的文件系统如 ext2 、ext3 等用作 Flash 的文件系统会有诸多弊端。因此,必须针对 Flash 的硬件特性设计符合应用要求的文件系统 。 一块 Flash 芯片可以被划分为多个分区,各分区可以采用不同的文件系统;两块 Flash 芯片也可以合并为一个分区使用,采用一个文件系统。即文件系统是针对于存储器分区而言的,而非存储芯片。 在嵌入式 Linux 下, MTD(Memory Technology Device, 存储技术设备 ) 为底层硬件 ( 闪存 ) 和上层 ( 文件系统 ) 之间提供一个统一的抽象接口,即 Flash 的文件系统都是基于 MTD 驱动层的。 MTD 驱动程序的是专门针对各种非易失性存储器 ( 以闪存为主 ) 而设计的,因而它对 Flash 有更好的支持、管理和基于扇区的擦除、读 / 写操作接口。 常见闪存设备的文件系统如下: A、 jffs2 JFFS 文件系统最早是由瑞典 Axis Communications 公司基于 Linux2.0 的内核为嵌入式系统开发的文件系统。 JFFS2 是 RedHat 公司基于 JFFS 开发的闪存文件系 统,最初是针对 RedHat 公司的嵌入式产品 eCos 开发的嵌入式文件系统,所以 JFFS2 也可以用在 Linux, uCLinux 中。 Jffs2: 日志闪存文件系统版本 2 (Journalling Flash FileSystem v2) 主要用于 NOR 型闪存,基于 MTD 驱动层,特点是:可读写的、支持数据压缩的、基于哈希表的日志型文件系统,并提供了崩溃 / 掉电安全保护,提供 “ 写平衡 ” 支持等。缺点主要是当文件系统已满或接近满时,因为垃圾收集的关系而使 jffs2 的运行速度大大放慢。 jffs 不适合用于 NAND 闪存主要是因为 NAND 闪存的容量一般较大,这样导致 jffs 为维护日志节点所占用的内存空间迅速增大,另外, jffs 文件系统在挂载时需要扫描整个 FLASH 的内容,以找出所有的日志节点,建立文件结构,对于大容量的 NAND 闪存会耗费大量时间。 B、 yaffs y affs 是 Yet Another Flash File System 的缩写, yaffs/yaffs2是专为嵌入式系统使用 NAND 型闪存而设计的一种日志型文件系统。与 jffs2 相比,它减少了一些功能 ( 例如不支持数 据压缩 ) ,所以速度更快,挂载时间很短,对内存的占用较小。另外,它还是跨平台的文件系统,除了 Linux 和 eCos ,还支持 WinCE, pSOS 和 ThreadX 等。 yaffs/yaffs2 自带 NAND 芯片的驱动,并且为嵌入式系统提供了直接访问文件系统的 API ,用户可以不使用 Linux 中的 MTD 与 VFS ,直接对文件系统操作。当然, yaffs 也可与 MTD 驱动程序配合使用。 yaffs 与 yaffs2 的主要区别在于,前者仅支持小页 (512 Bytes) NAND 闪存,后者则可支持大页 (2KB) NAND 闪存。同时, yaffs2 在内存空间占用、垃圾回收速度、读 / 写速度等方面均有大幅提升。 C、 Cramfs Cramfs 是 Compressed ROM File System 的缩写,是由 Linux的创始人 Linus Torvalds 参与开发的一种只读的压缩文件系统 , 基于 MTD 驱动程序。 在 cramfs 文件系统中,每一页 (4KB) 被单独压缩,可以随机页访问,其压缩比高达 2:1, 为嵌入式系统节省大量的 Flash 存储空间,使系统可通过更低容量的 FLASH 存储相同的文件,从而降低系统成本。 Cramfs文件系统以压缩方式存储,在运行时解压缩,所以不支持应用程序以 XIP 方式运行,所有的应用程序要求被拷到 RAM 里去运行,但这并不代表比 Ramfs 需求的 RAM 空间要大一点,因为 Cramfs 是采用分页压缩的方式存放档案,在读取档案时,不会一下子就耗用过多的内存空间,只针对目前实际读 取的部分分配内存,尚没有读取的部分不分配内存空间,当我们读取的档案不在内存时, Cramfs 文件系统自动计算压缩后的资料所存的位置,再即时解压缩到 RAM 中。 另外 ,Cramfs 的速度快,效率高,其只读的特点有利于保护文件系统免受破坏,提高了系统的可靠性 , 只读属性同时又是它的一大缺陷,使得用户无法对其内容对进扩充 。 Cramfs映像通常是放在 Flash 中,也能放在别的文件系统里,使用 loopback 设备可以把它安装别的文件系统里。 D、 Romfs 传统型的 Romfs 文件系统是一种简单的、紧凑的、只读的文件系统,不支持动态擦写保存,按顺序存放数据,因而支持应用程序以 XIP(eXecute In Place ,片内运行 ) 方式运行,在系统运行时,节省 RAM 空间。 uClinux 系统通常采用 Romfs 文件系统。 其他文件系统 :fat/fat32 也可用于实际嵌入式系统的扩展存储器 ( 例如 PDA, Smartphone, 数码相机等的 SD 卡 ) ,这主要是为了更好的与最流行的 Windows 桌面操作系统相兼容。 ext2 也可以作为嵌入式 Linux 的文件系统,不过将它用于 FLASH 闪存会有诸多弊端。 2 、 基于RAM 的文件系统 A、 Ramdisk Ramdisk是将一部分固定大小的内存当作分区来使用。它并非一个实际的文件系统,而是一种将实际的文件系统装入内存的机制,并且可以作为根 文件系统。将一些经常被访问而又不会更改的文件 ( 如只读的根文件系统 ) 通过 Ramdisk 放在内存中,可以明显地提高系统的性能。 在Linux 的启动阶段, initrd 提供了一套机制,可以将内核映像和根文件系统一起载入内存。 B、 ramfs/tmpfs Ramfs是 Linus Torvalds 开发的一种基于内存的文件系统,工作于虚拟文件系统 (VFS) 层,不能格式化,可以创建多个,在创建时可以指定其最大能使用的内存大小。 ( 实际上, VFS 本质上可看成一种内存文件系统,它统一了文件在内核中的表示方式,并对磁盘文件系统进行缓冲。 ) Ramfs/tmpfs文件系统把所有的文件都放在 RAM 中,所以读 / 写操作发生在 RAM 中,可以用 ramfs/tmpfs 来存储一些临时性或经常要修改的数据,例如 /tmp 和 /var 目录,这样既避免了对 Flash 存储器的读写损耗,也提高了数据读写速度。 Ramfs/tmpfs相对于传统的 Ramdisk 的不同之处主要在于:不能格式化,文件系统大小可随所含文件内容大小变化。 Tmpfs的一个缺点是当系统重新引导时会丢失所有数据。 3 、 网络文件系统NFS (Network File System) NFS 是由 Sun 开发并发展起来的一项在不同机器、不同操作系统之间通过网络共享文件的技术。在嵌入式 Linux 系统的开发调试阶段,可以利用该技术在主机上建立基于 NFS 的根文件系统,挂载到嵌入式设备,可以很方便地修改根文件系统的内容。 嵌入式linux设备可以按照 存储设备 选择合适的根文件系统类型。 二、 文件系统目录标准 Filesystem Hierarchy Standard(文件系统目录标准) 是 多数 Linux 版本采用 的 文件 系统 组织形式 。 FHS采用树形结构组织文件。 FHS 定义了系统中每个区域的用途、所需要的最小构成的文件和目录 。 目录名 存放的内容 /bin 必备的用户命令,例如ls 、 cp 等 /sbin 必备的系统管理员命令,例如ifconfig 、 reboot 等 /dev 设备文件,例如mtdblock0 、 tty1 等 /etc 系统配置文件,包括启动文件,例如inittab 等 /lib 必要的链接库,例如C 链接库、内核模块 /home 普通用户主目录 /root root用户主目录 /usr/bin 非必备的用户程序,例如find 、 du 等 /usr/sbin 非必备的管理员程序,例如chroot 、 inetd 等 /usr/lib 库文件 /var 守护程序和工具程序所存放的可变,例如日志文件 /proc 用来提供内核与进程信息的虚拟文件系统,由内核自动生成目录下的内容 /sys 用来提供内核与设备信息的虚拟文件系统,由内核自动生成目录下的内容 /mnt 文件系统挂接点,用于临时安装文件系统 /tmp 临时性的文件,重启后将自动清除 三、根文件系统的挂载分析 Kernel启动过程中, start_kernel 函数调用了 vfs_caches_init 函数, mnt_init 函数中的 init_rootfs() 函数用于注册根文件系统, init_mount_tree() 函数用于挂载根文件系统。 void __init vfs_caches_init(unsigned long mempages){unsigned long reserve; reserve = min((mempages - nr_free_pages()) * 3/2, mempages - 1);mempages -= reserve;names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);dcache_init();inode_init();files_init(mempages);mnt_init();bdev_cache_init();chrdev_init();}void __init mnt_init(void){unsigned u;int err;init_rwsem(&namespace_sem);mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount),0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);mount_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC);if (!mount_hashtable)panic("Failed to allocate mount hash table
");printk("Mount-cache hash table entries: %lu
", HASH_SIZE);for (u = 0; u < HASH_SIZE; u++)INIT_LIST_HEAD(&mount_hashtable[u]);err = sysfs_init();if (err)printk(KERN_WARNING "%s: sysfs_init error: %d
",__func__, err);fs_kobj = kobject_create_and_add("fs", NULL);if (!fs_kobj)printk(KERN_WARNING "%s: kobj create error
", __func__);init_rootfs();//根文件系统注册init_mount_tree();//根文件系统挂载}static void __init init_mount_tree(void){struct vfsmount *mnt;struct mnt_namespace *ns;struct path root;mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);if (IS_ERR(mnt))panic("Can't create rootfs");ns = create_mnt_ns(mnt);if (IS_ERR(ns))panic("Can't allocate initial namespace");init_task.nsproxy->mnt_ns = ns;get_mnt_ns(ns);root.mnt = ns->root;root.dentry = ns->root->mnt_root;set_fs_pwd(current->fs, &root);set_fs_root(current->fs, &root);}struct vfsmount *do_kern_mount(const char *fstype, int flags, const char *name, void *data){struct file_system_type *type = get_fs_type(fstype);struct vfsmount *mnt;if (!type)return ERR_PTR(-ENODEV);mnt = vfs_kern_mount(type, flags, name, data);if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) && !mnt->mnt_sb->s_subtype)mnt = fs_set_subtype(mnt, fstype);put_filesystem(type);return mnt;} struct vfsmount *vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data){struct vfsmount *mnt;char *secdata = NULL;int error;if (!type)return ERR_PTR(-ENODEV);error = -ENOMEM;mnt = alloc_vfsmnt(name);if (!mnt)goto out;if (flags & MS_KERNMOUNT)mnt->mnt_flags = MNT_INTERNAL;if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {secdata = alloc_secdata();if (!secdata)goto out_mnt; error = security_sb_copy_data(data, secdata);if (error)goto out_free_secdata;}error = type->get_sb(type, flags, name, data, mnt);if (error < 0)goto out_free_secdata;BUG_ON(!mnt->mnt_sb);WARN_ON(!mnt->mnt_sb->s_bdi);mnt->mnt_sb->s_flags |= MS_BORN;error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata);if (error)goto out_sb;WARN((mnt->mnt_sb->s_maxbytes < 0), "%s set sb->s_maxbytes to ""negative value (%lld)
", type->name, mnt->mnt_sb->s_maxbytes);mnt->mnt_mountpoint = mnt->mnt_root;mnt->mnt_parent = mnt;up_write(&mnt->mnt_sb->s_umount);free_secdata(secdata);return mnt;out_sb:dput(mnt->mnt_root);deactivate_locked_super(mnt->mnt_sb);out_free_secdata:free_secdata(secdata);out_mnt:free_vfsmnt(mnt);out:return ERR_PTR(error);}
四、NFS 方式挂载根文件系统 要使用NFS 方式挂载根文件系统,则 kernel 必须支持 NFS 方式。编译配置 kernel 时需要 make menuconfig 配置部分如下: 1、配置网络部分 Networking support Networking options TCP/IP networking IP: kernel level autoconfiguration [*] IP: DHCP support [*] IP: BOOTP support 2、 NFS方式配置 File systems ---> Network File Systems ---> <*> NFS client support [*] NFS client support for NFS version 3 [*] NFS client support for the NFSv3 ACL protocol extension [*] NFS client support for NFS version 4 (EXPERIMENTAL) [*] NFS client support for NFSv4.1 (DEVELOPER ONLY) [*] Root file system on NFS 3、 kernel 启动参数的设置 在uboot 中设置 kernel 的启动参数: setenv bootargs root=/dev/nfs nfsroot=192.168.6.200:/home/nfs/rootfs ip=192.168.6.210:192.168.6.200:192.168.6.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC2,115200 本文出自 “
生命不息,奋斗不止 ” 博客,转载请与作者联系!
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮