(2020.11.19 Thur)
文件系统的终极目标是把大量数据有组织的放入外部存储设备中。Linux通过文件系统来管理外部存储器。常见的分类有ext2fs、ext3fs和ext4fs。Windows采用的是FAT文件系统。NFTS常用于网络存储器。无论哪个存储系统,都有三方面功能
- 通过名字和层级来组织文件,比如文件名和路径
- 提供操作文件的接口,比如查找、新建、删除、读取和写入文件
- 提供权限功能,比如文件保护和文件共享
一个外部存储器划分成一个或多个分区(partition),每个分区可以用一种文件系统格式来管理。树莓派的Raspbian镜像烧制完成后,SD卡的存储空间就会划分成两个分区。一个空间是启动分区,采用了FAT32形式的文件系统,启动分区主要用于开机启动,空间小。另一个空间是主分区,采用ext4fs的文件系统。
外部存储器的挂载
尽管树莓派在SD卡上有两个分区,但是开机只能看到以根目录/为起点的文件系统,即两个物理分区以某种形式合并到了Linux的文件树上。这个过程称为挂载(mouting),即让文件树上的某个目录和存储器的物理分区对应起来。这个目录称为挂载点(mouting poi nt)。从挂载点开始向下的子文件树,就对应了挂载的物理分区。
Linux系统的挂载信息都保存在文件/etc/fstab中。树莓派的SD卡的主分区挂载在根目录/上,而启动分区挂载在根目录下的/boot上。这两棵文件树有重叠的从属关系,以根目录/为起点的文件树,包括了以/boot为起点的文件树。此时,Linux以子文件树优先,把启动分区挂载在/boot上。主分区的挂载点是根目录/,但不再包括/boot子文件树。因此/boot下的数据都会存放于启动分区,其他的数据存放于主分区。
一个外部存储器必须经过挂载,才能加入操作系统的文件树。只有加入文件树后,应用程序才能通过文件系统来访问外部存储器中的数据。在树莓派中插入一个优盘,用fdisk命令可以找到这个优盘
$sudo fdisk -l
它会自动挂载到/media下的一个目录上,比如/media/my_driver,往该目录上存入的文件都会存入优盘。如果对系统自动分配的挂载点不满意,可以卸载优盘,再挂载到一个自定义的挂载点。
$sudo umount /dev/sda1 #卸载优盘
$sudo nano /etc/fstab #设置默认挂载点为/etc/fstab,使用nano工具编辑这个文件,该文件中每一行都是一个设备的挂载设置
/dev/mmcblk0p7 / ext4 defaults,noatime 0 1
文件中每行有6个参数,含义如下
- 设备名称,一般是/dev/xxx
- 挂载点,对于USB设备默认是/media/xxx
- 文件系统格式,e.g.,ext4
- 设备参数,如defaults,noatime
- 一个不再使用的参数,通常设置为0
- 磁盘检测设置,1位根文件系统,2为永久挂载磁盘,0为可插拔的移动设备
挂载完成后,可以用df命令来查询文件系统的挂载情况
$sudo df
存储器开始部门的块会有一个总的分区表(partition table),记录着存储器的基本信息,比如块(block)的大小、存储器的编号和可用空间。块是存储器的读写单元。即使一个文件小于一个块,它还是会占据这个块的完整空间。分区表中还逐项记录每个分区的信息,包括分区的起始位置和大小。随后的存储空间划分成分区。不同的分区可以采用不同的文件系统。
ext文件系统
(2020.11.20 Fri)
ext4全称第四代拓展文件系统(4th extended file system),从ext到ext4都是专门为Linux内核开发的。这四代系统的共同特征是围绕inode来组织文件。
boot block | super block | inodes | data block |
---|
装有ext分区的第一个块是引导块(boot block),其中含有引导加载程序(boot loader),帮助计算机在开机时加载Linux内核。引导加载程序存储有内核的相关信息,如内核名称和内核所在位置。但在树莓派中,FAT32的启动分区负责开机启动,树莓派的引导加载程序也在启动分区,树莓派的ext主分区并没有引导块。
每个ext分区有个超级块(super block),记录着文件组织的信息,包括文件系统类型,inode的数目,块总数和空闲数量等。如果超级块损坏,会导致整个分区的文件系统损坏。
inode表中含有多个inode,inode是描述文件存储信息的数据结构,文件有一个对应的inode,每个inode有一个唯一的整数标号(inode number),可用下面指令查询inode编号
$stat example.txt
该指令返回了文件的附属信息,包括文件大小,拥有人,拥有组,修改日期等,这些信息就存在inode中。inode还存有文件包含的所有数据块的位置信息,即指向数据块的指针。在Linux系统中,一个大文件可以分为几个数据块存储。当Linux想打开一个文件时,先找到文件对应的inode,根据inode这张地图的指引将所有的数据块收集起来,才能拼凑出一个完整的文件;通过解析路径,根据沿途的目录文件来找到某个文件。目录文件的每个条目对应了一个子文件的文件名,以及该文件的inode编号。
当写入一个文件时,Linux系统会分配一个空白inode给该文件,将其inode编号记入该文件所属的目录,然后选取空白的数据块,让inode指针指向这些数据块,并向内存中放入数据。
FAT文件系统
FAT32格式的树莓派启动区,有一个引导块,用于在开机时加载Linux内核。引导块之后是文件分配表(FAT, file allocation table),其组织形式和inode不同,但起到了类似的作用。
文件分配表按顺序对应了所有的数据块。在其第一条记录中,说明了同一个文件中下一个数据块的位置。当一个数据块是文件的最后一个数据块时,它就不再有下一个数据块了。它在文件分配表中的记录,会填写成固定的0xffff。只要知道了一个文件的起点数据位置,就能根据文件分配表找到该文件的所有数据块。
FAT文件系统还有一个区域专门记录FAT根目录信息。其他的子目录则以文件的形式保持。目录中的每条记录对应了一个文件,除了文件名和属性,还记录了文件的起始数据块的位置。从根目录出发,可通过记录中起始数据块的位置,配合文件分配表来组装根目录下的子目录和文件。以此类推,可以找到整个FAT文件系统的文件。
ext和FAT的对比
ext的组织形式着眼于文件,inode为主要的中间层。FAT的组织形式着眼于数据块,以文件分配表FAT为主要的中间层。FAT的文件分配表的记录总数和数据块总数相同,可能会很占空间。FAT必须按照顺序一个一个找数据块,没法像ext那样从inode中获得一张完整的地图。因此当文件在存储器上比较零散时,FAT没法像ext一样优化读写路径,但由于windows系统的成功,FAT文件系统依然应用广泛。
文件描述符File descriptor
在Linux系统中,当打开一个文件时,会获得一个整数来代表该文件,该整数称为文件描述符。其中有一个文件描述符表,记录了该进程所有已经打开的文件。fd说明了目标文件在文件描述符表中的位置。文件描述符表的每条记录包含了一个指针。这个指针并没有直接指向文件的inode,而指向一个文件表(file table)。文件表中的指针指向加载到内存中inode,也就是目标文件的inode。从文件描述符出发,首先找到文件描述符表中的记录,再找到文件表,然后找到文件的inode,最后抵达数据块。一个进程打开了两个文件。
每个文件表中记录着状态表示(status flag),比如只读、读写等。文件打开状态是在打开文件时由应用程序决定的。文件表中还记录了当前读写位置。当有两个进程打开同一个文件时,每个进程都会有一个文件表。因此,即便是两个进程同时打开一个文件,在不同的进程中,同一个文件会有不同的状态和读写位置。
注意fork对文件描述符的影响。当进程fork时,子进程将只复制文件描述符表。子进程文件描述符表中的指针,还是指向父进程的文件表。这样父进程和子进程将共享文件打开状态和读写位置。当父进程和子进程同时操作已打开文件时,有可能会互相干扰,这种情况下写程序应小心。
Reference
1 Vamei,周梓昕著,树莓派开始玩转Linux,中国工信出版集团,电子工业出版社
网友评论