Linux操作系统:从磁盘到inode到文件系统到软硬链接
前言
通过本章你可以学习到文件系统的相关内容,本篇所有的指令都是一个一个敲出来的,可以尝试,本篇是作者写过最长的一篇所以有所疏漏在所难免,指令可能格式不正确,可以结合AI尝试敲写。
一、磁盘
1、内部结构

上图是一个机械硬盘的内部结构,由传动臂+磁头+盘片组成。
磁头可以在盘片上移动,从而达到读写的效果,不过注意的是,磁头并没有与盘片上接触,否则极易发生磨损。磁头通过传动臂进行移动,而盘片通过转轴移动,从而达到磁头可以读取盘片上任意位置的数据,所以可以得知,数据是存放在盘片上的,想要读取或者写入相应的数据本质就变成了如何找到对应的位置。
下边的这张图展示了在一个磁盘中一个具体的盘面的具体信息。

具体而言,盘面上不是光滑的而是具有一个一个的同心圆,这个我们叫它磁道,而多个盘片摞在一起就变成了上图中的右边,此时把上下很多磁道抽象成一个圆柱,这个圆柱就叫做柱面,下边一张图帮助你理解什么叫柱面。

可以看到,多个盘片摞在一起,同时上文说过传动臂是同步转动的,所以说磁头会去不同盘片的相同磁道,此时将它抽象成一个圆柱就是上图。
2、怎么找数据:
一个盘片有两个盘面,也就是说看似有三个盘片摞在一起,实则共有六个盘面和六个磁头。而一个磁道内部是分了无数个小小的方格,这一个一个的小方格内部就存储着磁盘的数据,这就是扇区。扇区是从磁盘写入和读出的最小单位,一般大小为512字节/4kb。所以如果要找数据就是在这么多扇区中寻找到你所要找的扇区,要找到扇区就需要找到对应的盘面,而要找到对应的盘面就需要找到对应的柱面,从而我们通过倒叙的方式讲述了一遍寻找数据的方法。
总结一下就是:柱面->盘面->扇区,这里没写磁道是因为其实找到柱面之后,磁道就已经确定了。
3、物理寻址(CHS):
C:柱面;
H:磁头;
S:扇区;
这个物理寻址就是我们第二部分讲述的细节,过程在上文已经讲述所以物理寻址(CHS)没有什么好说的。知道CHS各个字母代表的是什么即可。
4、逻辑寻址(LBA)
小时候见过磁带吧,就是一圈一圈的,这个可以把线拉出来然后就是一根直线,那么根据这个我们可以将一个磁道抽象出来。上文得知磁道也是一圈一圈的,如果把磁道剪开也是一个线性结构,此时就可以抽象为一个数组。

就这样我们就形成了一个由扇区为基本单位的数组,而这个数组的下标就是LBA。
所以我们可以继续抽象,上文不是提到了一个叫做柱面的东西吗,这个柱面不就是上下相对位置相同的磁道共同构成的吗,如果将这个柱面切开会是什么呢。

