🔥个人主页: Milestone-里程碑
❄️个人专栏: <<力扣hot100>> <<C++>><<Linux>>
🌟心向往之行必能至
目录
[1.1 硬盘的物理结构](#1.1 硬盘的物理结构)
[1.2 如何写入 CHS定址](#1.2 如何写入 CHS定址)
[1.3 硬盘的逻辑结构](#1.3 硬盘的逻辑结构)
[1.4 LCB&&CHS](#1.4 LCB&&CHS)
[2.1 引入"块"的概念](#2.1 引入"块"的概念)
[2.2 引入"分区"概念](#2.2 引入"分区"概念)
[2.3 引入"innode"概念](#2.3 引入"innode"概念)
[3.2 Block Group - 分区地图](#3.2 Block Group - 分区地图)
[3.3 Block Group的内部组成](#3.3 Block Group的内部组成)
[3.3.1 Data blocks -书架](#3.3.1 Data blocks -书架)
[3.3.2 块位图:Block Bitmap](#3.3.2 块位图:Block Bitmap)
[3.3.3 inode Table - 图书索引卡](#3.3.3 inode Table - 图书索引卡)
[3.3.4 GDT(Group Descriptor Table)](#3.3.4 GDT(Group Descriptor Table))
[3.3.5 超级块(Super Block) - 图书馆总账本](#3.3.5 超级块(Super Block) - 图书馆总账本)
[3.4 inode与Data blocks的映射关系](#3.4 inode与Data blocks的映射关系)
[3.5 目录与文件名](#3.5 目录与文件名)
[3.6 路径](#3.6 路径)
[3.7 挂载分区](#3.7 挂载分区)
[1. 查看可用分区](#1. 查看可用分区)
[2. 创建挂载点](#2. 创建挂载点)
[3. 挂载分区](#3. 挂载分区)
[4. 验证挂载](#4. 验证挂载)
[四. 软硬连接](#四. 软硬连接)
[4.1 软连接](#4.1 软连接)
[4.2 硬链接](#4.2 硬链接)
我们知道
文件=内容+属性,
已经打开的文件存入进了内存IO中,那么没打开的文件呢?
其实就存在硬盘里,那么当我们要打开时,硬盘是如何找到?
与我们前面提到的,目录是树状分布的有关,先描述再组织
一.理解硬件
1-1 磁盘、服务器、机柜、机房
• 机械磁盘是计算机中唯⼀的⼀个机械设备
• 磁盘--- 外设
• 慢
• 容量⼤,价格便宜
1.1 硬盘的物理结构

扇区:是磁盘存储数据的基本单位,512字节,块设备
所以哪怕系统要修改一个字节,也要先把这字节位于的扇区的512字节都读出来,再进行修改
磁头在传动臂的带动下,共进退

1.2 如何写入 CHS定址
如何定位⼀个扇区呢?
• 可以先定位柱面
• 确定磁头要访问哪⼀个磁头
• 再定位⼀个扇区(sector)
•
扇区是从磁盘读出和写⼊信息的最⼩单位,通常⼤⼩为 512 字节。
磁头(head)数:每个盘⽚⼀般有上下两⾯,分别对应1个磁头,共2个磁头
磁道(track)数:磁道是从盘⽚外圈往内圈编号0磁道,1磁道...,靠近主轴的同⼼圆⽤于停靠磁 头,不存储数据
柱⾯(cylinder)数:磁道构成柱⾯,数量上等同于磁道个数
扇区(sector)数:每个磁道都被切分成很多扇形区域,每道的扇区数量相同
圆盘(platter)数:就是盘⽚的数量
磁盘容量=磁头数 × 磁道(柱⾯)数 × 每道扇区数 × 每扇区字节数
细节:传动臂上的磁头是共进退的(这点⽐较重要,后⾯会说明)
1.3 硬盘的逻辑结构

这是一个老式磁盘
我们发现,如果立马的带子卷起来,就是同心圆,如果拉开,就变成了一条直线,线性结构

那么定位扇区,就变成定位数组下标了,我们把这种地址叫做LCB地址(三维数组)
1.4 LCB&&CHS
CHS转成LBA:
• 磁头数*每磁道扇区数 = 单个柱⾯的扇区总数
• LBA = 柱⾯号C*单个柱⾯的扇区总数 + 磁头号H*每磁道扇区数 + 扇区号S - 1
• 即:LBA = 柱⾯号C*(磁头数*每磁道扇区数) + 磁头号H*每磁道扇区数 + 扇区号S - 1
• 扇区号通常是从1开始的,⽽在LBA中,地址是从0开始的
• 柱⾯和磁道都是从0开始编号的
• 总柱⾯,磁道个数,扇区总数等信息,在磁盘内部会⾃动维护,上层开机的时候,会获取到这些参 数。
LBA转成CHS:
• 柱⾯号C = LBA // (磁头数*每磁道扇区数)【就是单个柱⾯的扇区总数】
• 磁头号H = (LBA % (磁头数*每磁道扇区数)) // 每磁道扇区数
• 扇区号S = (LBA % 每磁道扇区数) + 1
•
"//": 表⽰除取整
所以:从此往后,在磁盘使⽤者看来,根本就不关⼼CHS地址,⽽是直接使⽤LBA地址,磁盘内部⾃⼰转换。所以:
从现在开始,磁盘就是⼀个 元素为扇区 的⼀维数组,数组的下标就是每⼀个扇区的LBA地址。OS使⽤
磁盘,就可以⽤⼀个数字访问磁盘扇区了
二.引入文件系统
2.1 引入"块"的概念
硬盘就是典型的"块概念",OS在读取数据时,并不会一个扇区一个扇区地读取,而是一次读连续的多个扇区,被读取的连续扇区就叫做块
⼀个"块"的⼤⼩是由格式化的时候确定的,并且不可 以更改,最常⻅的是4KB,即连续⼋个扇区组成⼀个 "块"。"块"是⽂件存取的最⼩单位。
2.2 引入"分区"概念
在Windows中,我们有C盘 D盘 E盘,在Linux中,我们分区,区也是文件
柱面就是分区的最小单位
2.3 引入"innode"概念
之前提过 文件=属性+内容,我们使用ls -l可以查看文件属性
bash
[lcb@hcss-ecs-1cde ~]$ ls -l code
total 4
drwxrwxr-x 5 lcb lcb 4096 Dec 13 23:34 linux
分别为
权限 硬链接数(后面提) 所有者 所属组 最后修改时间 文件名
思考:
文件数据都是存储在"块"区域,那么文件属性存储在何处?
我们把存储文件属性的地方叫innode 中文:索引节点
Linux下⽂件的存储是属性和内容分离存储的
• Linux下,保存⽂件属性的集合叫做inode,⼀个⽂件,⼀个inode,inode内有⼀个唯⼀
的标识符,叫做inode号
但如果我们再使用ls -i会发现
bash
[lcb@hcss-ecs-1cde ~]$ ls -i code
140741 linux
多了一串数字,其实这是文件的索引节点
正如前面提到的数组下标,此时的140741就是linux在它所在分区的下标
查看innode源码,存储什么信息
bash
/*
* Structure of an inode on the disk
*/
struct ext2_inode {
__le16 i_mode; /* File mode */
__le16 i_uid; /* Low 16 bits of Owner Uid */
__le32 i_size; /* Size in bytes */
__le32 i_atime; /* Access time */
__le32 i_ctime; /* Creation time */
__le32 i_mtime; /* Modification time */
__le32 i_dtime; /* Deletion Time */
__le16 i_gid; /* Low 16 bits of Group Id */
__le16 i_links_count; /* Links count */
__le32 i_blocks; /* Blocks count */
__le32 i_flags; /* File flags */
union {
struct {
__le32 l_i_reserved1;
} linux1;
struct {
__le32 h_i_translator;
} hurd1;
struct {
__le32 m_i_reserved1;
} masix1;
} osd1; /* OS dependent 1 */
__le32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
__le32 i_generation; /* File version (for NFS) */
__le32 i_file_acl; /* File ACL */
__le32 i_dir_acl; /* Directory ACL */
__le32 i_faddr; /* Fragment address */
union {
struct {
__u8 l_i_frag; /* Fragment number */
__u8 l_i_fsize; /* Fragment size */
__u16 i_pad1;
__le16 l_i_uid_high; /* these 2 fields */
__le16 l_i_gid_high; /* were reserved2[0] */
__u32 l_i_reserved2;
} linux2;
struct {
__u8 h_i_frag; /* Fragment number */
__u8 h_i_fsize; /* Fragment size */
__le16 h_i_mode_high;
__le16 h_i_uid_high;
__le16 h_i_gid_high;
__le32 h_i_author;
} hurd2;
struct {
__u8 m_i_frag; /* Fragment number */
__u8 m_i_fsize; /* Fragment size */
__u16 m_pad1;
__u32 m_i_reserved2[2];
} masix2;
} osd2; /* OS dependent 2 */
};
/*
* Constants relative to the data blocks
*/
#define EXT2_NDIR_BLOCKS 12
#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
备注:EXT2_N_BLOCKS = 15
innode的大小一般为128/256,我们取128字节,可以看见上面存储了大小,时间戳等等信息
因此,正如我们前面所说,哪怕一个文件的内容为0,但它仍在磁盘中占据空间,因为文件属性占据空间
三.ext2文件系统
3.1认识
我们想要在硬盘上储⽂件,必须先把硬盘格式化为某种格式的⽂件系统,才能存储⽂件。⽂件系统的⽬的就是组织和管理硬盘中的⽂件。

ext2文件系统,将一个区分为若干个大小的Block Group,因此,只要管理一个分区,以此类推,管理所有分区,也就能管理硬盘了
3.2 Block Group - 分区地图
ext2⽂件系统会根据分区的⼤⼩划分为数个Block Group。⽽每个Block Group都有着相同的结构组
成
3.3 Block Group的内部组成
3.3.1 Data blocks -书架
数据区,用来存储文件内容,即block
• 对于普通⽂件,⽂件的数据存储在数据块中。
• 对于⽬录,该⽬录下的所有⽂件名和⽬录名存储在所在⽬录的数据块中,除了⽂件名外,ls -l命令 看到的其它信息保存在该⽂件的inode中。
• Block 号按照分区划分,不可跨分区
3.3.2 块位图:Block Bitmap
Block Bitmap中记录着Data Block中哪个数据块已经被占⽤,哪个数据块没有被占⽤,提升查找效率
3.3.3 inode Table - 图书索引卡
• 存放⽂件属性 如 ⽂件⼤⼩,所有者,最近修改时间等
• 当前分组所有Inode属性的集合
• inode编号以分区为单位,整体划分,不可跨分区
3.3.4 inode位图
每个bit表⽰⼀个inode是否空闲可⽤,提升效率
3.3.4 GDT(Group Descriptor Table)
块组描述符表,描述块组属性信息,整个分区分成多个块组就对应有多少个块组描述符。每个块组描 述符存储⼀个块组 的描述信息,如在这个块组中从哪⾥开始是inode Table,从哪⾥开始是Data
Blocks,空闲的inode和数据块还有多少个等等。块组描述符在每个块组的开头都有⼀份拷⻉。
bash
// 磁盘级blockgroup的数据结构
/*
* Structure of a blocks group descriptor
*/
struct ext2_group_desc
{
__le32 bg_block_bitmap; /* Blocks bitmap block */
__le32 bg_inode_bitmap; /* Inodes bitmap */
__le32 bg_inode_table; /* Inodes table block*/
__le16 bg_free_blocks_count; /* Free blocks count */
__le16 bg_free_inodes_count; /* Free inodes count */
__le16 bg_used_dirs_count; /* Directories count */
__le16 bg_pad;
__le32 bg_reserved[3];
};
3.3.5 超级块(Super Block) - 图书馆总账本
存放⽂件系统本⾝的结构信息,描述整个分区的⽂件系统信息。记录的信息主要有:bolck 和 inode的总量,未使⽤的block和inode的数量,⼀个block和inode的⼤⼩,最近⼀次挂载的时间,最近⼀次写⼊数据的时间,最近⼀次检验磁盘的时间等其他⽂件系统的相关信息。Super Block的信息被破坏,可 以说整个⽂件系统结构就被破坏了
作用:
- 文件系统识别:存储文件系统的类型、版本、状态(如是否已挂载)。
- 资源管理:记录空闲块和inode的数量,用于分配和回收资源。
- 一致性检查 :在系统崩溃后,通过超级块中的状态标志触发文件系统检查(如
fsck)。
我们常说的格式化,就是修改超级块的管理信息
3.4 inode与Data blocks的映射关系
我们前面说过一个inode对应一个block,这不就像哈希一样,需要映射吗
一个inode只存在大小为15的数组进程存储对应的blocks
bash__le32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */因此我们将前12个都设为直接指针,后三个分别为1级间接 2级间接 3级间接
原因:如果全部为直接指针
- 问题:最大文件只能 15 × 4KB = 60KB
- 后果:无法存储视频、数据库、虚拟机镜像
- 空间浪费:每个inode固定15个指针,即使文件只有1KB
如果全为间接指针:
- 问题:访问4KB小文件也要多读一次间接块
- 性能:每个文件访问都慢10ms(机械硬盘)
- 空间:每个文件浪费一个间接块(4KB)
例子:目标:找到逻辑块号 262144 对应的数据块
bash
二级间接块400(存储一级间接块号)
┌────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ ...│ 255│ 256│ 257│ ...│
├────┼────┼────┼────┼────┼────┼────┤
│ 450│ 451│ ...│ 501│ 502│ 503│ ...│ ← 读出来是502
└────┴────┴────┴────┴────┴────┴────┘
↑
索引256
一级间接块502(存储数据块号)
┌────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ ...│ 99 │ 100│ 101│ ...│
├────┼────┼────┼────┼────┼────┼────┤
│ 599│ 600│ ...│ 5999│6000│6001│...│ ← 读出来是6000
└────┴────┴────┴────┴────┴────┴────┘
↑
索引100
如果出现一个块的Data blocks内存不足,数据可以跨区储存,因为在块内,我们的indoe和data block号都可以跨块,但不能跨区
现实类比:银行保险箱系统
| 场景 | 解决方案 | 耗时 | 体验 |
|---|---|---|---|
| 取口袋现金 | 直接指针(随身钱包) | 0秒 | 即时 |
| 存取贵重物品 | 一级间接(保险柜) | 30秒 | 标准流程 |
| 取仓库黄金 | 二级间接(金库) | 2分钟 | 严格流程 |
| 调动国家储备 | 三级间接(中央金库) | 10分钟 | 多层审批 |
3.5 目录与文件名
问题一:我们访问文件时,没有使用文件名啊
⽬录也是⽂件,但是磁盘上没有⽬录的概念,只有⽂件属性+⽂件内容的概念。
问题二:目录也是文件吗?
对的,目录也是文件,属性无需多说,内容就是文件名和indoe的映射关系
访问⽂件必须要知道当前⼯作⽬录,本质是必须能打开当前⼯作⽬录⽂件,查看⽬录⽂件的
内容!
3.6 路径
我们访问文件时,如
bash
whb@bite:~/code/test/test$ ls -li
total 24
1596260 -rw-rw-r-- 1 whb whb 814 Oct 28 20:32 test.c
如果我们要访问test.c,就需要输入test.c,获取对应的indoe,那么test.c又怎么知道在谁的块内呢,此时就需要它的上级目录,那么上级目录又怎么知道在谁的块或者区呢?此处就需要上级目录的上级目录,不断递归,直至结束
访问⽂件必须要有⽬录+⽂件名=路径的原因
根⽬录固定⽂件名,inode号,⽆需查找,系统开机之后就必须知道
我们之前说过目录是树状结构,但我们此处却都是文件,没有数据结构?其实系统会不断记录,你打开或者创建一个文件,系统都会自动将这个文件加入到多叉树里面
可以试试,搜索某个文件,第一次较慢,第二次快,其实就是第一次系统的多叉树没信息,要不断构建,第二次有前面构建好的,直接搜索即可
3.7 挂载分区
我们前面说的知识,还有漏洞,那就是系统怎么知道在哪个分区里(我们说的indoe和blokc号不能跨区)
挂载是将分区或设备关联到目录树的过程,让分区可以被访问和使用。
1. 查看可用分区
bash
# 查看所有块设备
lsblk
# 或
sudo fdisk -l
# 示例输出:
# NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
# sda 8:0 0 238.5G 0 disk
# ├─sda1 8:1 0 512M 0 part /boot/efi
# ├─sda2 8:2 0 100G 0 part /
# └─sda3 8:3 0 137.9G 0 part /home
# sdb 8:16 0 465.8G 0 disk # 未挂载的新硬盘
# └─sdb1 8:17 0 465.8G 0 part
2. 创建挂载点
bash
sudo mkdir /mnt/mydisk
# 或
sudo mkdir /media/username/disklabel
3. 挂载分区
bash
# 基本语法:sudo mount 设备 挂载点
# 示例1:挂载第二块硬盘的第一个分区
sudo mount /dev/sdb1 /mnt/mydisk
# 示例2:指定文件系统类型(ext4, ntfs, exfat等)
sudo mount -t ext4 /dev/sdb1 /mnt/mydisk
# 示例3:挂载USB设备(通常自动识别)
sudo mount /dev/sdc1 /media/usb
4. 验证挂载
bash
# 查看挂载结果
df -h /mnt/mydisk
# 或查看所有挂载
mount | grep mydisk
# 示例输出:
# /dev/sdb1 466G 120G 346G 26% /mnt/mydisk
• 分区写⼊⽂件系统,⽆法直接使⽤,需要和指定的⽬录关联,进⾏挂载才能使⽤。
• 所以,可以根据访问⽬标⽂件的"路径前缀"准确判断我在哪⼀个分区。
四. 软硬连接
链接文件
4.1 软连接
软链接是通过名字引⽤另外⼀个⽂件,但实际上,新的⽂件和被引⽤的⽂件的inode不同,应⽤常⻅上可以想象成⼀个快捷⽅式(类似Windows的应用图标)。在shell中的做法
bash
[lcb@hcss-ecs-1cde 9]$ ln -s test.c list
[lcb@hcss-ecs-1cde 9]$ ls -l
total 0
lrwxrwxrwx 1 lcb lcb 6 Dec 19 14:32 list -> test.c
-rw-rw-r-- 1 lcb lcb 0 Dec 19 14:29 test.c
list存储test.c的路径
bash
[lcb@hcss-ecs-1cde 9]$ ln -s test.c list
[lcb@hcss-ecs-1cde 9]$ ls -l
total 0
lrwxrwxrwx 1 lcb lcb 6 Dec 19 14:32 list -> test.c
-rw-rw-r-- 1 lcb lcb 0 Dec 19 14:29 test.c
4.2 硬链接
与软连接创建快捷方式不同
我们知道,找到硬盘上文件的是靠文件名映射的indoe号,那么如果我们让一个indoe号对应多个文件名呢,这就是硬链接
bash
[lcb@hcss-ecs-1cde 9]$ ls -li
total 0
140781 -rw-rw-r-- 1 lcb lcb 0 Dec 19 14:29 test.c
硬链接
bash
[lcb@hcss-ecs-1cde 9]$ ln test.c list
[lcb@hcss-ecs-1cde 9]$ ls -li
total 0
140781 -rw-rw-r-- 2 lcb lcb 0 Dec 19 14:29 list
140781 -rw-rw-r-- 2 lcb lcb 0 Dec 19 14:29 test.c
我们两个文件对应的indoe值一样,且硬链接数成为了2
何为硬链接数,即该indoe可以映射的文件名
如果我们创建一个目录,会发现
bash
140782 drwxrwxr-x 2 lcb lcb 4096 Dec 19 14:45 1
它的硬链接数就是2,为什么呢?展开看看
bash
[lcb@hcss-ecs-1cde linux]$ ls -al 1
total 8
drwxrwxr-x 2 lcb lcb 4096 Dec 19 14:45 .
drwxrwxr-x 6 lcb lcb 4096 Dec 19 14:45 ..
发现它只有. 和..文件
其实就是因为.文件指向目录,所有硬链接数为2,同样的,我们如果在1的目录下在创建一个子目录,1的硬链接数就成为了3,因为新创建目录的..指向1
因此,对于目录,我们知道它的硬链接数,就知道了它有多少个子目录
链接目录
软链接成功
bash
[lcb@hcss-ecs-1cde linux]$ ls -li
total 16
140782 lrwxrwxrwx 1 lcb lcb 4 Dec 19 14:42 1 -> test
硬链接失败
bash
[lcb@hcss-ecs-1cde linux]$ ln test 1
ln: 'test': hard link not allowed for directory
原因在于:如果系统任由我们进行链接,那么系统的多叉树就会成环
而对于.和..的硬链接,系统会识别,自动处理
| 对比维度 | 软链接(符号链接) | 硬链接 |
|---|---|---|
| 本质 | 独立文件,存储源文件的路径,相当于快捷方式 | 源文件的别名,与源文件共享同一个 inode 和数据块 |
| 支持对象 | 支持文件 + 目录 | 仅支持文件,不支持目录(Linux 系统限制) |
| 跨文件系统 | 支持(可跨分区、跨磁盘) | 不支持(inode 是单个文件系统内的唯一标识) |
| 源文件删除后 | 软链接失效(ls 显示红色闪烁) |
硬链接仍可正常访问(数据块未被回收) |
| 权限属性 | 有独立的权限、所有者,修改不影响源文件 | 与源文件共享权限、所有者,修改会同步 |
| 文件大小 | 大小很小,仅等于源文件路径的字符长度 | 大小与源文件一致(实际共享数据,不额外占用空间) |
| 创建命令 | ln -s 源文件 链接名 |
ln 源文件 链接名(无 -s 参数) |
| 识别方式 | ls -l 开头为 l,末尾显示 -> 源路径 |
ls -l 开头为普通文件标识(-),无箭头指向 |