卢骏 · 2020年09月05日

根文件系统 (一)根文件系统简介

一、为什么需要根文件系统
1、init进程的应用程序在根文件系统中

linux在启动之后,会去执行init进程。将自己变为应用进程,而init进程就是linux的进程1,然后再由这个进程,去生成其他的进程。

而这init进程,就是在根文件系统中,因此需要根文件系统,来向内核提供init进程

2、根文件系统提供了根目录

linux根据树形目录结构来文件管理。而这个树形目录的跟就是/,因此就需要一个东西来存储这个东西,根文件系统就是做这件事的。

linux通过根文件系统,可以对linux的文件进行管理。

3、内核启动后的应用层配置在根文件系统上

etc目录下的文件,是对linux内核进行配置的。而这些文件是存储在根文件系统中的。

4、shell命令程序在根文件系统中

linux提供的shell命令,也是保存在根文件系统中。如ls,cd这些命令,也是保存在根文件系统中。shell命令的本质,是一个应用程序。通过file命令查看的话,可以看出shell命令是一个可执行的elf。
1.png

发行版,可以认为是内核 + 根文件系统(rootfs)。如ubuntu,就是linux 内核 + 一个根文件系统(ext2,ext3)。

各个发行版,即使使用了同一个linux内核,但是因为根文件系统不一样,配置文件不一样,因此造成了各个发行版之间是有区别的。

5、总结:

只有内核本身,是不能工作的。必须要有根文件系统(/etc目录下的配置文件,shell命令,/lib目录下的库文件)配合,才能工作

二、根文件系统的实质

根文件系统是特殊用途的文件系统,和普通文件系统的区别,是提供了根目录。

根文件系统也必须属于某种文件系统格式。文件系统有多种格式,而linux也支持多种文件格式。而根文件系统也必须是属于这些文件系统中的一个。不同的文件系统之间是有差别的。在嵌入式系统中,常用的文件格式是 yaffs,ext3,jffs等

Bootloader在启动内核时,会给内核传一个rootfstype参数,就是告诉内核,根文件系统的类型是什么。这样,内核才能根据该文件系统格式,去挂载根文件系统。

存储设备(块设备,如硬盘,flash等)是分块(扇区)进行访问的,所以在物理上去访问存储设备是按照块号(扇区号)来访问的,这样直接使用就很麻烦,因为不可能记住文件在设备中的哪一个扇区。因此就考虑是用文件系统来对设备中的文件进行管理。

本质上,文件系统就是一些代码,是一套软件。这套软件的功能就是对存储设备的扇区进行管理,将这些扇区的访问变成了对目录和文件名的访问。因此对于我们,就可以通过目录和文件名,来访问设备的数据了。

我们在上层按照特定的目录和文件名去访问一个文件时,文件系统会将这个目录+文件名转换为对应的扇区号去访问。

不同文件系统的差异就在于对这些扇区的管理策略和方法不同。如坏块管理,碎片管理等等。

三、根文件系统的形式

跟文件系统,以镜像的形式存在。使用专用工具软件,即可制作可供烧录的镜像文件。每种格式的镜像文件的工具是不一样的。

镜像文件系统具有一定的格式,格式是内化的,和文件名后缀没有关系。格式是由专用的文件制作时决定的。

镜像相对比较大,其中包含了根文件系统中的所有文件。如linux的/目录下的所有目录。因此可以认为镜像是目录的打包文件。

下图,就展示了一个镜像中的 / 目录下的文件夹和文件。

2.png

烧录此镜像类似于对相应分区格式化

烧录镜像后,会将存储设备进行格式化。其实就是将镜像的内容写入到存储设备中。这样,linux内核就可以挂载该镜像。

镜像文件形式的根文件系统主要目的是用来烧录到存储设备上,设备上的内核启动后会去加载它。

镜像文件形式的根文件系统是由文件夹形式的根文件系统使用专用的镜像制作工具制作而成的。

最初在开发主机中随便mkdir创建了一个空文件夹,然后向其中添加一些必要的文件(包括etc目录下的运行时配置文件,/bin等目录下的可执行程序,/lib目录下的库文件等。。。)后就形成了一个文件夹形式的rootfs。然后这个文件夹形式的rootfs可以被kernel通过nfs方式来远程挂载使用,但是不能烧录到块设备中。

我们为了将这个rootfs烧录到块设备中,于是会使用一些专用软件工具将其制作成可供烧录格式的根文件系统镜像。

文件夹形式的rootfs是没有格式的,制作成镜像后,就有了一定的文件格式,格式是由镜像制作过程和制作工具来决定的。每一种格式的镜像制作工具的用法都不同。

四、制作一个简单的根文件系统

使用的工具:

mke2fs : ubuntu中的默认安装的应用程序,这个应用程序用来制作ext2,或者ext3,或ext4等格式的根文件系统。