根据上边的图片可以看到,一个柱面可以抽象为一个二维数组。根据上文理解,一个盘面具有多个同心圆也就是说明了有多个柱面,一个柱面是一个二维数组,那么多个柱面不就是三维数组吗,此时不就是整个磁盘吗。
这个三维数组是不是对应了上边的CHS物理寻址,从三维数组里找到一个二维数组不就是找到哪一个柱面吗,从这个二维数组上找一个一维数组不就是去找哪一个盘面吗,从这个一维数组上去找哪一个下标不就是去找对应的扇区吗。
但是有一个问题就是,每次寻找都要这么找很麻烦,查询数据三维数组是非常麻烦的,如果逻辑上能把三维数组抽象成一维数组,此时有对应的下标不就是可以直接找到对应的数据吗。所以此时就有了LBA逻辑寻址。
在这里讲述一下这个来历即可,具体怎么算的可以做为扩展区搜索一下,操作系统想要访问文件,此时会得到一个LBA地址,而机械磁盘只知道CHS地址所以此时就会存在一种算法可以将LBA地址转换成为CHS,此时磁盘就可以得到这个CHS地址从而拿取数据。这个转换过程其实非常简单就是一个除模运算。重点来讲你只需要记住,得到LBA地址会转换成CHS地址,磁盘得知CHS地址就可以拿到数据即可。
额外扩展与思考
1、LBA地址是线性下标,OS使用LBA访问磁盘,那么CHS转换由谁来完成?
答:机械磁盘。
2、磁盘的块与扇区有什么区别?
答:磁盘的块由多个连续的扇区组成。
3、为什么引入块而非直接使用扇区?
答:扇区太小,块大一点,可以减少访问次数,提升效率(和缓冲区思想类似)。
二、分区与格式化--让磁盘有组织
1、实验(挂载和格式化)
以下是简单看一下创建,分区,挂载的简单操作,简单认识即可,后边会重点讲述,也是能够看亲眼看到文件系统,提前看一下后文会挨个讲述。
1、创建虚拟磁盘
指令:dd if=/dev/zero of=./disk.img bs=1M count=5
这个指令的意思就是从/dev/zero这个文件里每一次读1M的内容,读5次,到当前目录的disk.img文件里,也就创建了一个5M大小内容全为0的虚拟磁盘。
2、格式化
指令:mkfs.extf4 disk.img
这里是将文件系统创建到disk.img中,后边会细讲。
3、挂载:
就是创建一个虚拟磁盘,然后将这个磁盘绑定到一个空目录中,此时对这个空目录操作其实就是对这个虚拟磁盘操作
指令:sudo mount -o loop disk.img lesson6
这个指令的意思就是把这个disk.img挂载到我创建的lesson6这个空目录下,也就是说此时对lesson6这个目录进行操作就是对这个disk.img这个磁盘进行操作。
同时可以看 df -h这个指令去看挂载点。
上图代表着成功挂载。输入mount这个指令可以看到所有的挂载信息。
4、写入
指令:echo "hi" | sudo tee ./lesson6/a.txt(指令记住就行了)
此时就会把数据写入进去,随后就可以访问了。
为了让实验现象更清楚,此时我会往这个磁盘写入1M内容,此时观察现象。
观察到了吗,可用空间从4.4M到了3.3M,使用空间从24K到了1.1M,所以可以看出来我们确实是做了写入操作。
5、取消挂载
指令:sudo umount ./lesson6。这个地址就是你挂载到空目录的那个地址。
打开lesson6时可以发现存在a.txt和b.txt。
此时如果说取消挂载会发生什么呢。看下图。
会发现居然lesson6居然什么也没有,刚才创建的a.txt,b.txt一下子没了。如果重新挂载呢?
可以发现重新回来了,哦!所以我们可以直接了解到,挂载这个东西了,我们之前创建的disk.img是一个5M的虚拟磁盘,此时挂载到lesson6就代表着lesson6就被"夺舍"了,此时进行各种操作其实就是对创建的虚拟磁盘进行操作。而当我们取消挂载的时候lesson6就是自己本身,自然也就什么也没有了。
那么如果说把disk.img取消挂载之后删除,此时再次创建一个新的虚拟磁盘挂载到lesson6会发生什么呢?不用多说就是从0开始。
2、块和分区
1、块
已知磁盘最小的存储单位是扇区(512字节),如果说访问一个大文件此时就需要访问很多次扇区,此时就会导致效率低下,所以引入块这个概念,把连续多个扇区打包成一个块,从而提高效率。
块的特性:1、大小固定,一般是(4kb,8个扇区)。
2、文件系统层面的逻辑,磁盘上没有块这个逻辑,只有扇区。
指令:stat 文件名。《---查看一个文件的信息。

