「这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战」。
文件系统是管理存储介质的系统。
怎么理解存储介质
存储介质有很多,比如磁盘,通过它们,数据才能被我们识别并持久化为有用的东西。
- 磁盘物理形态
- 磁盘逻辑形态
可以认为,磁盘是01二进制数组,忽略更多的物理形态,可以认为磁盘就是由一个个的扇区组成的。
文件系统是管理磁盘的系统,如下图所示:
- 1个扇区是512KB
- 每个扇区都有一个编号
- 用下面命令可以看到磁盘设备的情况:
# fdisk -l
磁盘 /dev/vda:107.4 GB, 107374182400 字节,209715200 个扇区
Units = 扇区 of 1 * 512 = 512 bytes
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0xf6abafec
设备 Boot Start End Blocks Id System
/dev/vda1 63 209712509 104856223+ 83 Linux
复制代码
可以看到磁盘 /dev/vda大概100G的存储空间,一共有2亿多个扇区.
文件系统的组织管理能力
文件系统要实现管理文件的功能。 上面描述了文件系统的逻辑形态,就是一个大数组。
那么文件系统是怎么管理这个数组的?
也就是说:
- 上层功能:管理文件
- 下层实现:管理存储介质(块设备)数组中的存储单元
所以,文件系统其实就是做了这样的事情:怎么通过下层管理来实现上层功能。
基本能力
文件系统作为外部设备,和中央系统(内存)交互数据,需要设计成和内存交互匹配的大小, 因为内存页大小为4kB,所以设计磁盘的block(基本单位)大小也为4kB。 查看:
# stat -f /
文件:"/"
ID:336e5f556022d7bd 文件名长度:255 类型:ext2/ext3
块大小:4096 基本块大小:4096
块:总计:25769967 空闲:5032408 可用:3927408
Inodes: 总计:6553600 空闲:4886167
# blockdev --getbsz /dev/vda1
4096
复制代码
假设我们要自己实现一个简单的文件系统, 如下图所示,假设目前磁盘一共有32个blocks的存储空间:
实现文件系统就是要满足存储文件的功能:
- 现在要存储文件file1,这个文件内容占了磁盘的8个block
- 存储数据内容的称为data node
- 存储文件内容的block不一定连续,选择空闲的用
- 但是去哪里找这个文件呢?
- 需要设计一种索引来管理。
- index node,也就是iNode,用来存储文件名称,权限、创建时间等信息,
- index node 存储指向所有data block的索引。
- 假设一个inode占256B,则一个block可以存放16个iNode。
实现的更多细节
BITMAP
虽然设计了inode和datanode,
- 还需要划分inode和data node的范围,
- 为了更好的找到idode和data node,还需要记录这些inodes和data blocks的分配和释放情况
- 当有了新的数据产生时,我们需要选择一个空闲的block来存放数据,此外还需要一个空闲的inode。
使用bitmap判断哪些是已用的,哪些是空闲的。空闲就标记为0,正在使用就标记为1
为什么要使用bitmap呢?因为读取更少的数据就能知道哪个block快空闲,效率更高
如图,可以看出
- 一个block可以存储4096*8个bit信息,两个block的bitmap可存储6万多
- 记录inode使用情况的bitmap,实际才3*16个inode,完全够用
- 记录data block使用情况的bitmap,实际才24个data node ,也完全够用
可见bitmap占有空间极小,100G文件系统600多万inode只需要大概20个block,也就是80k的空间。
Super Block
superblock用来记录文件系统的一些基本元数据信息。
- 文件系统类型
- 分区信息:扇区的start和end号码
- iNodes数量、data blocks的数量
- index block的范围
- data block的范围等
- bitmap范围
多种文件类型
为了更好的用户体验,文件系统还支持其他文件类型,比如目录和链接。
磁盘上的文件
通过ls -l查看文件
# ls -li
drwxr-xr-x 2 root root 4096 11月 23 08:08 dir
-rw-r--r-- 3 root root 6 11月 21 14:33 file1
-rw-r--r-- 3 root root 6 11月 21 14:33 file1_hard
lrwxrwxrwx 1 root root 5 11月 21 14:36 file1_soft -> file1
复制代码
- 普通文件类型
- 刚刚存储的文件file1就是普通文件
- 这事linux中最多的一种文件类型, 包括 纯文本文件(ASCII);二进制文件(binary);数据格式的文件(data);各种压缩文件等。
- ls -l第一个属性为-。
- 目录文件类型
- 第一个属性为d,可通过cd进入的
- 目录其实就是一个inode
- 这个inode指向的是其他inode(目录下的文件或者目录)
- 软链接
-
soft link,第一个属性为l
-
实现是:创建一个新的inode,指向一个老的inode
-
其他文件类型
下面这些类型对于本文来说不重要,因为它们不属于文件系统管理的文件类型,而是各种驱动或内核管理的。
- 字符设备文件:即串行端口的接口设备,例如键盘、鼠标等等。第一个属性为c。
- 块设备文件:即存储数据以供系统存取的接口设备,也就是我们这里说的存储介质
- socket文件:代表网络客户端和服务端的读写的文件
底层管理的更多细节
块设备分区-fdisk
分区是将存储块设备按照物理地址分为几个可独立管理的子区域,
# fdisk /dev/vda1
欢迎使用 fdisk (util-linux 2.23.2)。
更改将停留在内存中,直到您决定将更改写入磁盘。
使用写入命令前请三思。
Device does not contain a recognized partition table
使用磁盘标识符 0xf65964ec 创建新的 DOS 磁盘标签。
命令(输入 m 获取帮助):m
命令操作
...
# mkfs -t ext4 /dev/vdb1
复制代码
可见这个过程就是
- fdisk指定一个块设备文件
- 从块设备文件创建一个分区n
- 选择是否主分区
- 选择分区号
- 选择第一个扇区
- 选择最后一个扇区
mkfs -t ext4 /dev/vdb1
初始化这个分区为ext4类型的文件系统- mount 这个文件系统到某个目录
那么,怎么理解挂载呢?
挂载
如下所示:/和/data是两个挂载点。
# df -hT
文件系统 类型 容量 已用 可用 已用% 挂载点
/dev/vda1 ext4 99G 80G 16G 85% /
/dev/vdb1 ext4 394G 6.5G 368G 2% /data
复制代码
怎么理解这个挂载点呢?
- 块设备是设备的驱动程序(内核)识别和初始化的
- /是必须要挂载的,不然这个目录就没办法使用了,其他目录倒也还好
- /data是/dev/vdb1的挂载点,意思是说在/data下面创建的所有文件和目录的数据,都会使用/dev/vdb1下的blocks来存储
- 存储数据的时候,child目录的挂载点优先级>child目录的挂载点优先级,意味着 /data/file会存储到/data的挂载点而不是/的挂载点,也就是实际存储在/dev/vdb1。
操作系统和文件系统的关系
- 操作系统启动后会挂载文件系统。
- 操作系统镜像存在于磁盘上(boot盘),这个是特殊的,在没有挂载的时候就能启动。
能力更上层-VFS
文件系统在实现的细节上可能各有不同,我们格式化分区的时候是可以选择不同的文件系统的。 通过命令你也看到我们的系统里面其实存在各种各样的挂载点和文件系统,但是不管读取哪一个,我们都是通过同样的方式,vim读写,cat读,echo写。
这是因为,文件系统有一个统一的抽象VFS,如下图:
VFS提供了统一的上层接口 去读写文件,这样应用程序就可以通过简单统一的方式去访问各种文件系统里面的文件了。