【Linux 系统】文件系统与软硬件连接
文章目录
- 前言
- 1. 磁盘空间的分区管理
- 1.1 分治
- 1.2 小区域
- 1.2.1 inode
- 1.2.2 block group
- 2. 文件操作相关问题
- 2.1 理解目录
- 3. 软硬连接
- 3.1 硬链接
- 3.2 软连接
- 补充
- 文件系统在创建文件的时候进行了哪些底层操作?
前言
在计算机系统中,文件分为两类:
-
被打开的文件。
-
未被打开的文件。
我们接下来主要探讨的内容就是:从操作系统的视角来看一个被打开的文件是如何在磁盘中存储的?(逻辑存储的方式)
主要是基于 ext2/ext3/ext4 文件系统的磁盘布局
-
共识:
文件 == 文件属性 + 文件内容
在 ext2/ext3/ext4文件系统的存储架构之下,我们存储的方式是通过文件属性和文件内容分开存储的方式。
- 文件属性:inode模块
- 文件内容:数据块。
详情请看后面。
1. 磁盘空间的分区管理
物理上的磁盘空间以4KB的大小作为一个扇区,这是访问磁盘的最小单位。其中还有扇区……概念。但是对于操作系统来说,他根本就不认识这些东西,从逻辑上我们操作系统认为其就是一个线性地址(通过LBA可以转化称为物理访问地址)
- 操作系统描述磁盘区域也很简单了:
struct eare
{long long start;long long end;
}
// ...
1.1 分治
下面我们可以磁盘空间512G为例。总共512G的磁盘空间如果整体管理起来就特别麻烦,此时我们操作系统采用的模式就是:分治。
-
我们将大的区域分区为多个小不同的区域,分别以同样的方式管理下层的磁盘空间。
我们可以不止分区分两个区域,我们也可以分多个区域。
例如:Windows下的分区
-
注意:
这个分区是文件系统上的对区域管理的分区。但是对我们在Linux上的根目录没有直接关系。分区是底层存储划分,通过挂载映射到目录树。
意味着:同一个根目录下的文件可能存在于不同的分区。-
Linux 只有一棵目录树,根目录
/
是起点。 -
分区是物理概念,对应 /dev/sda1 这样的设备文件
-
挂载是逻辑关联,将分区"附加"到目录树的某个位置
-
用户无感知:访问 /home/user 时,可能实际访问的是完全不同的分区
这种设计使得 Linux 的文件系统更加灵活和统一,用户不需要关心文件具体存储在哪个物理设备上
-
1.2 小区域
现在我们聚焦在一个小区域,我们通过分区域之后,但是这个区域还是很大,所以:我们还可以对这样的区域进行划分:
如果划分得到,我们就可以对下层的磁盘空间进行管理了。事实上,我们将每一个分出来的小区域被称为:Block Group。这个小区域都是还有组的编号,所以,下层管理,我们管理好了一个 Block Group 也可以管理好其它的磁盘数据。
如下图,这就是文件系统管理小区域的结构:
1.2.1 inode
在正式介绍Block group中的字段之前,我们先来认识一个非常重要的属性:inode。
在上图给出的 inode Bitmap和inode Table都谈到了inode
inode 是一个存储磁盘中文件属性的“数据块”。即:每一个 inode 都存储着单个文件的属性(一般是128字节)。同时:一般而言一个文件分配一个 inode;文件系统中全局唯一inode。
在磁盘的视角中:
- inode就是序列化的格式块。
在Linux内核视角中:
- inode就是一个
struct
结构体
struct inode
{int inode_id; //inode编号//文件类型//文件权限//计数引用//拥有者//所属组//ACM时间int block[15]; //数据块存储的位置。假设是15个
};
注意:
-
Linux内核不识别一个文件的文件名,只识别 inode编号。我们能保证一个文件的 inode 编号在分区中是唯一的。
为什么是分区唯一,而不是全局唯一?因为不同的分区我们可以为其添加一个属于哪一个分区的字段,操作系统可以同个 分区+inode编号 从而表示一个文件inode的唯一性。
-
并不是所有的没有被打开的文件都会在内核中创建
struct inode
。而是当一个文件要被打开的时候,Linux 会先读取在磁盘中的inode属性内容创建struct inode
再去创建struct file
……(后面我们会详谈)
1.2.2 block group
下面我们来介绍一个 block group 中的字段。
-
Boot Block
一个在整个文件系统最开头(仅第一个块组有)的数据块。一般大小通常是1KB。其作用是存储系统引导程序
-
inode Table
其中就是磁盘中存储 inode 的区域。其中存储中大量的 inode 信息。其中的 inode 都有唯一的编号。
-
Data blocks
数据块。这是文件系统中磁盘存储文件内容的区域。以块的形式进行存储数据内容(一般而言是4KB大小,这也是磁盘硬件读取数据的最小单位)和 inode 类似的,每一个数据块都有自己对应的编号。
-
存储方式:
在上文提到了操作系统描述 inode 中有一个字段
int block[15]
(这里是假设有15个内容)。其中我们可以认为:-
[0, 11] 的内容中的可以直接指向数据块(Data blocks)的指针(我们称其为:一级索引),这些数据块存储着文件的内容。
-
[12, 13] 的内容存放的也是一个指向数据块的指针(我们称其为:二级索引)。这些指针指向的内容是一个数据块。而数据块中存储的内容不再是文件内容了,而是存储的是一级索引。这个时候再同个一级索引,找到对应的文件内容。
-
[14, 14] 内容中存放的也是指针(三级索引),指向的内容就是二级索引。通过二级索引就能找到一级索引最后找到存储文件内容的数据块。
这么存储的好处是什么呢?当文件内容不大的时候通过一级索引就能找到文件内容,当文件内容较大的时候,我们又可以通过这样的多级映射的方式扩充一个文件能够指向的数据块。能够存储更多的内容!
-
-
-
inode Bitmap
位图映射。一个关于 inode 的位图结构,其中标记着一个映射关系的 inode 是否被使用:0表示空闲,1表示使用。我们上面谈到了每一个 inode 都有自己的唯一编号,通过一定的算法,我们能够使得达到判断一个 inode 是否被使用了。
-
Block Bitmap
数据块映射。一个关于数据块的位图结构,其中标记着一个映射关系的数据块是否被使用:0表示空闲,1表示使用。
-
通过这样的一个位图映射就有一个好处:
当我们要删除一个文件的时候,我们只需要将对应文件所用的数据块中的 Block Bitmap 映射位置设置为0即可,不需要再去清空数据块中的内容。这样可以极高的提高删除文件的成本,同时也不会影响下一次使用数据块。
-
-
Group Descriptor Table
用于描述一个组的基本信息:整个组的情况(一共有多少个数据块?一个有多少个数据块被使用了?还有多少没有被使用?)
-
SuperBlock
超级块,其中存储着整个文件系统情况,例如包含一些重要的字段:名称、格式、类型、一共有多少个组、每个组的大小、每个组inode的数量、每个组block的数量每个组的其实inode……
所以:
- super block中存储的信息都是一些非常重要的信息。我们不能让这样重要的信息没有备份,所以一般而言:super block 不会只出现在一个组中,还会有其它组备份。如果一个组的 super block 异常了,文件系统就可以即使修复。
上面每一个分区都可以以类似的方式进行管理。每一个模块都有自己的功能。
补充:
-
上面的很多数据内容都是在描述一个分区/一个组的使用情况的,所以在一个分区在被使用之前一定需要先初始化这些描述信息,这就是格式化:
2. 文件操作相关问题
下面我们就会根据上面谈到的文件系统的管理来回答下面几个问题,加深印象。
(我们从用户角度出发:不再关心分区和分组细节。只关心系统为文件分配的inode)
因为:分区和分组对用户来说都是透明的!
系统是通过解析文件路径的过程,自然就知道了它属于哪个分区
-
新建一个文件,系统需要做什么呢?
(具体如何确定分区和确定分组我们不关心,写在这里只是为了更完整)
- 系统需要确定创建文件的分区。
- 再根据 Group Descriptor Table 找到能用空间 inode 的组。
- 再通过 inode bitmap 找到对应的 inode 将文件的属性信息写入。
- 如果没有写入内容,可以不用先分配数据块存储内容。如果写入了内容,则根据 block bitmap 找到空闲的数据块将内容写入。
-
删除一个文件,系统需要做什么呢?
- 删除一个文件,需要先找到文件的 inode 编号。
- 再找到对应的 inode 属性信息,将其对应的内容信息中的 block bitmap 中的表示修改为0。
-
查找一个文件,系统需要做什么呢?
- 查找一个文件,需要先拿到文件的 inode 编号。
- 然后再通过 inode 编号找到文件的 inode,读取 inode 属性返回即可。
-
更改一个文件,系统需要做什么呢?
- 先找 inode 编号,再找 inode
- 再修改属性/内容……
上面4个问题都面临着一个问题:先要拿到文件的 inode 编号。可是在用户的视角里从来没有提供过什么编号!而是路径/文件名……
2.1 理解目录
理解了目录就能理解上面这个问题了……
目录也是文件。一个 文件=文件属性 + 文件内容。那么一个目录有内容吗?
-
一个目录当然有内容,其内容不就是在该目录下的子目录/文件吗!在用户看来,存放着下级目录和文件名。但是在系统看来目录的内容是文件名和inode的映射关系!
这就解释了几个问题:
-
一个文件的 inode 编号是如何找到的呢?通过存放该文件的目录找到的……
-
为什么一个目录下不能用同名文件?因为目录内容的文件名和 inode 映射关系是多对一的。
-
为什么目录没有"w"权限不能创建文件?因为没有该权限不能向文件中写入映射关系!
-
为什么目录没有"r"权限不能读取该目录下的信息?因为没有该权限不能获取一个文件的 inode 编号就找不到文件的 inode。
-
为什么目录没有"x"权限不能进入该文件?因为没有该权限,目录的inode就无法被用户读取到!
-
-
目录的 inode?
目录也是文件,所以目录也有自己对应的 inode 和 inode 编号。那么目录的 inode 编号又从哪里来呢?目录也是文件也有上级目录,上级目录也是文件……最后我们就找到了根目录。从根节点向下就是一条绝对路径。同时也意味着:每次读取路径都是一次递归的过程。但是系统中有缓存机制,可以缓解效率问题。
所以:Linux下对一个文件进行操作一定是通过绝对路径进行操作的,只是对用户来说是不可见的。
3. 软硬连接
使用下面指令:
ls -li
我们可以查看当前目录下的文件的 inode 编号和硬链接数。
3.1 硬链接
小编先查看当前路径下的一个硬链接情况:
- 其中红色方框就是表示当前文件的硬连接数。
- 最前面的一串数字就是代表这个目录文件的 inode编号。
这里留一个问题:为什么这个两个目录文件的硬链接数是2呢?(硬链接的应用场景)
-
硬链接的创建
指令:
ln [被连接的文件] [连接的文件]
注意:可以带路径。
案例:
观察两个文件,他们有相同的 inode 编号和硬链接数。
说明:
-
硬链接不是一个独立的文件,因为其没有独立的inode编号。所以对一个文件进行写入,“另外”一个文件也就被写入了
-
所谓硬链接,实际上就是在特定的目录中的数据块中添加文件名和 被连接文件的inode 编号的一个映射关系。任何一个文件都有唯一的inode,那么硬链接就是在对应的 inode 中所维护的技术引用进行操作。
-
应用场景:
每一个目录下面都有一个隐藏文件[
./..
]。这两个隐藏文件都是硬链接。一个是本目录的硬链接,一个是上级目录的硬链接,是用于路径定位的!所以:每一个目录文件的默认的硬链接数都是2,因为上级目录中有一个本目录的 inode,同时自己目录下有一个自己文件的硬链接。
注意:
-
Linux 不允许用户为目录创建硬链接,这是为了避免用户创建的目录硬链接导致了系统在路径查找时候的死递归,导致文件系统出问题。
-
Linux 下创建硬链接不能分区域!因为 inode 编号是在分区中唯一。
3.2 软连接
-
软连接的创建
指令:
ln -s [被连接的文件] [连接的文件]
案例:
说明:
-
软连接是一个独立的文件。拥有一个独立的 inode 编号。数据块中的内容就是指向连接文件的路径。换句话说:就是相当于在 Windows 下创建了一个快捷方式。
-
应用场景:
为用户提供便捷方式。不需要带路径访问。例如:我们可以将我们自己写好的可执行程序软连接一个文件到系统路径的
/usr/bin/
路径下,此时当我们执行我们的代码的时候,就不需要带路径了!
例如:不过需要注意的是:我们创建软连接时候带的路径需要时绝对路径!我们软连接中存储的就是相对路径。
补充
文件系统在创建文件的时候进行了哪些底层操作?
-
分配 inode
根据现有内存缓冲的 super block 信息中得到一个合适的 inode 信息。为创建的文件分配合适的 inode 并且添加属性字段。
-
分配数据块
为创建的文件分配合适的数据块。
-
更新文件所在目录的 inode 编号和文件名的映射
-
更新 super block 和 inode bitmap等信息。