块的大小IO BLock:4096(8个扇区,4KB)。
2、分区
简单来讲就是一个大的磁盘为了好管理从而分割成多个独立的“逻辑磁盘”,就像Windows上一个大的c盘一般分成多个其他的盘,比如说d盘,e盘之类的。每一个分区都是独立的可以去独立挂载。在不同的盘可以加入不同的文件系统去管理,比如说ext2/ext3/ext4等等等。
指令:fdisk -l 可以查看分区的信息。 
从这张图可以看到start和end,这个代表的是扇区。意思就是说/dev/vda1从第2048个扇区开始到2095结束,/dev/vda2从第4096个扇区开始到104857566个扇区结束。
3、额外扩展与思考
1、格式化就是在分区上写文件系统结构(超级块、位图、inode等等);
2、为什么格式化要比清空数据更快?
答:格式化是将分区标记为“空闲”,只是访问不到了而已不是真的去清空数据,数据还在。
3、分区表是磁盘上的第一层索引,记录每个分区的起始和结束。
三、inode与文件系统
1、文件属性
通过ls -l就可以看到一个文件的具体属性。
从左到右依次为:模式,硬链接数,文件所有者,所属组,文件大小,最后修改的时间,文件名
文件=属性+内容,内容存储在块之中,那么属性该储存在哪里呢,这也就是下文需要讨论的。存储文件属性的区域叫做inode,也叫索引节点。接下来通过Linux内核观察inode的具体信息。由于ext2_inode和ext4_inode内容差不多,但是ext2_inode更简单所以用ext2了。
![]()
这里i_mode不就是上边讲的模式吗,i_size就是大小,i_links_count就是硬链接数。所以当我们ls -l或者stat时,在终端上看到的文件信息就是从inode里获取的。
ext2_inode里最为重要的就是i_block[EXT2_NBLOCKS],一般来讲EXT2_NBLOCKS这个值是15,即i_block[15],这个数组是用来存储数据快好映射的,简单来说,里边存储的是磁盘块号,可以通过这个块号找到磁盘对应的块,这个块里就是存放数据的地方。15的话是有12个直接指针,1个一级间接指针,1个二级间接指针,1个三级间接指针.
直接块指针:范围i_block[0]~i_block[11],直接指向数据块,适合存储小文件,
一级间接指针:范围:i_block[12],指向一个间接块,这个块内没有数据而是一个块号,也就是说一整块数据块存储的是指向数据块的块号。如果说一个块大小为4KB,一个指针大小为4字节,那么就可以包含1024个块号,就等于通过一个块就能找到1024个数据块,即4MB。
二级间接指针:范围:i_block[13],执行一个一级间接指针,也就是说这个数据块里包含了1024个一级间接指针,而每一个一级间接指针又包含了1024个数据块,此时就可以存储4GB。
三级间接指针:范围i_block[14],指向一个二级间接指针,也就是说这个数据块里包含了1024个二级间接指针,而这些二级间接指针内部包含了1024个一级间接指针,一级间接指针内部又包含了1024个数据块地址,所以一个三级间接指针就可以找到4TB的数据。
总结一下,这里是32位的情况也就是说块号大小为4字节,64为就会是8字节,与此同时, 块号就是一个索引,可以帮助文件系统找到对应的数据块。下边用一张图来帮助上边的文字理解。
2、文件系统

上图是一张细节图,从最一开始的disk然后分区,partition1就是一个成功的分区,进入一个分区可以看到是格式化的一个文件系统,这里用的是ext2的文件系统,文件系统内部分了很多块组。块组内的数据是super block,GDT.....下边逐一讲讲
super_block超级块:
可以通过上边这张内核代码看到,存在着inode的数量,block的数量,没有使用的inode,block的数量,块大小等等的内容,从这里可以看出超级块就是一个档案,将整个分区的资料整理在这一个块内,这一个块可以复制多份,同时应该注意一个点就是,因为不同的分区文件系统不同所以说超级块在不同的分区内容是不同的。
GDT(块组描述符表)