制作ext2格式的rootfs的工具叫mke2fs.ext2, 制作jaffs2格式的rootfs的工具叫做mkfs.jffs2。

以下是命令:

dd if=/dev/zero  of=rootfs.ext2 bs=1024 count=2048
创建一个空文件,大小是2048*1k=2M。
losetup  /dev/loop1 rootfs.ext2
使用循环设备将文件rootfs.ext2虚拟化成块设备
mke2fs –m 0 /dev/loop1 2048
在虚拟化的块设备上,制作ext2文件类型的根文件系统
mount –t ext2 /dev/loop1  ./rootfs/
挂载创建的文件系统到目录rootfs上。

此时查看rootfs目录,可以看到有一个文件,然后再这个目录下,使用touch命令创建一个linuxrc普通文件。
3.png

这样,就创建好了一个ext2格式的根文件系统。并且该系统下,会有linuxrc文件,linux内核启动后,会去加载这个跟文件系统,然后去找到linuxrc文件,并执行。

使用下面两个命令,对刚刚挂载的文件系统进行卸载。

umount /dev/loop1

losetup -d /dev/loop1

下载到开发板中,执行。可以看到该根文件系统有被挂载,这里是当成ext3文件格式挂载了,那是因为bootargs参数里面指定的根文件系统类型是ext3。

但是执行linuxrc失败了,从而造成启动失败。
4.png

五、nfs方式制作根文件系统
1、nfs

nfs是一种网络通讯协议,由服务器和客户端构成。

nfs的作用可以让客户端和服务器进行网络通信。这里面使用nfs主要是做rootfs的挂载。

开发板中运行linux kernel做客户端,主机ubuntu搭建nfs服务器。在主机ubuntu的nfs服务器中导出制作文件夹形式的根文件系统,然后再客户端去挂载这个文件夹形式的rootfs。

2、nfs设置

安装nfs
5.png

修改配置文件
6.png

修改后,/etc/exports文件内容为以下:
7.png

启动NFS复位
8.png

挂载测试
10.png

也可以使用ip地址进行挂载。
11.png

上述,就创建了一个nfs的根文件系统,在/root/rootfs目录中。

3、开发板的nfs设置

设置开发板的boorargs参数,让linux内核,支持挂载nfs。

setenv bootargs "root=/dev/nfs nfsroot=192.168.1.141:/root/rootfs/ ip=192.168.1.88:192.168.1.141:192.168.1.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC2,115200"

◾root=/dev/nfs 告诉内核,根文件系统在nfs上
◾nfsroot=192.168.1.141:/root/rootfs 告诉内核,nfs的目录的位置
◾ip=192.168.1.88:192.168.1.141:192.168.1.1:255.255.225.0::eth0:off 指示开发板的ip地址是192.168.1.88, 主机的ip地址是192.168.1.141,网关是192.168.1.1,掩码是255.255.255.0。后面的参数是固定的
◾init=/linuxrc 指示linuxrc文件位置
◾console=ttySAC2,115200 使用串口2,波特率是115200.

运行开发板,内核启动的时候会报以下错误:

VFS: cannot open root device "nfs" or unknown-block(0,255)

please append a correct "root=" boot optin: here are the available partitions:

12.png

这个错误的原因是因为内核不支持nfs启动,也就是编译内核的时候,没有开启nfs服务。

内核需要配置支持nfs。否则是不能挂载nfs的根文件系统。

执行make menuconfig。
13.png

将networking support->networking options下的图中三个选项打开。

将file systerm->network file systems下的下面选项打开。
14.png

然后重新编译linux内核,这样linux内核就支持nfs挂载根文件系统了。
15.png

但是会打印以下信息,成功挂载了nfs根文件系统,但是执行linuxrc文件失败。因为这个根文件系统不是一个可以完整的文件系统。

[5.150447] eth0: link up, 100Mbps, full-duplex, lpa 0x4DE1

[5.154656] ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready

[5.597114] Looking up port of RPC 100005/1 on 192.168.1.141

[5.825916] VFS: Mounted root (nfs filesystem) on device 0:12.

[5.830359] Freeing init memory: 540K

[5.876111] Failed to execute /linuxrc. Attempting defaults…

[5.915162] Kernel panic – not syncing: No init found. Try passing init= option to kernel. See Linux Documentation/init.txt for guidance.

六、linuxrc
1、/linuxrc是一个可执行的应用程序

/linuxrc是应用层的,和内核源码没有关系。

/linuxrc在开发板当前内核系统下是可执行的。因此在ARM soc的linux系统下,这个应用程序就是用arm-linux-gcc编译链接的,如果是在PC机上linux系统下,这个程序就是用gcc编译链接的。

/linuxrc如果是静态编译链接的,那么直接可以运行;如果是动态编译链接的,那么还需要提供必要的库文件才能运行。但是因为/linuxrc程序是由内核直接调用执行的,因此用户没有用户去导出库文件的路径,因此实际上这个/linuxrc没有办法动态链接,一般都是静态链接的。

