ARM9嵌入式Linux开发-嵌入式Linux根文件系统概述

标签: ARM9嵌入式Linux开发

Linux中一切皆文件! 是指:在Linux系统中,各个部分都可把它看成是一个文件,包括了所有的硬盘分区、目录、软盘、CD-ROM光盘和其他存储介质,以及显示器和打印机等硬件设备都可被看成一个文件。        

从本质上而言,用户的一切工作就是对文件的操作。

嵌入式Linux文件系统

嵌入式文件系统的需求分析

文件系统是指在一个物理设备上的任何文件组织和目录,它构成了Linux系统上所有数据的基础,Linux程序、库、系统文件和用户文件都驻留其中,并向用户或程序提供一个使用文件的统一界面,从而能够使对文件的各类操作能够在更抽象、更简单的层次上进行。

Linux支持的常见的文件系统有FAT32、NTFS、VFAT、ISO9660、Ext2、Ext3等,但这些文件系统在嵌入式环境中一般不能直接使用,这主要是与嵌入式环境有一定关系。

1.一般来说,嵌入式系统的应用条件远比通用计算机使用的环境恶劣,通用文件系统用于嵌入式系统时,往往在可靠性上面设计考虑不足。     

2.通用文件系统是针对系统资源非常丰富的计算机平台,依靠运算速度较快的CPU系统,同时其使用速度较慢的磁盘驱动器,因此它们常常大量使用缓存技术,要耗费比较多的系统资源。这些对于嵌入式系统中系统资源十分有限的应用状况是格格不入。     

3.嵌入式系统的层次存储。关键的数据资料和系统配置脚本,需要在设计的时候保证其完整性,一般的数据允许一定程度的误码损伤,不必需要严格保证存储的正确性。通用的文件系统是无法考虑这种层次存储。     

4.嵌入式系统存储时一般不使用硬盘,大多数是使用闪存来存放代码和数据,而且从成本考虑使用的闪存容量又极为有限,可是通用文件系统资源占用量大。  如:NTFS最小使用条件是50MB,这当然无法用于轻便级的嵌入式系统了。

嵌入式文件系统分类

根据存储介质的不同,嵌入式文件系统可以分为以下三类:

基于FLASH的文件系统

Flash(闪存)作为嵌入式系统的主要存储媒介,有其自身的特性。Flash的写入操作只能把对应位置的1修改为0,而不能把0修改为1(擦除Flash就是把对应存储块的内容恢复为1),因此,一般情况下,向Flash写入内容时,需要先擦除对应的存储区间,这种擦除是以块(block)为单位进行的。     

闪存主要有NOR和NAND两种技术。Flash存储器的擦写次数是有限的,NAND闪存还有特殊的硬件接口和读写时序。因此,必须针对Flash的硬件特性设计符合应用要求的文件系统;传统的文件系统如ext2等,用作Flash的文件系统会有诸多弊端。     在嵌入式Linux下,MTD(Memory Technology Device,存储技术设备)为底层硬件(闪存)和上层(文件系统)之间提供一个统一的抽象接口,即Flash的文件系统都是基于MTD驱动层的。使用MTD驱动程序的主要优点在于,它是专门针对各种非易失性存储器(以闪存为主)而设计的,因而它对Flash有更好的支持、管理和基于扇区的擦除、读/写操作接口。     

顺便一提,一块Flash芯片可以被划分为多个分区,各分区可以采用不同的文件系统;两块Flash芯片也可以合并为一个分区使用,采用一个文件系统。即文件系统是针对于存储器分区而言的,而非存储芯片。

(1)JFFS     

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

JFFS2: 日志闪存文件系统版本2 (Journalling Flash FileSystem v2)主要用于NOR型闪存,基于MTD驱动层,特点是:可读写的、支持数据压缩的、基于哈希表的日志型文件系统,并提供了崩溃/掉电安全保护,提供“写平衡”支持等。缺点主要是当文件系统已满或接近满时,因为垃圾收集的关系而使JFFS2的运行速度大大放慢。     

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

(2)YAFFS     

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     

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

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

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

(4)Romfs     

传统型的Romfs文件系统是一种简单的、紧凑的、只读的文件系统,不支持动态擦写保存,按顺序存放数据,因而支持应用程序以XIP(eXecute In Place,片内运行)方式运行,在系统运行时,节省RAM空间。uClinux系统通常采用Romfs文件系统。     

其他文件系统:fat/fat32也可用于实际嵌入式系统的扩展存储器(例如PDA, Smartphone, 数码相机等的SD卡),这主要是为了更好的与最流行的Windows桌面操作系统相兼容。ext2也可以作为嵌入式Linux的文件系统,不过将它用于FLASH闪存会有诸多弊端。

