精华内容
下载资源
问答
  • 什么是根文件系统

    2018-04-07 16:39:58
    文章为作者原创,转载请附明原地址:https://mp.csdn.net/postedit/79842551一、根文件系统简介根文件系统首先一种文件系统,该文件系统不仅具有普通文件系统的存储数据文件的功能,但是相对于普通的文件系统而言...
    文章为作者原创,转载请附明原地址:https://mp.csdn.net/postedit/79842551
    一、根文件系统简介
    根文件系统首先是一种文件系统,该文件系统不仅具有普通文件系统的存储数据文件的功能,但是相对于普通的文件系统而言它还是内核启动时所挂载(mount)的第一个文件系统,系统引导启动程序会在根文件系统挂载之后从中把一些初始化脚本(如inittab、rcS)和服务加载到内存中去运行。我们要明白文件系统和内核是完全独立的两个部分。在嵌入式中移植的内核下载到开发板上,是没有办法真正的启动Linux操作系统的,会出现无法加载文件系统的错误。

    根文件系统之所以在前面加一个”根“,说明它是加载其它文件系统的”根“,既然是根的话,那么如果没有这个根,其它的文件系统也就没有办法进行加载的。它包括了Linux启动时所必须的目录和关键性的文件,例如Linux启动时都需要有init目录下的相关文件,在 Linux挂载分区时Linux一定会找/etc/fstab这个挂载配置文件等,根文件系统中还包括了应用程序(如ls、mkdir、rm、ifconfig等命令)和 GNU C 库(glibc、eglibc或uclibc)等。任何包括这些Linux 系统启动所必须的文件都可以成为根文件系统。Linux启动时,第一个必须挂载的是根文件系统;若系统不能从指定设备上挂载根文件系统,则系统会出错而退出启动。成功之后可以自动或手动挂载其他的文件系统。因此,一个系统中可以同时存在不同的文件系统。

    在 Linux 中将一个文件系统与一个存储设备关联起来的过程称为挂载(mount)。使用 mount 命令将一个文件系统附着到当前文件系统层次结构中(根)。在执行挂载时,要提供文件系统类型、文件系统和一个挂载点。根文件系统被挂载到根目录下“/”上后,在根目录下就有根文件系统的各个目录,文件:/bin /sbin /mnt等,再将其他分区挂接到/mnt目录上,/mnt目录下就有这个分区的各个目录文件。

    Linux操作系统可以支持多种多样不同的文件系统,如windows下的FAT32、NTFS,Linux服务器使用的ext2、ext3、ext4、btrfs、xfs、reiser4、ufs、hfs(苹果系统),Linux操作系统自身使用的伪文件系统如swap、proc、sysfs、tmpfs等,也有一些设备的文件系统如iso9660(光盘)、nfs(网络文件系统)等,当然还包括我们嵌入式设备上用的不同的文件系统如:initramfs(在内存中运行的文件系统)、jffs2(一种基于Norflash的文件系统,也支持小页的nandflash)、ubifs(目前Android手机上的Nandflash普遍使用的文件系统)、yaffs2(早期专门用来支持大页Nandflash的文件系统,具备可移植性可在单片机裸机情况下使用)、cramfs(一种基于内存的只读的文件系统)、squashfs(在openwrt路由器上广泛使用的俄文件系统)、romfs(一种只读、支持片上执行XIP的文件系统,在uClinux中广泛使用)等。下面是几种常用的嵌入式根文件系统的对比:


    initramfs 该文件系统直接将根文件系统直接打包进Linux内核里(只有这种文件系统具有该功能),这样内核和根文件系统绑定在一块成为一个单独的文件,他在Linux系统启动后加载到内存中运行,所以速度快,但浪费内存,系统升级的时候内核和根文件系统一起升级比较方便。但因为他是基于内存的文件系统,所以系统掉电后针对根文件系统下所有文件的修改掉电都会丢失,如果要升级根文件系统只有重新编译、烧录Linux内核;

    jffs2 专门针对Norflash设计的文件系统,小页的Nandflash也可以使用,只是效率不高;因为根文件系统存放在Flash上,所以根文件系统路径下的文件修改后掉电仍然存在;

     yaffs2 早期专门针对大页Nandflash设计的文件系统,他的源码独立于Linux内核维护着,所以可以在单片机等裸机环境下使用(只有该文件系统可以),linux内核想用它必须要打上补丁;在分区较大时,nandflash的挂载时间较长,现在逐渐被ubifs广泛替代;

     ubifs 专门针对Nandflash设计的一种文件系统,他在内核的MTD一层上面又建立了UBI一层,挂载速度、磨损均衡、读写速度非常快,目前nandflash上应用得最广的一种根文件系统;

    Linux操作系统之所以能够支持这么多种不同的文件系统,主要是通过叫做VFS的中间层对这些文件系统提供了完美的支持。对于用户来说,这些文件系统几乎是透明的,在大部分情况下,用户通过libc和kernel的VFS交互,不需要关心底层文件系统的具体实现,但是有时应用程序也需要考虑底层文件系统限制(比如fat vfat不支持链接,比如各个文件系统支持最大文件限制不同)。VFS主要有以下特性:

    1. 向上,对应用层提供一个标准的文件操作接口,如open()、read()、write()、ioct()、close()等;

    2. 对下,对所有文件系统提供一个统一的标准接口,以便其他操作系统的文件系统可以方便的移植到Linux上;

    3. VFS内部则通过一系列高效的管理机制,比如inode cache, dentry cache 以及文件系统的预读等技术,使得底层文件系统不需沉溺到复杂的内核操作,即可获得高性能;
     4. 此外VFS把一些复杂的操作尽量抽象到VFS内部,使得底层文件系统实现更简单。
    下图是Linux内核里文件系统的分层结构图:


    根文件目录:
    1./bin目录
    该目录下的命令可以被root与一般账号所使用,由于这些命令在挂接其它文件系统之前就可以使用,所以/bin目录必须和根文件系统在同一个分区中。
    /bin目录下常用的命令有:cat、chgrp、chmod、cp、ls、sh、kill、mount、umount、mkdir、[、test等。其中“[”命令就是test命令,我们在利用Busybox制作根文件系统时,在生成的bin目录下,可以看到一些可执行的文件,也就是可用的一些命令。
    2./sbin 目录
    该目录下存放系统命令,即只有系统管理员(俗称最高权限的root)能够使用的命令,系统命令还可以存放在/usr/sbin,/usr/local/sbin目录下,/sbin目录中存放的是基本的系统命令,它们用于启动系统和修复系统等,与/bin目录相似,在挂接其他文件系统之前就可以使用/sbin,所以/sbin目录必须和根文件系统在同一个分区中。
    /sbin目录下常用的命令有:shutdown、reboot、fdisk、fsck、init等,本地用户自己安装的系统命令放在/usr/local/sbin目录下。
    3、/dev目录
    该目录下存放的是设备与设备接口的文件,设备文件是Linux中特有的文件类型,在Linux系统下,以文件的方式访问各种设备,即通过读写某个设备文件操作某个具体硬件。比如通过"dev/ttySAC0"文件可以操作串口0,通过"/dev/mtdblock1"可以访问MTD设备的第2个分区。比较重要的文件有/dev/null, /dev/zero, /dev/tty, /dev/lp*等。
    4./etc目录
    该目录下存放着系统主要的配置文件,例如人员的账号密码文件、各种服务的其实文件等。一般来说,此目录的各文件属性是可以让一般用户查阅的,但是只有root有权限修改。对于PC上的Linux系统,/etc目录下的文件和目录非常多,这些目录文件是可选的,它们依赖于系统中所拥有的应用程序,依赖于这些程序是否需要配置文件。在嵌入式系统中,这些内容可以大为精减。
    5./lib目录
    该目录下存放共享库和可加载(驱动程序),共享库用于启动系统。运行根文件系统中的可执行程序,比如:/bin /sbin 目录下的程序。
    6./home目录
    系统默认的用户文件夹,它是可选的,对于每个普通用户,在/home目录下都有一个以用户名命名的子目录,里面存放用户相关的配置文件。
    7./root目录
    系统管理员(root)的主文件夹,即是根用户的目录,与此对应,普通用户的目录是/home下的某个子目录。
    8./usr目录
    /usr目录的内容可以存在另一个分区中,在系统启动后再挂接到根文件系统中的/usr目录下。里面存放的是共享、只读的程序和数据,这表明/usr目录下的内容可以在多个主机间共享,这些主要也符合FHS标准的。/usr中的文件应该是只读的,其他主机相关的,可变的文件应该保存在其他目录下,比如/var。/usr目录在嵌入式中可以精减。
    9./var目录
    与/usr目录相反,/var目录中存放可变的数据,比如spool目录(mail,news),log文件,临时文件。
    10./proc目录
    这是一个空目录,常作为proc文件系统的挂接点,proc文件系统是个虚拟的文件系统,它没有实际的存储设备,里面的目录,文件都是由内核
    临时生成的,用来表示系统的运行状态,也可以操作其中的文件控制系统。
    11./mnt目录
    用于临时挂载某个文件系统的挂接点,通常是空目录,也可以在里面创建一引起空的子目录,比如/mnt/cdram /mnt/hda1 。用来临时挂载光盘、移动存储设备等。
    12. /tmp目录
    用于存放临时文件,通常是空目录,一些需要生成临时文件的程序用到的/tmp目录下,所以/tmp目录必须存在并可以访问。
    那我们利用Busybox制作根文件系统就是创建这上面的这些目录,和这些目录下面的各种文件。
    展开全文
  • 什么是根文件系统

    千次阅读 2018-07-30 08:47:58
    相信做过linux移植的朋友们一定对根文件系统这个名词十分熟悉,在移植的过程中,bootloader,kernel和根文件系统是必须要移植的,bootloader和kernel都比较直观好懂,而我自己花了很多时间才能理解什么是根文件系统...

    相信做过linux移植的朋友们一定对根文件系统这个名词十分熟悉,在移植的过程中,bootloader,kernel和根文件系统是必须要移植的,bootloader和kernel都比较直观好懂,而我自己花了很多时间才能理解什么是根文件系统,所以我就来说说我对根文件系统的理解。

    首先说说什么文件系统。用我的话来讲,文件系统就是用来管理块设备的。做过单片机的朋友们都知道当我们要读取块设备时,要知道数据存取在块设备的哪一个扇区号,然后一块块的去读取数据。但是我们在玩电脑的时候,读取硬盘时却是通过路径去访问文件的,这是怎么做到的呢?没错,这就是文件系统的功能,有了它,我们读取块设备时可以通过文件路径去读取,这样子访问块设备就变得十分方便了。

    根文件系统是特殊的文件系统。这句话怎么理解?它具备了文件系统的功能,就是管理块设备,同时也有着文件系统没有的功能。除了管理块设备以外,我就来说说具备的其它功能。首先我们要知道,在linux操作系统中,进程是由其父进程衍生出来的,所以有一个很明显的问题,第一个进程是怎么来的?毫无疑问,是linux内核启动的时候创造出来的。但是,有架构思想的朋友们就不难想出,内核代码产生的进程是运行在内核态的,而我们的应用程序是运行在用户态的。所以这里就产生了矛盾。单纯由内核产生的进程1是无法衍生出用户态的进程,所以,此时就需要根文件系统帮忙。将进程1从内核态引导到用户态上。说到这里,我相信很多人明白了,其实根文件系统里面包含着一个应用程序,该应用呈现出就是进程1。

    当然,根文件系统还由配置系统的功能。大家想一下,我们平时开机后,电脑桌面,开机账户,开机密码,开机运行的程序等等这些初始化的功能是怎么来的?没错,这些配置都存储在根文件系统里面。在linux系统下,一般都在/etc目录下,这个目录存储的都是内核启动需要的配置文件。

    当然,根文件系统还提供了根目录。大家都知道文件的存储时按照树形结构存储的。树枝可以由我们这些用户去定义,当时根目录也是/是怎么生成的?就是由根文件系统生成的。

    总结起来说,其实内核仅仅是提供了内核的功能,譬如说内存管理,进程调度等等那些基本的操作系统的功能。而剩下很多需要配置的文件都是由根文件系统提供的。那么为什么根文件系统不涵盖在内核里面形成一个统一的代码呢?在我个人看来,就是为了可移植性。内核中的程序是操作系统的基本功能基本在所有的CPU里面都是通用的,所以全部涵盖在内核层里面。而根文件系统是一些配置文件,不同的CPU有不同的配置模式,所以这里就有所不同。

    展开全文
  • 根文件系统初见

    2019-11-15 14:08:44
    当Linux内核启动时,必须找到并执行第一个用户程序,通常是init,这就是根文件系统。对于根文件系统实际工作上用的比较多,基于内存和flash的都用过很多,自认为对这一块还是比较深刻。...1.根文件系统是什么 、...

    当Linux内核启动时,必须找到并执行第一个用户程序,通常是init,这就是根文件系统。对于根文件系统工作上用的比较多,基于内存和flash的都实现编译、裁剪和加载启动验证过,自认为对这一块还是比较深刻。但是没有深入的去看一下里面的原理,都是基于网上的一些常用的使用方法,稍微有点非常规的用法,出现问题了,就蒙了,原来还可以这样,有一种豁然开朗的感觉。本文档基于ubuntu16.04梳理一下了根文件系统的流程,内容包括

    • 1.根文件系统是什么 、如何初始化、如何应用
    • 2.基于内存根文件系统、存放在哪里、如何解压、解压在哪、如何启动
    • 3.基于真实的根文件系统,其启动过程

    1. 根文件系统分类

    Linux启动时,第一个必须挂载的是根文件系统;若系统不能从指定设备上挂载根文件系统,则系统会出错而退出启动。在Linux应用中,主要的存储设备为RAM(DRAM, SDRAM)和ROM(常采用FLASH存储器),其框图如下:
    在这里插入图片描述

    • 1.基于ROM的文件系统
    1. 对于现在基于ROM的存储系统,嵌入式一般采用flash,基于MTD结构由其特性决定了,其文件系统一般采用jffs2、YaFFS、Cramfs等。
    2. 对于现在的PC系统,一般采用的是机械硬盘,其也经历了ext2,ext3,reiserfs,ext4,现在主流的系统都采用的是ext4。
    3. 对于现在手机,经历了很多的过程,2010年,Android从最初的YAFFS2切换到了Ext4,主要的原因YAFFS2只支持单线程,无法发挥多核CPU的潜力。2016年,为了解决了安卓平台一直以来的一个顽疾,长期使用后的卡顿,引入了F2FS。以前Linux沿用的Ext4文件系统适合机械硬盘,F2FS可以说是为闪存量身定制的文件系统,能极大地改善闪存长期使用后的读写性能。2018年华为提出了EROFS,可提供比其他只读文件系统方案更好的性能,且可以节省存储空间。因为文件系统本身是只读的设计,也加强了对于数据的安全防护。
    • 2.基于RAM的文件系统
    1. Ramdisk:是将一部分固定大小的内存当作分区来使用。它并非一个实际的文件系统,而是一种将实际的文件系统装入内存的机制,并且可以作为根文件系统。将一些经常被访问而又不会更改的文件(如只读的根文件系统)通过Ramdisk放在内存中,可以明显地提高系统的性能。在Linux的启动阶段,initrd提供了一套机制,可以将内核映像和根文件系统一起载入内存。
    2. ramfs/tmpfs:把所有的文件都放在RAM中,所以读/写操作发生在RAM中,可以用ramfs/tmpfs来存储一些临时性或经常要修改的数据,例如/tmp和/var目录,这样既避免了对Flash存储器的读写损耗,也提高了数据读写速度。对于传统的Ramdisk的不同之处主要在于:不能格式化,文件系统大小可随所含文件内容大小变化,当系统重新启动后,丢失所有的数据。
    3. 网络文件系统NFS:可以利用该技术在主机上建立基于NFS的根文件系统,挂载到嵌入式设备,可以很方便地修改根文件系统的内容

    2 为什么引入Ramdisk

    在早期的Linux系统中,一般只有软盘或者硬盘被用来作为Linux的根文件系统,因此很容易把这些设备的驱动程序集中到内核中。但是对于现实的需求越来越多,硬件设备花样百出,其表现为:

    • 1.根文件系统可能保存在各种设备中,包括SCSI,SATA,U盘等等,而不同的设备又要不同的硬件厂商的驱动,比如Intel的南桥自然需要Intel的IDE/SATA驱动,VIA的南桥需要VIA的IDE/SATA;
    • 2.根文件系统也有不同的文件系统的可能,比如ubuntu发行版本可能用到ext3,而其他的版本可能用到ext4,不同的文件系统也需要不同的文件系统模块,
    • 3.对所有的硬件的兼容性,特别是x86的系统,不同的硬件也有不同的硬件模块

    假如把所有的驱动/模块都编译进内核,那么就问题就来了,内核会非常的庞大,已经违背了内核的精神和本质,对于内核现有的方案已经有成熟的方案

    • 驱动/模块都驻留在根文件系统本身上/lib/modules/xxx,采用模块加载的方法

    对于这块,内核本来采用的是文件系统的方式,通过将驱动/模块编译成.ko方式加载,而此时需要先运行.ko,然后在加载根文件系统,那么就出现先有“鸡”还是先有“蛋”的问题来了,如果不支持这个存储设备,那么文件系统就无法挂载,但是你却先要我挂载后的文件/lib/modules/xxx。于是内核就引入了ramdisk的方法,总之ramdisk的存在的意义

    • 解决了“先有鸡还是先有蛋的问题”,既内核编译空间很小,同时也解决了在挂载真正的文件系统之前完成了各个模块/驱动的初始化

    3. 根文件系统初始化

    首选在内核启动过程,会初始化rootfs文件系统,rootfs和tmpfs都是内存中的文件系统,其类型为ramfs. 然后会把这个rootf挂载到根目录。 其代码如下:

    void __init mnt_init(void)
    {
    	int err;
    	...
    	kernfs_init();
    
    	err = sysfs_init();
    	if (err)
    		printk(KERN_WARNING "%s: sysfs_init error: %d\n",
    			__func__, err);
    	fs_kobj = kobject_create_and_add("fs", NULL);
    	if (!fs_kobj)
    		printk(KERN_WARNING "%s: kobj create error\n", __func__);
    	init_rootfs();
    	init_mount_tree();
    }
    

    这个函数主要完成下面的功能

    • sysfs_init初始化了sysfs文件系统,也就是根文件系统的sys目录,sysfs先于rootfs挂载是为全面展示linux驱动模型做好准备
    • init_rootfs()注册rootfs,然后调用init_mount_tree()挂载rootfs
    int __init init_rootfs(void)
    {
    	int err = register_filesystem(&rootfs_fs_type);
    
    	if (err)
    		return err;
    
    	if (IS_ENABLED(CONFIG_TMPFS) && !saved_root_name[0] &&
    		(!root_fs_names || strstr(root_fs_names, "tmpfs"))) {
    		err = shmem_init();
    		is_tmpfs = true;
    	} else {
    		err = init_ramfs_fs();
    	}
    
    	if (err)
    		unregister_filesystem(&rootfs_fs_type);
    
    	return err;
    }
    

    在init_rootfs()中,注册rootfs文件类型,主要作用是把rootfs加入内核维护的一个文件系统类型的链表中,同时供其它模块进行系统调用,下面来看看init_mount_tree

    static void __init init_mount_tree(void)
    {
    	struct vfsmount *mnt;
    	struct mnt_namespace *ns;
    	struct path root;
    	struct file_system_type *type;
    
    	type = get_fs_type("rootfs");
    	if (!type)
    		panic("Can't find rootfs type");
    	mnt = vfs_kern_mount(type, 0, "rootfs", NULL);
    	put_filesystem(type);
    	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 = mnt;
    	root.dentry = mnt->mnt_root;
    	mnt->mnt_flags |= MNT_LOCKED;
    
    	set_fs_pwd(current->fs, &root);
    	set_fs_root(current->fs, &root);
    }
    

    该函数首先拿到上面注册的rootfs文件系统,再调用vfs_kern_mount方法挂载该系统,然后将挂载结果mnt赋值给类型为struct path的变量root,同时将root.dentry赋值为mnt->mnt_root,即挂载的rootfs文件系统的根目录。最后,设置当前进程的当前目录和根目录都为root。
    至此,rootfs文件系统建立、并且挂载于自己超级块(包括目录项dentry和i节点inod)对应的目录项,设置了系统current根目录和根文件系统、pwd的目录和文件系统。rootfs流程完毕,但此时的rootfs是ramfs,这些数据掉电丢失,在根文件系统下要永久保存一些文件,就需要把根文件系统安装到实际硬盘或flash中。

    4. 虚拟挂载文件系统

    4.1. 虚拟文件系统存放在哪里

    initramfs/initrd根据CONFIG_BLK_DEV_INITRD定义是否使用,从vmlinux.lds.h文件可知,INIT_RAM_FS存放ramfs相关内容,包括.init.ramfs和.init.ramfs.info两个段。该方式主要针对arm架构,对于x86架构采用的是vmlinux.lds.S直接定义,

    #ifdef CONFIG_BLK_DEV_INITRD
    #define INIT_RAM_FS                                                     \
            . = ALIGN(4);                                                   \
            VMLINUX_SYMBOL(__initramfs_start) = .;                          \
            KEEP(*(.init.ramfs))                                            \
            . = ALIGN(8);                                                   \
            KEEP(*(.init.ramfs.info))
    #else
    #define INIT_RAM_FS
    #endif
    

    然后将该区域放到采用以下方式存储于init.data区,

    init.data : {
                    INIT_DATA
                    INIT_SETUP(16)
                    INIT_CALLS
                    CON_INITCALL
                    SECURITY_INITCALL
                    INIT_RAM_FS
                    *(.init.rodata.* .init.bss)     /* from the EFI stub */
            }
    

    .init.ramfs和.init.ramfs.info两个段在initramfs_data.S中定义,通过编译后的汇编,我们可以知道这两个段的地址,对于存储的地址,我们需要重点关注__initramfs_start和__initramfs_size,而__initramfs_size是通过编译出来的INITRAMFS_IMAGE计算得到的地址大小

    .section .init.ramfs,"a"
    __irf_start:
    .incbin __stringify(INITRAMFS_IMAGE)
    __irf_end:
    .section .init.ramfs.info,"a"
    .globl VMLINUX_SYMBOL(__initramfs_size)
    VMLINUX_SYMBOL(__initramfs_size):
    #ifdef CONFIG_64BIT
            .quad __irf_end - __irf_start
    #else
            .long __irf_end - __irf_start
    #endif
    

    对于编译过程,暂时不做分析,如果想认真分析请看内核的参考文档<<Documentation/filesystems/ramfs-rootfs-initramfs.txt>>,其大致过程如下:

    • 1.gen_initramfs_list.sh脚本以cpio格式对目标root目录压缩生成XXX.cpio.gz文件
    • 2.initramfs_data.S只做一件事情,创建一个Section,包含XXX.cpio.gz文件,参考编译出来的usr/.initramfs_data.o.cmd文件
    • 3.编译链接,通过__initramfs_start和__initramfs_size执行XXX.cpio.gz文件开始地址和文件大小,后面加载和解压文件的时候会 用到。

    4.2 虚拟文件系统如何启动

    4.1 启动阶段解析

    由第三节根目录已经挂上去了,可以挂载具体的文件系统了,其挂载的代码如下:

    static int __ref kernel_init(void *unused)
    {
    	....
    	if (ramdisk_execute_command) {
    		ret = run_init_process(ramdisk_execute_command);
    		if (!ret)
    			return 0;
    		pr_err("Failed to execute %s (error %d)\n",
    		       ramdisk_execute_command, ret);
    	}
    	....
    }
    

    内核启动时,通过ramdisk_execute_command来决定是否挂载ramdisk,接着上节ramdisk_execute_command来看,什么情况下会给这个参数赋值,一是修改Kernel的bootargs,增加rdinit选项,也就是说启动时候将rdinit参数传递给内核,内核解析。

    static int __init rdinit_setup(char *str)
    {
    	unsigned int i;
    
    	ramdisk_execute_command = str;
    	/* See "auto" comment in init_setup */
    	for (i = 1; i < MAX_INIT_ARGS; i++)
    		argv_init[i] = NULL;
    	return 1;
    }
    __setup("rdinit=", rdinit_setup);
    

    当系统没有指定这个参数的时候,会通过以下的代码来指定,如果没有指定,则会使用默认的/init

    static noinline void __init kernel_init_freeable(void)
    {
    	...
    	do_basic_setup();
    	if (!ramdisk_execute_command)
    		ramdisk_execute_command = "/init";
    
    	if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
    		ramdisk_execute_command = NULL;
    		prepare_namespace();
    	}
    	...
    }
    

    我们知道对于ramdisk采用__initramfs_start和__initramfs_size读取,在我们不熟悉的情况下,那么我们只有通过编译存放的地址来看看内核怎么读取,然后解压等操作,先看看这个地址做了什么?

    static int __init populate_rootfs(void)
    {
       /* Load the built in initramfs */
       char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size);----------------------1if (err)
       	panic("%s", err); /* Failed to decompress INTERNAL initramfs */
       /* If available load the bootloader supplied initrd */
       if (initrd_start && !IS_ENABLED(CONFIG_INITRAMFS_FORCE)) {------------------------------2#ifdef CONFIG_BLK_DEV_RAM                  ------------------------------------------------【3】
       	int fd;
       	printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n");
       	err = unpack_to_rootfs((char *)initrd_start,
       		initrd_end - initrd_start);
       	if (!err) {
       		free_initrd();
       		goto done;
       	} else {
       		clean_rootfs();
       		unpack_to_rootfs(__initramfs_start, __initramfs_size);
       	}
       	printk(KERN_INFO "rootfs image is not initramfs (%s)"
       			"; looks like an initrd\n", err);
       	fd = sys_open("/initrd.image",
       		      O_WRONLY|O_CREAT, 0700);
       	if (fd >= 0) {
       		ssize_t written = xwrite(fd, (char *)initrd_start,
       					initrd_end - initrd_start);
    
       		if (written != initrd_end - initrd_start)
       			pr_err("/initrd.image: incomplete write (%zd != %ld)\n",
       			       written, initrd_end - initrd_start);
    
       		sys_close(fd);
       		free_initrd();
       	}
       done:
       	/* empty statement */;
    #else
       	printk(KERN_INFO "Unpacking initramfs...\n");
       	err = unpack_to_rootfs((char *)initrd_start,							
       		initrd_end - initrd_start);    --------------------------------------------------4if (err)
       		printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err);
       	free_initrd();
    #endif
       }
       flush_delayed_fput();
       /*
        * Try loading default modules from initramfs.  This gives
        * us a chance to load before device_initcalls.
        */
       load_default_modules();
    
       return 0;
    }
    

    对于这段代码做了些什么呢?是不是感觉前面分析的好像有点点不对头,是的,确实是的,下面来看看真实的有哪些不一样呢?

    • 1.unpack_to_rootfs顾名思义,就是解压包到rootfs,这个主要是针对initramfs,它的作用和initrd类似,只是和内核编译成一个文件(该initramfs是经过gzip压缩后的cpio格式的数据文件),该cpio格式的文件被链接进了内核中特殊的数据段.init.ramfs上其中全局变量__initramfs_start和__initramfs_end分别指向这个数据段的起始地址和结束地址。内核启动时会对.init.ramfs段中的数据进行解压,然后使用它作为临时的根文件系统。
    • 2.判断是否加载了initrd,这个initrd_start一般由启动参数传递进来,后面会再看看这个参数的来龙去脉
    • 3.CONFIG_BLK_DEV_RAM是将内存假设为一个硬盘驱动器,你在他的上面存储文件,内存的I/O速度是硬盘的N倍,一般没有使用,暂不考虑
    • 4.判断加载的是不是initramfs CPIO文件,如果解压成功,释放image中initrd对应内存。

    通过这个函数发现,对于基于内存得文件系统,内核是分别处理的,那么我们再回头来看看这两个的区别initramfs和initrd

    • 1.Linux内核只认cpio格式的initramfs文件包(因为unpack_to_rootfs只能解析cpio格式文件),非cpio格式的 initramfs文件包将被系统抛弃,而initrd可以是cpio包也可以是传统的镜像(image)文件,实际使用中initrd都是传统镜像文件。
    • 2.initramfs在编译内核的同时被编译并与内核连接成一个文件,它被链接到地址__initramfs_start处,与内核同时被 bootloader加载到ram中,而initrd是另外单独编译生成的,是一个独立的文件,其地址是由启动参数传递给内核解析,这个后面再单独分析
    • 3.initramfs被解析处理后原始的cpio包(压缩或非压缩)所占的空间(&__initramfs_start - &__initramfs_end)是作为系统的一部分直接保留在系统中,不会被释放掉,而对于initrd镜像文件,如果没有在命令行中设置"keepinitd"命令,那么initrd镜像文件被处理后其原始文件所占的空间(initrd_end - initrd_start)将被释放掉

    4.2 启动

    kernel_init()是用户空间第一个进程,启动ramdisk_execute_command来替代当前进程

    static int __ref kernel_init(void *unused)
    {
       	 kernel_init_freeable();    ----------------------------------------1if (ramdisk_execute_command) {
       		ret = run_init_process(ramdisk_execute_command);----------------2if (!ret)
       			return 0;
       		pr_err("Failed to execute %s (error %d)\n",
       		       ramdisk_execute_command, ret);
       	}
    }
    
    • 1.执行各种initcall,包括对ramfs注册和populate_rootfs()解压ramdisk;以及判断ramdisk_execute_command是否存在,否则prepare_namespace()
    • 2.启动ramdisk_execute_command
      那么我们就来看看ramdisk_execute_command这个参数是怎么来的,
    static noinline void __init kernel_init_freeable(void)
    {
       if (!ramdisk_execute_command)
       	ramdisk_execute_command = "/init";               ----------------------------1if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {------2】
       	ramdisk_execute_command = NULL;
       	prepare_namespace();
       }
    }
    
    static int __init rdinit_setup(char *str)
    {
       unsigned int i;
    
       ramdisk_execute_command = str;
       /* See "auto" comment in init_setup */
       for (i = 1; i < MAX_INIT_ARGS; i++)
       	argv_init[i] = NULL;
       return 1;
    }
    __setup("rdinit=", rdinit_setup);
    
    • 1.从这个可以看出,对于ramdisk_execute_command一种方式是通过启动参数传递,如果没有传递就走默认的init,最后就会执行虚拟文件系统下面的init文件
    • 2.然后sys_access()检查rootfs中是否存在ramdisk_execute_command,没有则需要prepare_namespace()准备rootfs,挂载真实的根文件系统

    最后,我们回过头看看,怎么启动虚拟和真实的文件系统,如果虚拟的文件系统在解压后确实存在ramdisk_execute_command对应的目录,那么就会执行虚拟的文件系统,而如果不存在,那么就会在prepare_namespace挂载真实的文件系统,并且在启动参数中将execute_command传递给内核,就会在真实的文件系统中执行execute_command。有一个疑问,虚拟文件系统启动后,成功后就会返回,不会执行execute_command的真实文件系统,那么如果有虚拟的文件系统,怎么样去启动真正的文件系统呢?那么这个需要看虚拟文件系统做了些什么。

    static int __ref kernel_init(void *unused)
    {
    	if (ramdisk_execute_command) {
    		ret = run_init_process(ramdisk_execute_command);
    		if (!ret)
    			return 0;
    		pr_err("Failed to execute %s (error %d)\n",
    		       ramdisk_execute_command, ret);
    	}
    
    	/*
    	 * We try each of these until one succeeds.
    	 *
    	 * The Bourne shell can be used instead of init if we are
    	 * trying to recover a really broken machine.
    	 */
    	if (execute_command) {
    		ret = run_init_process(execute_command);
    		if (!ret)
    			return 0;
    		panic("Requested init %s failed (error %d).",
    		      execute_command, ret);
    	}
    }
    

    该过程,如果定义了虚拟的文件系统就执行虚拟的文件系统的/init或者Bootloader传递的参数,如果没有定义虚拟文件系统,那么就走真实的文件系统。下面我们来看看怎么从虚拟的文件系统切换到真实的文件系统,我们来看看init这个脚本做了什么,该init文件是一个由sh解释并执行的脚本文件

    • 1.建立了相关目录和挂载点,并将kernel运行过程中产生的信息挂载到/sys和/proc目录下。
    • 2.如果在GRUB的kernel行上有quiet关键字,则在kernel启动和initrd中init脚本执行的过程中不会在屏幕上显示相关信息而是一个闪烁的下划线,否则将显示"Loading, please wait…"
    • 3.在/dev目录下建立devtmpfs文件系统,devtmpfs是一个虚拟的文件系统被挂载后会自动在/dev目录生成很多常见的设备节点文件,当挂载devtmpfs失败时会手动建立/dev/console和/dev/null设备节点文件。/dev/console 总是代表当前终端,用于输出kernel启动时的输出内容,在最后通 过 exec 命令用指定程序替换当前 shell 时使用
    • 4.引入 /conf/conf.d 下的所有文件,注意在引入的时候用了 -f 参数判断,这样只有普通的文件才 会被引入,链接(硬链接除外)、目录之类的非普通文件不会被引入,当使用不支持命令行参数的开机引导程序时,可以在该目录下建立各种参数设置文件
    • 5.for循环中主要功能是获取kernel的命令行选项(/proc/cmdline )然后赋给相应的变量。其中最重要的是root,其它的可有可无
    • 6.按照/scripts/init-top/ORDER文件的配置依次执行其下的脚本文件,这里最重要的是开启了udev daemon,udev 以 daemon 的方式启动 udevd,接着执行 udevtrigger 触发在机器启动前已 经接入系统的设备的 uevent,然后调用 udevsettle 等待,直到当 前 events 都被处理完毕
    • 7.把系统的启动交给了将要进入的系统的 ${init} (上面初始化为 “/sbin/init”),并用 /dev/console 作为输入与输出的设备,最终也是调用到真实文件系统的/sbin/init。

    5 真实文件系统

    对于真实的文件系统,从上面的流程可以看出,其实可以分为直接启动真实文件系统,也就是我们大家熟悉的/sbin/init,还有一种是由虚拟的文件系统来启动真实的文件系统,对于直接启动真实的文件系统,比较简单,其流程应该是直接挂载,并执行就可以了,下面我们主要来看看挂载的流程

    void __init prepare_namespace(void)
    {
    	int is_floppy;
    
    	if (root_delay){		-----------------------------1printk(KERN_INFO "Waiting %d sec before mounting root device...\n",
    		       root_delay);
    		ssleep(root_delay);
    	}
    	wait_for_device_probe();------------------------------2md_run_setup();			------------------------------3if (saved_root_name[0]) {-----------------------------4】
    		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())	---------------------------------5goto out;
    
    	/* wait for any asynchronous scanning to complete */
    	if ((ROOT_DEV == 0) && root_wait) {	-----------------6printk(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();---------------------------------------7】
    out:
    	devtmpfs_mount("dev");
    	sys_mount(".", "/", NULL, MS_MOVE, NULL);
    	sys_chroot(".");
    }
    
    • 1.安装 root 文件系统 之前等待10秒,由bootloader传递过来的
    • 2.等待已知设备完成其探测
    • 3.md_run_setup初始化MD设备,MD设备主要包含了LINUX内核的软RAID实现
    • 4.该方法中的saved_root_name变量的值是在kernel启动时,由传给kernel的root参数决定的,对应的设置方法如下root=/dev/sda2,由启动__setup(“root=”, root_dev_setup)来解析
    • 5.通过设备文件标识符ROOT_DEV判断启动设备文件是否是软盘,并把判断结果存入局部变量is_floppy中
    • 6.调用initrd_load函数
    • 7.挂载root,也是最重要的操作
    void __init mount_root(void)
    {
    #ifdef CONFIG_ROOT_NFS			------------------------【1】
    	if (ROOT_DEV == Root_NFS) {
    		if (mount_nfs_root())
    			return;
    
    		printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");
    		ROOT_DEV = Root_FD0;
    	}
    #endif
    #ifdef CONFIG_BLK_DEV_FD
    	if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
    		/* rd_doload is 2 for a dual initrd/ramload setup */
    		if (rd_doload==2) {
    			if (rd_load_disk(1)) {
    				ROOT_DEV = Root_RAM1;
    				root_device_name = NULL;
    			}
    		} else
    			change_floppy("root floppy");
    	}
    #endif
    #ifdef CONFIG_BLOCK		  -------------------------【2】
    	{
    		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
    }
    

    1.对于网络文件系统NFS的挂载
    2.调用create_dev函数在/dev目录中创建root设备文件,设备标识符为ROOT_DEV,设备文件名称为root_device_nam,调用函数mount_block_root安装根文件系统,遍历注册的文件系统类型,依次尝试将/dev/root指向的硬盘分区挂载到/root目录下。里面的细节暂不做分析,做完这些之后,其余的事项大家都很清楚了,最后直接运行/sbin/init。

    5 结束语

    对于根文件系统,其实有两种处理方式,一种是真实的文件系统,像现在很多平台都在使用,例如我们比较常见的JFFS2、YAFFS都是真实的文件系统,像基于内存的initramfs其实也是一个真实的文件系统,其实大部分的平台都是按照这个方式来做的。而现在linux 发行版,为了适配各种不同的硬件,将所有驱动编译进内核是不现实的,所有就采用了initrd技术来解决该问题,内核中只有编译基本的硬件功能,在按照过程中通过检查系统硬件,就产生我们现在看到虚拟和真实文件系统之分。

    展开全文
  • 文件系统是对一个存储设备上的数据和元数据进行组织的机制。这种机制有利于用户和操作系统的交互。在一篇oracle的技术文章中看到这样一句话 尽管内核是 Linux 的核心,但文件却是用户与操作系统交互所采用的主要工具...

    文件系统是对一个存储设备上的数据和元数据进行组织的机制。这种机制有利于用户和操作系统的交互。在一篇oracle的技术文章中看到这样一句话

    尽管内核是 Linux 的核心,但文件却是用户与操作系统交互所采用的主要工具。这对 Linux 来说尤其如此,这是因为在 UNIX 传统中,它使用文件 I/O 机制管理硬件设备和数据文件

    这句话我是这样理解的,在Linux没有文件系统的话,用户和操作系统的交互也就断开了,例如我们使用最多的交互shell,包括其它的一些用户程序,都没有办法运行。在这里可以看到文件系统相对于Linux操作系统的重要性。

    下面是Linux文件系统组件的体系结构:
    在这里插入图片描述
    用户空间包含一些应用程序(例如,文件系统的使用者)和 GNU C 库(glibc),它们为文件系统调用(打开、读取、写和关闭)提供用户接口。系统调用接口的作用就像是交换器,它将系统调用从用户空间发送到内核空间中的适当端点。

    VFS 是底层文件系统的主要接口。这个组件导出一组接口,然后将它们抽象到各个文件系统,各个文件系统的行为可能差异很大。有两个针对文件系统对象的缓存(inode 和 dentry)。它们缓存最近使用过的文件系统对象。

    每个文件系统实现(比如 ext2、JFS 等等)导出一组通用接口,供 VFS 使用。缓冲区缓存会缓存文件系统和相关块设备之间的请求。例如,对底层设备驱动程序的读写请求会通过缓冲区缓存来传递。这就允许在其中缓存请求,减少访问物理设备的次数,加快访问速度。以最近使用(LRU)列表的形式管理缓冲区缓存。注意,可以使用 sync 命令将缓冲区缓存中的请求发送到存储媒体(迫使所有未写的数据发送到设备驱动程序,进而发送到存储设备)。

    当我们在Windows下,提到文件系统时,你的第一反应是想到的是什么?是不是Windows下的一些Fat32、NTFS等的文件系统的类型。而在Linux中,你可能会想到Ext2、Ext3,但你还必须要有一个根文件系统的概念。根文件系统首先是一种文件系统,该文件系统不仅具有普通文件系统的存储数据文件的功能,但是相对于普通的文件系统,它的特殊之处在于,它是内核启动时所mount的第一个文件系统,内核代码的映像文件保存在根文件系统中,系统引导启动程序会在根文件系统挂载之后从中把一些初始化脚本(如rcS,inittab)和服务加载到内存中去运行。我们要明白文件系统和内核是完全独立的两个部分。在嵌入式中移植的内核下载到开发板上,是没有办法真正的启动Linux操作系统的,会出现无法加载文件系统的错误。

    那么根文件系统在系统启动中到底是什么时候挂载的呢?先将/dev/ram0挂载,而后执行/linuxrc.等其执行完后。切换根目录,再挂载具体的根文件系统.根文件系统执行完之后,也就是到了Start_kernel()函数的最后,执行init的进程,也就第一个用户进程。对系统进行各种初始化的操作。如果要能明白这里的过程的话,可要好好的看看Linux内核源码了。

    根文件系统之所以在前面加一个”根“,说明它是加载其它文件系统的”根“,既然是根的话,那么如果没有这个根,其它的文件系统也就没有办法进行加载的。它包含系统引导和使其他文件系统得以mount所必要的文件。根文件系统包括Linux启动时所必须的目录和关键性的文件,例如Linux启动时都需要有init目录下的相关文件,在 Linux挂载分区时Linux一定会找/etc/fstab这个挂载文件等,根文件系统中还包括了许多的应用程序bin目录等,任何包括这些Linux 系统启动所必须的文件都可以成为根文件系统。

    Linux启动时,第一个必须挂载的是根文件系统;若系统不能从指定设备上挂载根文件系统,则系统会出错而退出启动。成功之后可以自动或手动挂载其他的文件系统。因此,一个系统中可以同时存在不同的文件系统。

    在 Linux 中将一个文件系统与一个存储设备关联起来的过程称为挂装(mount)。使用 mount 命令将一个文件系统附着到当前文件系统层次结构中(根)。在执行挂装时,要提供文件系统类型、文件系统和一个挂装点。根文件系统被挂载到根目录下“/”上后,在根目录下就有根文件系统的各个目录,文件:/bin /sbin /mnt等,再将其他分区挂接到/mnt目录上,/mnt目录下就有这个分区的各个目录,文件。

    Linux根文件系统中一般有如下图的几个目录:

    1./bin目录

    该目录下存放所有用户都可以使用的、基本的命令,这些命令在挂接其它文件系统之前就可以使用,所以/bin目录必须和根文件系统在同一个分区中。

    /bin目录下常用的命令有:cat,chgrp,chmod,cp,ls,sh,kill,mount,umount,mkdir,mknod,[,test等“[”命令其实就是test命令,我们在利用Busybox制作根文件系统时,在生成的bin目录下,可以看到一些可执行的文件,也就是可用的一些命令。

    2./sbin 目录

    该目录下存放系统命令,即只有管理员能够使用的命令,系统命令还可以存放在/usr/sbin,/usr/local/sbin目录下,/sbin目录中存放的是基本的系统命令,它们用于启动系统,修复系统等,与/bin目录相似,在挂接其他文件系统之前就可以使用/sbin,所以/sbin目录必须和根文件系统在同一个分区中。

    /sbin目录下常用的命令有:shutdown reboot fdisk fsck等,本地用户自己安装的系统命令放在/usr/local/sbin目录下。

    3、/dev目录

    该目录下存放的是设备文件,设备文件是Linux中特有的文件类型,在Linux系统下,以文件的方式访问各种设备,即通过读写某个设备文件操作某个具体硬件。比如通过"dev/ttySAC0"文件可以操作串口0,通过"/dev/mtdblock1"可以访问MTD设备的第2个分区。

    4./etc目录

    该目录下存放着各种配置文件,对于PC上的Linux系统,/etc目录下的文件和目录非常多,这些目录文件是可选的,它们依赖于系统中所拥有的应用程序,依赖于这些程序是否需要配置文件。在嵌入式系统中,这些内容可以大为精减。

    5./lib目录

    该目录下存放共享库和可加载(驱动程序),共享库用于启动系统。运行根文件系统中的可执行程序,比如:/bin /sbin 目录下的程序。

    6、/home目录

    用户目录,它是可选的,对于每个普通用户,在/home目录下都有一个以用户名命名的子目录,里面存放用户相关的配置文件。

    7./root目录

    根用户的目录,与此对应,普通用户的目录是/home下的某个子目录。

    8./usr目录

    /usr目录的内容可以存在另一个分区中,在系统启动后再挂接到根文件系统中的/usr目录下。里面存放的是共享、只读的程序和数据,这表明/usr目录下的内容可以在多个主机间共享,这些主要也符合FHS标准的。/usr中的文件应该是只读的,其他主机相关的,可变的文件应该保存在其他目录下,比如/var。/usr目录在嵌入式中可以精减。

    9、 /var目录

    与/usr目录相反,/var目录中存放可变的数据,比如spool目录(mail,news),log文件,临时文件。

    10、/proc目录

    这是一个空目录,常作为proc文件系统的挂接点,proc文件系统是个虚拟的文件系统,它没有实际的存储设备,里面的目录,文件都是由内核临时生成的,用来表示系统的运行状态,也可以操作其中的文件控制系统。

    11、 /mnt目录

    用于临时挂载某个文件系统的挂接点,通常是空目录,也可以在里面创建一引起空的子目录,比如/mnt/cdram /mnt/hda1 。用来临时挂载光盘、硬盘。

    1. /tmp目录

    用于存放临时文件,通常是空目录,一些需要生成临时文件的程序用到的/tmp目录下,所以/tmp目录必须存在并可以访问。

    那我们利用Busybox制作根文件系统就是创建这上面的这些目录,和这些目录下面的各种文件。

    对于嵌入式Linux系统的根文件系统来说,一般可能没有上面所列出的那么复杂,比如嵌入式系统通常都不是针对多用户的,所以/home这个目录在一般嵌入式Linux中可能就很少用到,而/boot这个目录则取决于你所使用的BootLoader是否能够重新获得内核映象从你的根文件系统在内核启动之前。一般说来,只有/bin,/dev,/etc,/lib,/proc,/var,/usr这些需要的,而其他都是可选的。

    根文件系统一直以来都是所有类Unix操作系统的一个重要组成部分,也可以认为是嵌入式Linux系统区别于其他一些传统嵌入式操作系统的重要特征,它给 Linux带来了许多强大和灵活的功能,同时也带来了一些复杂性。我们需要清楚的了解根文件系统的基本结构,以及细心的选择所需要的系统库、内核模块和应用程序等,并配置好各种初始化脚本文件,以及选择合适的文件系统类型并把它放到实际的存储设备的合适位置,下面是几中比较常用的文件系统。

    (1) jffs2

    JFFS嵌入式系统文件系统最早是由瑞典 Axis Communications公司基于Linux2.0的内核为嵌入式系统开发的文件系统。JFFS2是RedHat公司基于JFFS开发的闪存文件系统,最初是针对RedHat公司的嵌入式产品eCos开发的嵌入式文件系统,所以JFFS2也可以用在Linux, uCLinux中。

    Jffs2: 日志闪存嵌入式系统文件系统版本2 (Journalling Flash FileSystem v2)主要用于NOR型闪存,基于MTD驱动层,特点是:可读写的、支持数据压缩的、基于哈希表的日志型文件系统,并提供了崩溃/掉电安全保护,提供“写平衡”支持等。缺点主要是当文件系统已满或接近满时,因为垃圾收集的关系而使jffs2的运行速度大大放慢。目前jffs3正在开发中。关于jffs系列文件系统的使用详细文档,可参考MTD补丁包中mtd-jffs-HOWTO.txt。

    jffsx不适合用于NAND闪存主要是因为NAND闪存的容量一般较大,这样导致jffs为维护日志节点所占用的内存空间迅速增大,另外,jffsx文件系统在挂载时需要扫描整个FLASH的内容,以找出所有的日志节点,建立文件结构,对于大容量的NAND闪存会耗费大量时间。

    (2) yaffs: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在内存空间占用、垃圾回收速度、读/写速度等方面均有大幅提升。

    (3) Cramfs:Compressed ROM File System

    Cramfs是Linux的创始人 Linus Torvalds参与开发的一种只读的压缩文件系统。它也基于MTD驱动程序。在cramfs文件系统中,每一页(4KB)被单独压缩,可以随机页访问,其压缩比高达2:1,为嵌入式系统节省大量的Flash存储空间,使系统可通过更低容量的FLASH存储相同的文件,从而降低系统成本。

    Cramfs文件系统以压缩方式存储,在运行时解压缩,所以不支持应用程序以XIP方式运行,所有的应用程序要求被拷到RAM里去运行,但这并不代表比 Ramfs需求的RAM空间要大一点,因为Cramfs是采用分页压缩的方式存放档案,在读取档案时,不会一下子就耗用过多的内存空间,只针对目前实际读取的部分分配内存,尚没有读取的部分不分配内存空间,当我们读取的档案不在内存时,Cramfs文件系统自动计算压缩后的资料所存的位置,再即时解压缩到 RAM中。另外,它的速度快,效率高,其只读的特点有利于保护文件系统免受破坏,提高了系统的可靠性。

    由于以上特性,Cramfs在嵌入式系统中应用广泛。但是它的只读属性同时又是它的一大缺陷,使得用户无法对其内容对进扩充。Cramfs映像通常是放在Flash中,但是也能放在别的文件系统里,使用 loopback 设备可以把它安装别的文件系统里。

    (4) 网络文件系统NFS (Network File System)

    NFS是由Sun开发并发展起来的一项在不同机器、不同操作系统之间通过网络共享文件的技术。在嵌入式Linux系统的开发调试阶段,可以利用该技术在主机上建立基于NFS 的根文件系统,挂载到嵌入式设备,可以很方便地修改根文件系统的内容。以上讨论的都是基于存储设备的文件系统(memory-based file system),它们都可用作Linux的根文件系统。实际上,Linux还支持逻辑的或伪文件系统(logical or pseudo file system),例如procfs(proc文件系统),用于获取系统信息,以及devfs(设备文件系统)和sysfs,用于维护设备文件。

    展开全文
  • 根文件系统

    2021-03-19 14:24:42
    一、什么是根文件系统 根文件系统首先一种文件系统,该文件系统不仅具有普通文件系统的存储数据文件的功能,但是相 对于普通的文件系统而言它还是内核启动时所挂载(mount)的第一个文件系统,系统引导启动程序会 ...
  • 文章目录根文件系统基本概念什么是根文件系统根文件系统主要目录结构程序文件的放置定制应用程序库文件的放置Linux 设备文件举例字符设备举例块设备举例设备的主设备号次设备号创建设备节点基本的设备节点Linux...
  • 文章目录1,根文件系统基本概念1.1,什么是根文件系统?1.2,根文件系统主要目录结构1.3,程序文件的放置1.4,定制应用程序1.5,库文件的放置2,Linux 设备文件2.1,举例2.1.1,字符设备举例2.1.2,块设备举例2.2,...
  • 引言:在linux系统中,一直对根文件系统理解得模棱两可,是时候彻底梳理一下了,包括根文件系统是什么 、如何初始化、如何应用及Android系统中的根文件系统等问题。首先要弄清楚根文件系统是什么?以下英文部分摘自...
  •  首先你考虑一点描述语句:根文件系统一种“文件系统”,可以认为一种“特殊的”“文件系统”,为什么是叫“特殊的”呢?因为这种“根文件系统”承载着某些“特殊的功能”(其实“文件系统”相同的话,所具备...
  • 虚拟根文件系统与真实根文件系统

    千次阅读 2016-05-05 09:21:21
    引言:根文件系统的noinitramfs已经分析,继续上文未完的initramfs和Android根文件系统分析,这两者有什么关系?1.initramfs对于initramfs,kernel 2.5开始引入,其实质在内核镜像中附加一个cpio包(cpio一个用于...
  • 最后终于弄明白了根文件系统到底是啥,一直在困惑根文件系统和 jffs2、 yaffs、exc3等啥区别,是啥关系,查了好多资料,基本上都差不多,都说根文件系统是一种文件系统,都没有说根文件系统是什么格式进行存储的呢...
  • Linux根文件系统

    2021-03-07 11:55:57
    1.1、为什么需要根文件系统 (1)init进程的应用程序在根文件系统上 (2)根文件系统提供了根目录/ (3)内核启动后的应用层配置(etc目录)在根文件系统上。几乎可以认为:发行版=内核+rootfs (4)shell命令程序在根文件系统...
  • 根文件系统构建

    2020-07-17 10:01:37
    根文件系统一般也叫做 rootfs,那么什么根文件系统?看到“文件系统”这四个字,很多 人,包括我第一反应就是 FATFS、 FAT、 EXT4、 YAFFS 和 NTFS 等这样的文件系统。在这里, 根文件系统并不 FATFS 这样的...
  • linux根文件系统

    2017-06-15 22:58:07
    什么需要根文件系统?  1、init进程的应用程序挂在根文件系统上。 2、根文件系统提供了根目录 / 。 3、内核启动后的应用层配置(etc目录)在根文件系统上。(可以认为,发行版 =内核+rootfs)。 4、Shell命令...
  • 计算机的文件系统是一种存储和组织计算机数据的方法,它使得对其访问和查找变得容易,文件系统使用文件和树形目录的抽象逻辑概念代替了硬盘和光盘等物理设备使用数据块的概念,用户使用文件系统来保存数据不必关心...
  • Linux之文件系统和根文件系统

    千次阅读 2015-06-30 13:56:08
    在学习Linux的过程中,会遇到“文件系统”和“根文件系统”两个概念,这两个概念容易混淆,总觉得为什么Linux需要这么多的文件系统,这两种系统的各自作用是什么,有什么区别呢?  “尽管内核是Linux的核心,但...
  • Linux系统中的根文件系统,Root FileSystem,简称为rootfs; 关于rootfs,之前一直很迷惑,不知道所要表达的真正的含义; 即便通过buildroot自己建立了相关的rootfs之后,还是没能很明白的理解,到底rootfs...
  • 文件系统是什么

    2021-01-23 21:57:47
    什么是根文件系统? 既然内核启动要挂载根文件系统,那么挂载完根文件系统,不是已经可以用终端shell命令创建、访问文件了吗,为啥还要挂载其它文件系统?启动过程究竟要挂载几次文件系统? 文件系统一般对应...
  • 首先你考虑一点描述语句:根文件系统一种“文件系统”,可以认为一种“特殊的”“文件系统”,为什么是叫“特殊的”呢?因为这种“根”“文件系统”承载着某些“特殊的功能”(其实“文件系统”相同的话,所...
  • 学习linux系统移植的时候,自己做了个根文件系统,然后把他移植到2410的板子上,结果移植成功,在板子上看到了我移植的文件系统,然后兴高采烈地做tslib的移植,最后悲剧了,发现在触摸屏校验的时候出现一个问题,...
  • 根文件系统原理

    2017-02-17 13:45:26
    什么需要根文件系统(1)init进程的应用程序在根文件系统上(2)根文件系统提供了根目录/(3)内核启动后的应用层配置(etc目录)在根文件系统上(4)shell命令程序在根文件系统上总结:一套Linux体系,只有内核本身不能...
  • 一:什么是根文件系统(1)首先明白一点,我们的文件在硬盘上的存储并非连续存放的,而是零散的存放在硬盘中的某些空余扇区中的,这就导致了我们很难去查看我们存放的文件(因为人无法记住所有文件存放在哪些扇区...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,142
精华内容 856
关键字:

根文件系统是什么