前言
本篇系列就会进入文件系统部分 , 这里将会正式带大家了解文件到底是怎么回事 ,Linux 系统底层到底在做什么 ?? 相信认真学习完本文 , 你会大有所获 ! 颠覆你对文件的认知 ! 本文篇幅较长 , 皆给学者一个完整的文件体系 , 让学者彻彻底底的搞明白 !
● 了解磁盘
我们在学习 Linux 时经常会听到磁盘 , 键盘灯这样的外部设备 , 那磁盘到底是个啥呢 ?? 这里笔者详细介绍一下磁盘 , 只有了解了磁盘 , 后续的文件系统就会相对容易去理解了 !
一 、关于磁盘需要知道的常识
1 . 磁盘是什么 ?
磁盘 (disk )是一个外部设备 , 指利用磁记录技术存储数据的存储器。磁盘是计算机主要的存储介质,可以存储大量的二进制数据,并且断电后也能保持数据不丢失 。 磁盘是一个机械设备 .
简单一点 : 知道磁盘是一个机械设备 , 外设 , 存储的是二进制数据 .
以下展示一个磁盘 , 带大家见一见 :
2 . 磁盘的特点
既然磁盘是个外设 , 那其肯定有自己独特的特点 .
- 磁盘容量大 , 便宜 ! 这是其最大的特点了 .
- 磁盘的读写相对较慢 .
二 、磁盘重点理解
要了解磁盘 , 我们就要详细了解其结构以及工作原理 .
1 . 物理结构了解
其中 : 图 1 就是真实的磁盘的内部结构的样子 , 图 2 是对磁盘进行立体化的图示(方便我们好理解工作原理) .
对于物理结构 , 我们需要知道的以下知识 :
- 一个磁盘可以有多个盘片 ,
每个盘片有两个盘面 .
- 盘片上是用来读写数据的 ,
即 : 盘片上存储数据 .
- 想要读写数据 , 必须有磁头 , 磁头就相当于笔 ,
每一个面都有一个独立的磁头用来读写数据 .
磁头在机械臂的带动下是共进退的 !!!!!!!!!!!!
(特别重要 !)
现在 , 再对物理结构详细一点 :
一个盘面内部其实是这样的 :
所以 , 一个盘面是由多个同心圆的磁道组成的 .
之前提到过 , 盘片上存的是数据 , 那么具体是在哪里存的呢 ??
- 磁道 : 是用来存数据的 .
- 磁道内部都是一个一个扇区组成 .
重点
- 扇区 : 磁盘存储数据的基本单位 , 512字节 . 什么意思呢 ??? 就是说 : 一个磁道上是由数个 扇区 组成的 , 最终的数据是存在扇区里的 .
2 . CHS 寻址
有了上面的理解 , 那对磁盘进行写入时 , 怎么写入 ?? 考虑 , 就写入 512字节(一个扇区) 是怎么写入的 ??
- 首先 , 一个磁盘是有盘片的 , 盘片中是有磁道的 , 磁道中是有扇区的 , 这样的关系要清楚 !
- 其次 , 磁盘是有磁头的 , 磁头就相当于笔 .
- 最后 , 磁头是在传动臂带动下同进退的 !
所以 , 那真实的读写是怎么进行的呢 ??
- 先找 , 柱面 , 因为一个柱面上会有多个磁道 .
- 其次 , 找是哪个磁道 .
- 然后 , 因为一个磁道会包含多个扇区 , 所以要确定好是哪个扇区的 !
- 确定好扇区 , 就意味着要在这个扇区进行读写了 .
- 以上都是磁头进行寻找 , 这个过程叫 : 寻址 .
这是磁盘真实的寻址过程 , 所以就叫 : C(Cylinder)H(Head)S(Sector) , 即 : 柱面 磁头 扇区 .
3 . LBA 地址
什么是 LBA 地址呢 ?? LAB : Logical Block Address , 逻辑块地址 , 顾名思义是其逻辑结构 .
对于 , 我们 , 操作系统来说并不关系磁盘是怎么去进行寻址的 , 我们认为把内容较给操作系统 , 就是交给了磁盘 , 所以 , 为了更好的理解 , 就有了逻辑结构(想象出来的结构) .
- 一个磁盘会有多个柱面 .
- 一个柱面有多个磁道
- 一个磁道有多个扇区
所以 , LBA 地址就是数组的下标 !!
重点
-
注意 : LBA 地址可以和 CHS 互相转化 !
-
操作系统只需要 LBA 地址 , 磁盘对 LBA 地址进行转化 , 最终转化为 CHS 地址 , 在磁盘内部进行寻址 !
● 掌握 Ext2 文件系统(重点)
一 、操作系统如何访问磁盘 ?
对磁盘而言 , 最小的单位是扇区 , 但是对于操作系统而言 , 最小的单位是 : 块 !
必须知道的 :
- 操作系统是以块为单位进行访问的 !
- 一个块包含 : 8 个扇区 !
所以 , 对操作系统来说 , 是一块一块来访问的 , 每个块中包含的都是 8 个 扇区 ! 这样对其而言效率会高 !
那如果对于一个磁盘来说 , 磁盘很大呢 ?? 操作系统要管理怎么管理呢 ??
那对于这 10 G 操作系统又怎么来管理呢 ???
二 、文件系统
这就是一个 Linux 真实管理磁盘的结构图 !
- 磁盘是可以分区的 , 就像 Windows 上的 D 盘 , E 盘等 , 我们进行分区管理 .
- 一个分区是分组的 , 管理好了组就管理好了分区 , 管理好了分区就管理好了磁盘
- 一个组中包含很多东西 .如 : inode table 等 …
所以 , 文件系统是以 " 分区 "为基本单位的 !
所以 , 每一个块区是以" 块 "为基本单位的 !
● Block Group(块组)
前面笔者也提到了 , 在操作系统层面我们讨论的都是块 ! 一个块中又分为多组 !
一个块组中分以下内容 :
这里详细介绍 :
1 . inode
Linux 任何文件都有自己的属性集合 , 那么文件的属性在哪里呢 ??
- 初识什么是 inode ?
inode 内存级的一个结构体 , 这个结构体中会包含文件的属性等信息 .
这个结构体的大小是固定的 , 通常为 128 字节 .
2 . inode Table
inode Table 即 : inode 表 . 也就是说这个表中会包含多个 inode 结构体 .
简单来讲就是 : 不同文件的属性集合表 !
前面提到了 , 一个块区是以 4096 字节为基本单位的 !
所以 , 这里我们仔细探讨一下一个 inode 中到底有什么内容 ?
2.1 . inode number
显示文件的 inode number :
ls -il // i 选项 , 表示 inode number
2.2 . datablock 映射数组
映射数组 ?? 映射什么 ??
2.3 . 补充知识(重要重要重要!)
- 文件名不作为 属性出现在 inode 中 !(重要)
- inode 编号 , 块号在同一个区中是唯一的 , 但是在不同区中不唯一
3 . Data Blocks
前面讲了一个文件的属性存放位置 , 但是文件还有内容呀 , 存到哪呢 ??
- 是什么 ?
叫 : 数据块 , 用来存数据的 . 是用来存文件内容的 ! - 一个 Data Blocks 是以 4 KB 为基本单位的
4 . inode Bitmap (inode 位图)
Bitmap – 位图 , 是用来干什么的 ??
用来管理 inode 的 , 每一个比特位表示 inode 是否空闲 / 使用 .
5 . Block Bitmap (块位图)
同 inode Bitmap 一样 , 其也是用来记录是否空间的 .
每一个比特位表示一个数据块是否空闲 / 使用 .
6 . GDT(Group Descriptor Table)
块组描述符表,描述块组属性信息 , 一个块组一个 GDT , 每个块组中存这个该块组的描述信息 , 如在这个块组中从哪里开始是inode Table,从哪里开始是Data Blocks,空闲的inode和数据块还有多少个等等。块组描述符在每个块组的开头都有⼀份拷贝 。
即 : GDT 是用来管理整个分区的块组的 !
7 . Super Block(超级块)
存放文件系统本身的结构信息,描述整个分区的文件系统信息 , 记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,⼀个block和inode的大小 ,最近⼀次挂载的时间,最近⼀次写入数据的时间,最近⼀次检验磁盘的时间等其他文件系统的相关信息 。
总之 , Super Block 是管理文件系统的 , 记录文件系统的相关信息 !
8 . 总结
所以 , 这里我们就可以知道以下知识了 :
● 磁盘的格式化
一个文件是在磁盘上存储的 , 但是管理者是操作系统 , 那么在一个磁盘在使用前 , 会干什么呢 ??
答 : 磁盘格式化 !!! 平时我们也会谈论格式化 , 那么到底什么是格式化呢 ??
前面也有讲到 , 一个磁盘会分区 , 分块 . 块会分组 , 组会分内容 .
我们通过格式化的效果可以看到 , 格式化以后的磁盘内容会清空 ! 那底层是什么 ?
● 文件名存在哪里 ?
文件名不属于属性 , 那它在哪里呢 ??
答 : 存在所属的目录的内容中 !
为什么呢 ??
● 有关文件和目录
一 、文件
所以 , 一个普通文件在磁盘上的存储是不是就是以上所有讲的内容呢 ??
答 : 是 ! , 普通文件就是按照以上的方式 , 文件的属性存在 inode 中 , 文件的内容存在 DataBlocks(数据块) 中 .
- 我们访问一个普通文件是到底是怎么访问的 ?
我们在访问一个文件时 , 经常会这样访问 :
cat test.txt
我们平时访问文件 , 也没有用过什么 inode 编号来访问啊 , 都是文件名啊 ???
以下是具体的介绍 :
● 路径解析
访问一个文件 , 操作系统到底做了什么 ??
前面提到 , 我们访问文件用的是文件名 , 虽然知道了文件名可以映射 inode 的 , 但是操作系统怎么就知道是我当前路径下的这个文件呢 ?? 为什么不会是其他路径下的呢 ??
所以 , 要清楚以下内容 :
- 访问一个文件 , 必须有路径和文件名
- 根目录是固定的 , 系统开机就有 , 不用找 , 所以路径解析会从根目录开始
- 路径谁提供 ??? 进程 ! 因为进程代表用户 , 进程会记录 CWD .
二 、目录
目录也是文件呀 , 文件 = 属性 + 内容 . 所以目录的访问方式和普通文件一样 , 所以 , 对磁盘来说没有目录的存在 , 都是普通文件 !
所以 , 给出一个问题 : Linux 磁盘中 , 真的存在目录吗 ??
答 : 不存在 , 只有文件的概念 , 即 : 只保存属性和内容 !
那问题来了 , 那我们使用的目录又是啥呢 ?? 不都是文件吗 ?? 不是磁盘就没有目录的概念吗 ?? 这不是矛盾吗 ???
其实 , 这个目录是内存级的 , 是操作系统自动生成的 , 属于操作系统内存概念 , 这个也是路径缓存方案 , 以下具体介绍 !
● 路径缓存
在路径解析中 , 讲到了要使用一个文件名 , 就需要找到这个文件名 , 要找到这个文件名就需要路径解析 , 进行查找 !
路径解析是从 / 路径开始查找的
, 但是这样还存在一个问题 :
- Linux中,在内核中维护树状路径结构的内核结构体叫做:
struct dentry
struct dentry {
atomic_t d_count;
unsigned int d_flags; /* protected by d_lock */
spinlock_t d_lock; /* per dentry lock */
struct inode *d_inode; /* Where the name belongs to - NULL is
* negative */
/*
* The next three fields are touched by __d_lookup. Place them here
* so they all fit in a cache line.
*/
struct hlist_node d_hash; /* lookup hash list */
struct dentry *d_parent; /* parent directory */
struct qstr d_name;
struct list_head d_lru; /* LRU list */
/*
* d_child and d_rcu can share memory
*/
union {
struct list_head d_child; /* child of parent list */
struct rcu_head d_rcu;
} d_u;
struct list_head d_subdirs; /* our children */
struct list_head d_alias; /* inode alias list */
unsigned long d_time; /* used by d_revalidate */
struct dentry_operations *d_op;
struct super_block *d_sb; /* The root of the dentry tree */
void *d_fsdata; /* fs-specific data */
#ifdef CONFIG_PROFILING
struct dcookie_struct *d_cookie; /* cookie, if any */
#endif
int d_mounted;
unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */
}
关于 dentry 结构我们要知道 :
所以 , 关于路径缓存我们必须知道的 :
以下就是一个文件名在 dentry 树下的查找 :
三 、总结
● 挂载分区
前面笔者讲到过以下 :
- inode 编号在同一区域内有唯一值 .
- inode 编号在不同区域内不唯一 .
以下是挂载 :
意思就是 : 一个路径和一个分区相关联 , 所以就称 : 挂载 !
所以 , 不用担心不同分区的 inode 重复导致的问题 , 因为一个分区被写入文件系统时就会和该 inode 编号所在的目录所挂载 , 所以 , 都是一一对应的 !
● Linux 文件部分综合总结
我们的文件分为 : 被打开的文件 , 没有被打开的文件 .
在一个磁盘使用前 , 需要进行格式化 , 将磁盘清空 , 格式化的本质就是写入文件系统的管理信息 !
- 所以 , 在文件没有被打开时 , 其还在磁盘上 , 所以在磁盘上的存储方式就是 : 属性 + 内容 .
- 那么文件要打开时 , 即 : 我们用 fopen , open 会做什么 ??
● 根据路径 + 文件名进行路径解析 , 查找 dentry 树 , 找到了 dentry 树中该节点的文件 inode 节点信息 , 然后找到 inode 编号 , 找到文件的属性 , 通过 inode 和 数据块的映射关系 , 找到文件的内容 . 最终一个文件所有内容被找到 , 若以上 dentry 树中没有找到 inode 节点信息 , 就从磁盘中加载该文件 !
对于文件操作系统会做什么 ??? (重要重要重要 !)
- 当进程运行时 , 会分配 task_struct , files_struct(文件描述符表) , 表中的文件描述符可以找到对应的文件 .
- 当一个文件被创建时 , 磁盘会记录 inode 信息到 inode Table 中 . 所以一个文件的 inode 的信息是在磁盘中的 .
- 当一个文件被打开时 , 系统就会创建一个 struct file 对象 来描述一个文件 .
- 当一个文件被访问时 , 系统就会找文件的 dentry 树(目录项缓存)来找文件的 inode 节点信息 , 然后通过 inode 编号找到文件的内容和属性 .
- 找到文件的内容后 , 会把内容加载到操作系统内核文件缓冲区中 , 避免频繁 I / O .
- 文件的属性会一直在 inode 中 , 操作系统需要时 , 会将其加载到内存中 .
- 补充 : 磁盘只认 inode , 我们用文件名访问 , 最终会通过映射关系找到 inode . 最终还会回归到上述方式 .
总结
以上是对 Linux 文件部分全方面的认知了 , 这些掌握了 , 你对 Linux 的理解就更深一步了 , 相信学者一定会大有所获 , 笔者创作本篇章也是花了很久 , 若觉得有用的话 , 恳请关注哦 ~~~ !