基于RAM的文件系统

(1)Ramdisk     

Ramdisk是将一部分固定大小的内存当作分区来使用。它并非一个实际的文件系统,而是一种将实际的文件系统装入内存的机制,并且可以作为根文件系统。将一些经常被访问而又不会更改的文件(如只读的根文件系统)通过Ramdisk放在内存中,可以明显地提高系统的性能。     

在Linux的启动阶段,initrd提供了一套机制,可以将内核映像和根文件系统一起载入内存。

(2)Ramfs/Tmpfs     

Ramfs是Linus Torvalds开发的一种基于内存的文件系统,工作于虚拟文件系统(VFS)层,不能格式化,可以创建多个,在创建时可以指定其最大能使用的内存大小。(实际上,VFS本质上可看成一种内存文件系统,它统一了文件在内核中的表示方式,并对磁盘文件系统进行缓冲。)     

Ramfs/Tmpfs文件系统把所有的文件都放在RAM中,所以读/写操作发生在RAM中,可以用Ramfs/Tmpfs来存储一些临时性或经常要修改的数据,例如/tmp和/var目录,这样既避免了对Flash存储器的读写损耗,也提高了数据读写速度。     

Ramfs/Tmpfs相对于传统的Ramdisk的不同之处主要在于:不能格式化,文件系统大小可随所含文件内容大小变化。Tmpfs的一个缺点是当系统重新引导时会丢失所有数据。

网络文件系统NFS

NFS是由Sun开发并发展起来的一项在不同机器、不同操作系统之间通过网络共享文件的技术。在嵌入式Linux系统的开发调试阶段,可以利用该技术在主机上建立基于NFS的根文件系统,挂载到嵌入式设备,可以很方便地修改根文件系统的内容。

以上讨论的都是基于存储设备的文件系统(memory-based file system),它们都可用作Linux的根文件系统。实际上,Linux还支持逻辑的或伪文件系统(logical or pseudo file system),例如procfs(proc文件系统),用于获取系统信息。     

Linux支持多种文件系统,为了对各类文件系统进行统一管理,Linux引入了虚拟文件系统VFS(Virtual File System),为各类文件系统提供一个统一的操作界面和应用编程接口。

根文件系统介绍

什么是Linux根文件系统

文件系统即在磁盘上组织文件的方法;

不同的操作系统都有一种把数据保存为文件和目录的方法(不同文件系统);

  • 如:Windows操作系统中,不同的磁盘分区有独立的根目录,并且用唯一的驱动器标识符表示,比如C:\,D:\等;
  • Linux的文件系统组织和Windows不同,它的文件系统是一个整体,把所以的文件系统结合成一个完整的统一体,这就是Linux的根文件系统。

使用Linux的时候,通过命令“ls /” 就会发现:

  • 在/下包涵很多的目录,比如etc、usr、var、bin ... ... 等目录;
  • 而在这些目录中还有很多的目录或文件;
  • 文件系统在Linux下看上去就象树形结构;
  • 所以可以把文件系统的结构形象的称为树形结构;
  • linux文件系统的最顶端是“/”;
  • 我们称“/”为Linux的根,也就是Linux操作系统的根文件系统;
  • Linux的文件系统的入口就是“/”,所有的目录、文件、设备都在“/”之下。

根文件系统目录结构

一个Linux的根文件系统目录结构如下:    

  • /:Linux文件系统的入口,也是处于最高一级的目录;
  • /bin:系统所需要的那些命令位于此目录,比如 ls、cp、mkdir等命令;
  • 这个目录中的文件都是可执行的、普通用户都可以使用的命令。作为基础系统所需要的最基础的命令就是放在这里;
  • /boot:Linux的内核及引导系统程序所需要的文件目录;如内核的映像文件,启动加载器(GRUB);
  • /dev:设备文件存储目录,比如声卡、磁盘... ...
  • /etc:系统配置文件的所在地,一些服务器的配置文件也在这里;比如/etc/inittab是init进程的配置文件,etc/fstab是用来指定启动时需要自动安装的文件系统列表
  • /home:普通用户家目录默认存放目录;
  • /lib:库文件存放目录;
  • /mnt:这个目录一般是用于存放挂载储存设备的挂载目录的,比如有cdrom 等目录;
  • /proc:操作系统运行时,进程信息及内核信息(比如cpu、硬盘分区、内存信息等)存放在这里。/proc目录伪装的文件系统proc的挂载目录;
  • /root:Linux超级权限用户root的目录;
  • /sbin:大多是涉及系统管理的命令的存放,是超级权限用户root的可执行命令存放地,普通用户无权限执行这个目录下的命令 ,如ifconfig;
  • /tmp :临时文件目录 ;
  • /usr:这个是系统存放程序的目录,比如命令、帮助文件等;
  • /var:目录包含在正常操作中被改变的文件:假脱机文件、记录文件、加锁文件、临时文件和页格式化文件。