/linuxrc是被当做进程1来运行的。

2、/linuxrc执行时引出用户界面

操作系统启动后,在一系列的配置之后,会给用户提供操作界面(cmd或者gui)。这个用户界面就是/linuxrc中带出来的。

操作界面,不是/linuxrc程序中负责的,而是其他专门的应用程序来负责。但是该应用程序是直接或者间接的被/linuxrc调用执行的。

也就是进程1(init进程,也就是/linuxrc)是其他所有应用程序的父进程。

3、/linuxrc负责系统启动后的配置

linux启动后,需要配置,才可以被使用。而这配置的过程,就是靠/linuxrc来完成的。

linux启动后的应用层的配置(一般叫运行时配置,英文称为etc)是为了让操作系统使用起来更方便,更具有实用性。

4、/linuxrc在嵌入式linux中一般就是busybox

busybox是一个c语言写出来的项目,里面包含了很多了.c文件和.h文件。这个项目也是被配置编译成各个平台下的可以运行的用于程序。

如果使用arm-linux-gcc来编译busybox就会得到一个可以在arm开发板linux内核上运行的应用程序。

busybox这个程序开发目的是为了在嵌入式环境下构建rootfs使用的,也就是说专门开发的init进程应用程序。

busybox为当前系统提供了一整套的shell命令程序集。如vi,cd, mkdir, ls等。

在桌面版的linux发行版中(如redhat,ubuntu等)中vi,cd,ls这些命令都是一个个独立的应用程序。但是在嵌入式linux中,为了节省空间以及提高效率,将vi,cd等这些常用的shell命令集合到一起构成了一个shell包,叫busybox。

七、rootfs中还需要什么

以下是我的ubuntu发行版的根文件系统目录。有很多目录,但是对于嵌入式,不是所有的目录都是必须的。

1.png
其中,对于嵌入式,最小的根文件系统,必须有的是:

目录说明
dev存放设备的文件。因为linux中,一切皆文件,因此一个硬件设备也被虚拟化成一个设备文件来访问。在/dev/xxx就表示一个硬件设备,要操作这个硬件时就open打开这个设备文件,然后read/write/ioctl操作这个设备,最后close关闭这个设备。
sys里面可以是空的,但是必须有。和驱动有关,属于linux中的虚拟文件系统。
proc里面可以是空的,但是必须有。和驱动有关,属于linux中的虚拟文件系统。
usr系统的用户所有的一些文件的存放地。里面的内容,busybox安装的时候会自动生成。
etc目录中的所有文件都是运行时配置文件。这里面的配置文件会直接或者间接的被/linuxrc所调用执行,完成操作系统的运行时配置。该目录是制作rootfs的关键。
lib存放当前linux中的动态和静态链接库文件以及相关的头文件。
八、VFS
1、VFS简介

在linux启动的时候,会出现VFS的打印信息。
2.png

VFS是linux内核的一种设计理念、设计机制。VFS就是virtual file system,叫虚拟文件系统。

有具体的一些文件系统,如FAT,NTFS, ext2,ext3,jffs2,yaffs2,ubi等。这些设计的目的是为了管理块设备(硬盘,nand等)。

VFS是借鉴了文件系统的设计理念(通过文件系统将底层块设备扇区式访问,转化为目录+文件名的方式来访问),将硬件设备的访问也虚拟化成了对目录+文件的访问。所以有了VFS,就可以通过设备文件(目录+文件名,如/dev/mmcblk0p2)的方式来访问系统中的硬件设备。

VFS并不是一种实际的文件系统。只存在于内存中,不存在于任何外设空间。VFS在系统启动时建立,在系统关闭时消亡。

2、VFS的意义

对硬件设备的访问,和普通文件的访问的接口,进行了统一。这样才实现了linux中,一切皆是文件。

将操作系统的上层(应用层)对下层不同文件系统类型的访问细节给屏蔽掉了。这样,我们使用cp命令实现复制文件的时候,就不用考虑不同文件系统的文件的兼容性文件。有了VFS后,实现就要容易多了。VFS成了一个隔离层,隔离了下层的不同文件系统的差异性,对上层应用提供一个统一的接口。

如下图,应同层,通过调用VFS提供的统一接口,能实现不同文件系统之间的数据交互。
3.png

3、总结

VFS机制和rootfs挂载,和其他文件系统的挂载是有关联的。

内核中有一些sys proc这种虚拟文件系统,这些是和VFS机制有关的。

/dev/目录下的设备文件都和VFS有关。

更多相关阅读

奔跑吧,SOC(三) 互联总线协议
linux module工具安装与使用
sv使用default clocking指定全局默认时钟

原文首发于骏的世界博客
作者:卢骏
更多IC设计相关的文章请关注IC设计极术专栏,每日更新。

推荐阅读
关注数
10973
内容数
1216
主要交流IC以及SoC设计流程相关的技术和知识
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息