可以看到,这里存的都是块号,描述的是这个块组的信息,也就是说描述着这个块组内部的分布情况,比如说哪里是inode_table,哪里是block_bitmap。每一个块组内部都有一个GDT用来描述整个块组内部的分布情况。
Block_bitmap(块位图)
采用位图的形式表示块的使用情况,使用了标1,没有使用标0。
inode_bitmap(inode位图)
标记inode表中inode的使用情况。
inode_table
里边存放着一个一个的inode结构体,也就是
,此时就打通了一条路,就是说当你拿到一个inode号通过计算可以找到inode_table内部的一个ext2_inode,此时ext2_inode内部的i_block就能找到对应的数据块。
注意:inode编号是分区管理,不能跨区,可以跨同区的块组。
data_blcok
就是存储数据的。
四、目录与路径解析
1、实验及结论
ls -li打印inode号 ls -lia打印全部文件(包括隐藏文件)
上边是我进入了一个文档内部写下的指令,可以发现不同的文件对应着不同的inode号,通过上文我们得知,找一个文件可以通过inode号来找。可是我们平时写文件从来没有用过inode号,所以不妨大胆猜测一下,在目录内部产生了一个文件名和inode号产生了映射,所以输入文件名就会转换成为inode,此时就可以找到这个文件的属性和数据。
注意"."这个文件的inode号,竟然与lesson4文件inode号一样,一个文件的inode号是唯一的,那么也就是说明这个"."文件就是lesson4,所以我们说"."文件是当前文件,而".."文件的inode是655361,此时不妨大胆想一下,这个inode值是不是就是上一个目录的inode值呢?
lesson4的上一层文件是ubuntu,此时inode也是一样的,所以cd ..就可以返回上一层目录,所以当我们cd ubuntu和在lesson4的目录下cd ..是一样的,那么也就能够证明了文件名与inode产生了映射,我们输入的文件名会被转化成inode。
总结一下,所以为什么我们必须要打开目录才能访问文件,本质上是因为目录存储的是文件名和inode号的映射,找到一个文件需要inode号,所以必须通过文件名得到文件的inode号。
2、路径解析
问题引入:已知一个目录也是文件,目录内部存储的是文件名和inode映射,如果想要找到某个文件就需要知道它在哪个目录中,可是怎么知道这个目录,知道这个目录了那上一个目录怎么找。
所以这里就引出了路径解析这一个概念,想要知道这个文件在哪里就需要知道它在哪个目录,可目录也是文件也需要找这个目录。
所有文件都有路径,访问一个文件就需要从根目录开始,根据目录名去找指定目录,直到找到最终的文件,这个过程就叫做路径解析。
下边是一张关于路径的解析图,路径是/home/ubuntu/lesson4/abc,就是查找对应文件的inode找到了就进去。从根目录开始。
3、路径缓存
我们知道如果想要找到一个文件的话需要inode,而inode在块组的inode_table里,换言之就是在磁盘上,此时从根目录开始解析目录就需要频繁的访问磁盘,最后找到文件并获取数据。由于磁盘是外设,此时效率就会极其低下。如果将文件的路径缓存下来,此时路径解析在内存中进行,此时效率就会快很多,所以引出了路径缓存这个概念。
dentry树结构