文件存放规则

为了实现各种linux版本系统的标准化,各种版本的linux系统都会遵循FHS标准 (FHS,Flesustem Hierachy Standard 文件系统层次标准); 其定义了许多文件和 目录的名字和位置的 标准主要规则如右图:

最少的目录

在嵌入式系统中,根目录下的很多目录都可以删除,如下:

  • 为多用户提供可扩展环境的所需目录都可以删除;如(/home,/mnt,/root)
  • 根据引导加载情况,/boot目录可以删除。
  • 其余的目录: /bin,/dev,/etc,/proc,/sbin,/usr,/lib都是不可缺少的。

BusyBox移植

BusyBox概述

BusyBox是标准Linux工具的一个单个可执行实现。BusyBox 包含了一些简单的工具,例如cat和echo,还包含了一些更大、更复杂的工具,例如grep、find、mount以及telnet。有些人将 BusyBox称为Linux工具里的瑞士军刀。简单的说BusyBox就好像是个大工具箱,它集成压缩了Linux的许多工具和命令。     

嵌入式根目录下的bin、sbin以及linuxrc通常就是指向BusyBox的链接。在构建嵌入式linux的根文件系统的时候,会根据目标平台配置BusyBox源码,编译生成这两个目录和一个文件。根目录下的其他目录和文件需要自行建立或者从BusyBox的example中拷贝修改。

BusyBox启动流程分析

内核启动的最后一步就是启动init进程,init进程是由内核启动的第一个(也是唯一一个和)用户进程(进程ID 为1),它根据配置文件决定启动哪些程序,比如某些脚本,启动shell,运行用户指定的程序等。init进程是由内核调用/sbin/init文件而启动的,下面看一下内核代码中init/main.c,如下所示:

static noinline int init_post(void)
	__releases(kernel_lock)
{
	...
	if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
		printk(KERN_WARNING "Warning: unable to open an initial console.\n");//尝试打开/dev/console设备作为init进程的标准输入设备

	(void) sys_dup(0);
	(void) sys_dup(0);
//复制了两个handle, 这样stdout,stdin,stderr 都指向/dev/console, 这样就打开了标准设备输入,输出,标准错误设备

...
	if (execute_command) {//如果命令行参数设置了“init=...”,则execute_command等于这个参数指定的程序(我们的命令行参数中有“init=/linuxrc”)
		run_init_process(execute_command);
		printk(KERN_WARNING "Failed to execute %s.  Attempting "
					"defaults...\n", execute_command);
	}
	run_init_process("/sbin/init");
	run_init_process("/etc/init");
	run_init_process("/bin/init");
	run_init_process("/bin/sh");

	panic("No init found.  Try passing init= option to kernel.");
}

BusyBox中_install目录下的linuxrc是BusyBox的一个软链接,指向/bin/busybox,而/sbin/init也是/bin/busybox的符号链接,因此这个linxrc基本没有实际的意义只是一个连接作用。我们可以重写linuxrc,添加自己的一些初始化的东西。这样就可以把Linux内核中的init程序和BusyBox中的init程序结合起来了。

Busybox是目标板系统上执行的第一个应用程序, 当调用Busybox它会执行Busybox自身的init进程。 Busybox initt 程序对应的代码在init/init.c文件 中。其对应的流程图如右:     

如果存在/etc/inittab文件,Busybox init程序 解析它,然后按照它的指示创建各种子进程;否则使 用默认的配置创建子进程。其中,inittab文件的 每一行的格式为:id:runlevel:action:process。 process字段用来指定所执行程序的路径, 包括命 令行选项。action字段用来指定可应用到process的 动作。BusyBox init能够识别的inittab动作类型如 下表所示:    

/etc/inittab文件中每个条目用来定义一个子进程,并确定它的启动方法,格式为 :<id>:<runlevels>:<action>:<process>

例如: s3c2410_serial0:askfirst:-/bin/sh

(1)<id>:表示这个进程要使用的控制台(即标准输入、标准输出、标准错误设备)。如果省略,则使用与init进程一样的控制台。

(2)<runlevels>:对于Busybox init程序,这个字段滑意义,可省略。

(3)<action>:表示init程序如何控制这个子进程,

(4)<process>:要执行的程序,它可以是可执行程序,也可以是脚本 如果<process>:字段有"-"字符,表示这个程序被称为“交互的”。

在/etc/inittab/文件的控制下,init进程的行为总结如下:

(1)在系统启动前期,init进程首先启动<action>为sysinit wait once的3类子进程。

(2)在系统正常运行期间,init程序首先启动,<action>为respawn askfirst的两类子进程,并监视它们,发现某个子进程退出时重新启动它。

(3)在系统退出时,执行<action> 为shutdown restart ctrlaltdel的3类子进程之一或全部。     

如果根文件系统中没有/etc/initab文件,Busybox init程序将使用默认的inittab条目。    

编译/安装BusyBox

  • 获取源码。从http://www.busybox.net/下载BusyBox
  • 解压
  • 配置
    • 静态链接
    • 指定交叉编译器
版权声明:本文为ce123原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/ce123/article/details/106873314

智能推荐

Qt 之 Query Model Example 解析

总体概括 Query Model Example主要演示了怎么使用QSqlQueryModel这个数据库查询模型类。其中包括创建普通的数据库查询模型、可编辑的数据库查询模型和自定义的数据库查询模型。普通(默认)的数据库查询模型是只读的(不可再模型中编辑数据,模型只通过视图展示数据);可编辑的数据库查询模型重写了QSqlQueryModel的flags()方法和setData()方法;自定义的数据库...

Flutter:Scaffold.of() called with a context that does not contain a Scaffold.

Flutter:Scaffold.of() called with a context that does not contain a Scaffold. 当我第一次点击按钮想要弹出底部消息时出现了如下错误 当BuildContext在Scaffold之前时,调用Scaffold.of(context)会报错。这时可以通过Builder Widget来解决,代码如下:...

【机器学习基础】线性回归

                                                        &nbs...

08-Vue实现书籍购物车案例

书籍购物车案例 index.html main.js style.css 1.内容讲解 写一个table和thead,tbody中每一个tr都用来遍历data变量中的books列表。 结果如下: 在thead中加上购买数量和操作,并在对应的tbody中加入对应的按钮。结果如下: 为每个+和-按钮添加事件,将index作为参数传入,并判断当数量为1时,按钮-不可点击。 结果如下: 为每个移除按钮添加...

堆排序

堆排序就是利用堆进行排序的方法,基本思想是,将代排序列构造成一个大根堆,此时整个序列的最大值就是堆顶的根节点。将它与堆数组的末尾元素交换,此时末尾元素就是最大值,移除末尾元素,然后将剩余n-1个元素重新构造成一个大根堆,堆顶元素为次大元素,再次与末尾元素交换,再移除,如此反复进行,便得到一个有序序列。 (大根堆为每一个父节点都大于两个子节点的堆) 上面思想的实现还要解决两个问题: 1.如何由一个无...

猜你喜欢

基础知识(变量类型和计算)

一、值类型 常见的有:number、string、Boolean、undefined、Symbol 二、引用类型 常用的有:object、Array、null(指针指向为空)、function 两者的区别: 值类型暂用空间小,所以存放在栈中,赋值时互不干扰,所以b还是100 引用类型暂用空间大,所以存放在堆中,赋值的时候b是引用了和a一样的内存地址,所以a改变了b也跟着改变,b和a相等 如图: 值...

Codeforces 1342 C. Yet Another Counting Problem(找规律)

题意: [l,r][l,r][l,r] 范围内多少个数满足 (x%b)%a!=(x%a)%b(x \% b) \% a != (x \% a) \% b(x%b)%a!=(x%a)%b。 一般这种题没什么思路就打表找一下规律。 7 8 9 10 11 12 13 14 15 16 17 18 19 20 28 29 30 31 32 33 34 35 36 37 38 39 40 41 49 50...

[笔记]飞浆PaddlePaddle-百度架构师手把手带你零基础实践深度学习-21日学习打卡(Day 3)

[笔记]飞浆PaddlePaddle-百度架构师手把手带你零基础实践深度学习-21日学习打卡(Day 3) (Credit: https://gitee.com/paddlepaddle/Paddle/raw/develop/doc/imgs/logo.png) MNIST数据集 MNIST数据集可以认为是学习机器学习的“hello world”。最早出现在1998年LeC...

哈希数据结构和代码实现

主要结构体: 实现插入、删除、查找、扩容、冲突解决等接口,用于理解哈希这种数据结构 完整代码参见github: https://github.com/jinxiang1224/cpp/tree/master/DataStruct_Algorithm/hash...