上图是关于dentry内核代码,下边是一张整体图片。
在 dentry 缓存树上查找目标文件名:
- 若缓存中没有该 dentry,则访问磁盘读取目录块确认;如果磁盘上该文件确实不存在,就创建负缓存 dentry(
d_inode=NULL),加入 dentry 树。 - 如果磁盘查找后文件存在,就新建 dentry 挂入缓存树,以该 dentry 作为起点继续查找下一级节点。
缓存命中时,路径解析(路径寻址)直接在内存 dentry 树上完成,以此提升效率;但是文件真正的数据读取,该访问磁盘依旧要访问磁盘。dentry 缓存只优化路径查找阶段,不能免除文件数据的磁盘 IO。
额外知识与思考
1、为什么cd能进入目录?
答:因为chdir系统调用会解析路径,并修改进程的cwd指针。
2、路径解析的两种模式:绝对路径,相对路径。
3、每一个文件都有一个dentry结构。
五、挂载--加深理解
1、实验及结论
1、在家目录创建两个大小为5的虚拟磁盘
sudo dd if=/dev/zero of=./disk1.img bs=1M count=5
sudo dd if=/dev/zero of=./disk2.img bs=1M count=5
2、装文件系统
sudo mkfs.ext4 disk1.img
sudo mkfs.ext4 disk2.img
3、挂载到创建的两个目录disk1,disk2
sudo mount -o loop disk1.img ./disk1
sudo mount -o loop disk2.img ./disk2
4、此时分别向disk1和disk2写入"hello"
sudo echo "hello disk1" |sudo tee ./disk1/a.txt
sudo echo "hello disk2" |sudo tee ./disk2/a.txt
5输入ls -li观察结果
可以发现inode值是相同的,所以这个实验证明了在不同的文件系统内部,inode编号只在单个文件系统内部唯一,尽管都是ext4但是在不同的挂载点就是不同的文件系统。一般而言,一个分区是只有一个文件系统的所以这也证明了,跨区的inode号是不唯一的。
6、进入disk1,输入ls -lai
可以发现当前文件的inode是2,此时要说明一个问题就是,上文我们说了不同文件系统的inode是不唯一的,我们的根目录inode是2,这个也是2,此时不妨大胆猜测一下这个disk1就是我们创建的磁盘的根,和我们主机上的根是不一样的。
7、验证挂载覆盖
挂载中:
取消挂载:
可以发现挂载点的目录inode发生了变化,也就是说挂载这个东西其实就是发生了路径重定向。
总结一下,如果一个文件被塞入了文件系统,此时还不能使用,必须挂载。这个挂载其实就是给了一个访问入口,可以通过这个入口来访问。
2、其他知识点及总结
1、为什么一般设定挂载到空目录?
答:我们知道挂载会覆盖掉目录的内容,如果你的目录内部有重要数据此时挂载就会覆盖,就会影响,所以挂载不一定只能到空目录也可以在非空目录,前提数据目前用不到。
2、为什么需要挂载?
答:当一个文件格式化之后,就会形成独立的inode。而Linux才用单目录树的结构,每一个分区自成体系,挂载的作用就是把这些独立的文件接入到全局目录树上,此时用户就可以通过一个路径来访问不同设备上的文件。
六、软硬链接
1、实验及结果
1、硬链接
指令:ln 文件1 文件2 (文件1是已经创建了的,2是未创建的)。
而已观察到a和b的inode是一样的。此时echo "hi" >a,看看会发生什么。
我们发现,向a中写入a后,cat a打印出hi没有问题,但是cat b竟然也打印出了和a一样的内容,说明这个是同一个文件。所以说硬链接的本质就是给文章取了一个别名。修改权限也是一样,修改一个另一个也会被修改。
与此同时看权限位后边的一个数字是2,这个数字叫做硬链接数,也就是说一个inode值上有几个文件,如果删除一个文件,那么此时这个数就会变成1。
所以这里解释一下为什么一般我们创建的目录这个硬链接数是2,原因是内部都会有一个“.”文件,这个文件的inode和这个文件的inode是相同的也就是一个硬链接。
而".."也是一个文件并且具有inode值,在上文我们了解到,".."是上一个文件的inode值所以不妨猜想一下,我们上一级的目录的硬链接数肯定要大于2,看一看上一个目录ubuntu。
但是说为什么我们创建了没有多少个目录却有20个硬链接的原因是有很多的影藏文件,此时输入指令ls -lia即可查出。
2、软链接
指令:ln -s 文件1 文件2 (与硬链接类似只是多了-s)
可以发现inode是不一样的,也就证明了软链接不是同一个文件。
可以发现,往a里写文件b同样也可以收到,但是a的大小增长了3而b却还是1。所以类比Windows就是软链接类似于快捷方式。接下来删除a文件,会发生什么。
可以发现b文件还在,也证明了软链接是创建了一个新的文件,不同的inode,此时发现b->a这个变红了,在Windows上,删除相关文件时,此时只剩快捷方式也是访问不了源文件的。
这就是删除主文件后,软链接形成的文件是读取失败了的,相信有Windows的基础理解这个比较容易一些。删除主文件之后再创建此时依然可以使用链接文件。
2、软硬链接额外知识点
1、硬链接不可以跨区:因为我们知道,硬链接的inode值是相同的,上文提到,不同的文件系统inode值是不唯一的,此时硬链接跨区会出现混乱问题。同时删除一个硬链接的文件,硬链接数会减1,当减为0是会释放数据块。
2、软链接可以跨区:软链接是路径文本,而一个文件的路径是独立且唯一的。
后记:
1、文件系统的本质:就是把磁盘扇区当成一个“索引大盘”,通过super_block管理全局,inode管理文件属性,用目录去做inode和文件名的映射。
后续将更新 库相关内容。敬请期待!!!
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐









可以发现inode值是相同的,所以这个实验证明了在不同的文件系统内部,inode编号只在单个文件系统内部唯一,尽管都是ext4但是在不同的挂载点就是不同的文件系统。一般而言,一个分区是只有一个文件系统的所以这也证明了,跨区的inode号是不唯一的。















所有评论(0)