目录
一、理解文件系统
1、前言
2、磁盘
二、inode
1、创建一个新文件的 4 个操作
三、软硬链接
1、软链接
2、硬链接
3、硬链接的应用
4、软链接的应用
一、理解文件系统
1、前言
在我们电脑文件里,分为打开的文件和未打开的文件,我们在上几篇文章给大家介绍了打开的文件,现在我们重点介绍未打开的文件。
我们知道打开的文件是通过操作系统被进程打开,一旦打开,操作系统就要维护多个文件,所以它是需要被操作系统管理的。也就是说这种方式,磁盘上和内存上都有这个文件,它们不是完全一样的,内存中的文件更强调的是属性和方法,磁盘中的文件更强调的是数据,它们是通过缓冲区关联的;而普通的未打开的文件在磁盘上,未被加载到内存中,它当然也要被管理;其中管理打开的文件和管理未打开的文件在操作系统中有一个功能模块叫做文件系统。
之前我们谈过进程 VS 程序,一个被打开的程序就是进程,只不过我们在解释进程时不是严格地把它当作文件来解释,需要明白的是进程是要被加载到内存的,程序就是一个磁盘文件,打开的文件是进程,而普通未打开的文件是程序。
- 一个文件属性分为这么几块
分别对应:
- 权限
- 硬链接数
- 文件所有者、所属组
- 文件大小
- 最后修改时间
- 文件名
我们可以用stat命令查看文件更详细信息。
这些信息我们都看不懂是什么意思,接下来我们要先了解磁盘。
2、磁盘
- 内存 -- 掉电易失存储介质
- 磁盘 -- 永久性存储介质 -- SSD、U盘、flash卡、光盘、磁带
磁盘分为机械硬盘(HDD)和固态硬盘(SSD),现在很多的电脑都是机械硬盘和固态硬盘组合使用,但服务器上大多都是机械硬盘,只有一些高效率的存储集群会用到固态硬盘。机械硬盘和固态硬盘在存储技术上肯定是不同的,而我们主要了解机械硬盘,因为它多用于服务器上,其次虽然固态硬盘要比机械硬盘快不少,但在 CPU 看来,两者都很慢,我们这里就了解最慢的。
虽然磁盘的盘面看起来很光滑,但是它上面有一些同心圆,这些同心圆用圆白线划分,每一圈叫做磁道,数据写在这些有颜色的区域上。实际上你并不是把一圈的空间都用完,所以这里还使用了一些直白线划分,被圆白线和直白线划分出来的区域叫做扇区。所以当盘片在旋转、磁头摆动就可以找到这个盘片的任何一个扇区进行读写。 实际磁头和盘面并不是接触的,它们之间的距离就像一架飞机在离平地 1 米的距离滑行。
盘面是有两面的,每个面上都有一个磁头,且两面都是同心圆,数据只能写在同心圆(磁道)上,根据配置不同,有些磁盘可能还有多组盘片,我们可以从上至下的分为不同的盘面,也叫做你是第几个盘面。
C 语言中访问内存的基本单位是 1byte(1B) ,但是在操作系统的角度认为内存的基本单位一般是 4kb,在操作系统看来,内存就是一个数组,每一个元素是 4kb。
如果我们要malloc 1byte,操作系统不会直接分配4kb给我们,有可能 C 语言本身就缓冲了一部分空间让你去使用,如果超出这一部分空间,操作系统再重新分配。
磁盘存储的基本单位是一个扇区,它是磁盘读取的最小单元,大部分磁盘的一个扇区是 512byte,但你会发现虽然这里好像越靠近圆心,扇区越小,其实它们都是 512byte,原因是越靠近圆心的虽然扇区越小,但是比特位也相对外圈更密集。内存和磁盘之间也是有交互的,它们之间的交互我们称为 output、input,也叫做 IO,一般内存和磁盘之间 IO 交互时,不是纯硬件级别的交互,而是要通过文件系统完成,也就是通过操作系统。
这里用户和内存之间交互的基本单元大小是 1byte,一般内存和磁盘之间交互时的基本单元大小是 4kb,所以文件系统在往磁盘读数据时,要读 8 个扇区,这就是数据由磁盘加载到内存的过程。
将数据存储到磁盘 --> 将数据存储到该数组
找到磁盘特定扇区的位置 --> 找到数组特定的位置
对磁盘的管理 --> 对该数组的管理
上面这个指向关系,我们可以看作对磁盘的抽象管理。所以需要对磁盘抽象化处理,将圆状结构的磁盘空间抽象成线性结构的磁盘空间。这里举两个例子方便理解:
- 其实在 C 语言中我们见过的 int arr[3][4] 二维数组就是把线性的数据结构抽象成了好理解的有行有列的结构。
- 曾经风靡一时的磁带是把数据存储于那条黑色的带子上,可能是为了空间的原因,把带子卷起来形成一个圆状,所以磁带在物理上,既可以是圆状,也可以是线状。
比如磁盘的所有磁道被我们抽象成了一条 500GB 的线性空间,我们可以把它看作一个很大的数组 —— 扇区 array[NUM],其中每一个元素是 512byte,操作系统要申请 4kb,那就给数组的 8 个元素。所以将磁盘抽象后,操作系统就摆脱盘面、磁道、扇区的束缚了,操作系统只关心你想访问的哪个下标,这里的地址我们称为逻辑区块地址(Logical Block Address, LBA),这里抽象出来的数组下标是和机械硬盘中盘面、磁道、扇区构成映射关系的,这里的映射关系是由对应的机械磁盘驱动维护的,操作系统想往 2 下标处写数据,最终 2 下标一定是能对应到具体磁盘中某个扇区上。如果要往固态硬盘中写数据,也是把它抽象成线性的数组,它也有自己的固态硬盘驱动维护数组下标和固态硬盘之间的映射关系。至此,通过抽象的方法,就完成了操作系统和磁盘之间的解耦。所以最终操作系统对磁盘的管理,转换成了对数组的管理。
我们把磁道抽象程一个线性空间,假设我们要找下标为28888的扇区编号。
每个扇面有2w个扇区。 每个扇面有50个磁道。 每个磁道有400个扇区。
如图即为寻找的过程。
为什么操作系统(文件系统)和磁盘不以 512byte 为单位呢?
太小了,可能会导致多次 IO,进而导致效率的降低。
如果操作系统使用和磁盘一样的大小,万一磁盘的基本大小发生改变,那 OS 的源代码要不要改呢?那岂不是将硬件和软件(OS)进行解耦。
二、inode
500G 的磁盘空间抽象成每个元素是 512byte 的数组,那样非常大,不易管理,所以操作系统还要对这 500G 的数组进行拆分,比如这里拆分成了 100G、100G、150G、150G,所以这里只要管理好了第一个 100G 的空间,然后把管理的方法复制到其它空间,其它的空间也能被管理好。
把拆分的过程叫做分区,这也就是我们的电脑上为什么会有 C 盘、D 盘、E 盘等。至此我们仅仅是对空间进行划分,要把空间管理好还需要写入相关的管理数据,比如把中国 960 万平方公里,划分了不同大小的省份,要管理好一个省,我们不考虑地质差异等因素,只要一个领导、一个团队,他们把一个省管理好了,那么他们的管理方法就可以复制到其它省。同样的,刚刚我们分区的工作只是把中国划分成不同的省份,接下来我们还要分配每个省的省长、省中每个市的市长、市中每个镇的镇长等,以此来管理一个省。
将这种分配的过程叫做格式化过程,所谓的格式化在计算机中就是写入文件系统,也就是说我们要把文件系统写入某个分区中,这个文件系统的核心包括数据 + 方法,数据就类似这个省有多少人口、粮食等,方法就类似这个省有生育政策、耕种政策等。同样文件系统包含的数据就是文件系统的类型等,方法就是操作各种文件的方法。
当然不同的分区可以使用不同的文件系统,Linux 下就使用五六种不同的文件系统,Linux 可以支持多种文件系统,包括 Ext2、Ext3、fs、usb-fs、sysfs、proc。这就好比各个省份需要因地制宜地分配不同的团队。我们谈的都是 Ext 系列的文件系统,也不谈其它的文件系统如何,我们就认为磁盘上不同分区的文件系统是一样的。
因为一个省也很大,为了更好的管理,还要分配市长、镇长等,同样的分区后的 100G 空间还要再划分,比如这里划分了 10 组 10G 的空间,然后把它看作成一个一个的块组(Block group),一个块组中又有多个 4kb 空间,而磁盘存储是有块基本单位的,文件系统认为一块是 4kb,我们只要把一个块组管好,整个文件系统内的块组就能管好,所以问题又转换为怎么把这 10G 的空间管好,所以接下来划分的才是文件系统写入的相关细节,也是我们要研究的,这个区域的信息大家都有,可能略有差异。
这里 Linux 文件系统以 Ext 系列的为话题,上图为磁盘文件系统图(内核内存映像肯定有所不同),磁盘是典型的块设备,硬盘分区被划分为一个个的 block。一个 block 的大小是由格式化的时候确定的,并且不可以更改。例如 mke2fs 的 -b 选项可以设定 block 大小为 1024、2048 或 4096 字节。而上图中启动块(Boot Block)的大小是确定的,因为不同的文件系统可能略有差异。一般一个磁盘的 0 号分区的 0 号块组上的第一扇区存储着一些启动信息。掌握以下信息能够让一个文件的信息可追溯,可管理。
- 超级块(Super Block):是文件系统的核心结构,用于存放文件系统本身的结构信息,描述文件系统的属性信息,记录的信息主要有:bolck 和 inode的总量,未使用的 block 和 inode 的数量,一个 block 和 inode 的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。一般计算机启动时,Super Block 会被加载到操作系统,其中每一块组好像都有一个 Super Block,但实际可能 10 个块组中只有两三个有 Super Block。如果 Super Block 的信息被破坏,可以说整文件系统结构就被破坏了。
- GDT,Group Descriptor Table:块组描述符。Super Block 描述的是整个块组相关的信息,这里描述的是一组的属性信息,每一个块组都必需要有一个 Group Descriptor Table。
- 块位图(Block Bitmap):Block Bitmap 中记录着 Data Block 中哪个数据块已经被占用,哪个数据块没有被占用。其中比特位为 1 表示该 block 被占用,否则表示可用。
- Block Group:ext2 文件系统会根据分区的大小划分为数 个Block Group,而每个 Block Group 都有着相同的结构组成。
- inode 位图(inode Bitmap):每个 bit 表示一个 inode 是否空闲可用,比特位和特定的 inode 是一一对应的。其中,inode bitmap 中比特为 1 表示该 indoe 被占用,否则表示可用。
- i 节点表:存放文件属性,如文件大小、所有者、最近修改时间等。
- 数据区:存放文件内容。
我们说过文件 = 内容 + 属性。这里的内容和属性采用分离存储,属性放在 inode Table 中。一个组中可以放多少个 inode 是一定的,基本上一个文件或目录一个 inode,inode 是一个文件的所有属性集合,属性也是数据,也要占用空间。所以即便是一个空文件也要占用空间,这里的属性集合包含文件权限、大小等,但不包含文件名。
内容放在 Date blocks 中。比如这里的块组是 10G,那么 inode Table 占 1G,Date blocks 占了 8G, 一个 inode 是 512byte,粗略的算一下,1G 大概 42 亿多字节,除以 512 大概也有几千万,所以这样一个块组能保存几千万文件的 inode 信息。这里 inode Table 和 Data blocks 的划分可能会出现你用完了、我没用完,或者你没用完、我用完了的情况,这种情况并没有有效的方法解决。
Date blocks 相当于一个数据块集合,报错的都是特定文件的内容,它以 4kb为单位(Date blocks 可以理解成多个 4kb(扇区*8)大小的集合),对应的数据块属于哪些文件,是由 Data Blocks 和 inode Table 维护的。如下图,inode Table 是一个大小为 128字节的空间,包含了若干大小相同的块,这些块有不同的编号,对应就是对应文件的属性,Data blocks 也包含了若干大小相同的块,这些块也有不同的编号,对应就是文件的内容。此时新建文件或目录,就给文件申请 1 号 inode,并把文件的各种属性写入到 1 号 inode,1 号 inode 中包含了一个数组 block b[32],比如 1 号 inode 需要 2 个数据块,所以 [0] = 2,[1] = 3,所以 1 号 inode 就可以找到对应的数据块。换而言之,要在磁盘上查找一个文件,我们只需要知道这个文件的 inode 是多少,因为 inode 需要标识唯一性,每一个 inode 块都要有一个 inode 编号,至此,我们知道真正标识文件的不是文件名,而是文件的 inode 编号。既然 inode 大小是确定的,万一文件是 10 个 T,此时数据块就不够了,文件系统的处理策略是数据块不仅可以保存数据的内容,还可以保存其它数据块的编号,它类似于 b+ 树。换言之,对于保存较大的文件,可能就需要多级索引的形式。
- 我们用ls -li 就能看到文件的编号了
目录是不是文件呢?
我们的上图中就是目录,也有自己的inode,目录也要有自己的属性。
目录也有数据块,里面存放的是文件名和对应文件inode的映射关系。(这就是为什么同一目录下没有同名文件。)
echo “hello world” > file.txt 是先启动进程,这个进程当然知道自己所在的目录,那么它就可以拿着 file.txt 文件名找它对应的 inode,把数据追加到对应的数据块中。所以说 inode 不存储文件名,只是往目录的数据块中写入文件名和文件对应的 inode。
在目录下,我们没有"W"权限时,无法创建文件
因为无法把该映射关系写入到数据块中。
在目录下,我们没有"R"权限时,无法读写文件
因为无法读取到映射关系,找不到对应的inode,读不到文件
1、创建一个新文件的 4 个操作
(1)存储属性
内核先找到一个空闲的i节点(这里是263466)。内核把文件信息记录到其中。
(2)存储数据
该文件需要存储在三个磁盘块,内核找到了三个空闲块:300、500、800。将内核缓冲区的第一块数据复制到 300,下一块复制到 500,以此类推。
(3)记录分配情况
文件内容按顺序 300、500、800 存放。内核在 inode 上的磁盘分布区记录了上述块列表。
(4)添加文件名到目录
新的文件名 linux_1。linux 如何在当前的目录中记录这个文件?内核将入口(1052214,linux_1)添加到目录文件。文件名和 inode 之间的对应关系将文件名和文件的内容及属性连接起来。
touch 一个空文件 file.txt,ext* 文件系统,做了什么工作?
它先在 inode Bitmap 申请没有被使用的比特位,然后把文件对应的属性信息 inode Table 里写,再把申请好文件的文件名和 inode 编号写入 Data blocks 建立映射关系。如果想 echo “hello world” > file.txt,现在是在当前目录下,file.txt 和 inode 就找到了,再通过 file.txt 文件的 inode 去对应的 Data blocks 中写入数据。所以就能理解为什么大多数操作系统下同一个目录中不允许存在同名文件。一方面是不方便人查看,另一方面是若一个目录下有同名文件了,那么 inode 去操作文件时操作的就不知道是哪个文件了。
Block Bitmap 和 inode Bitmap 是位图,就是用比特位 0 / 1 来表示的。Block Bitmap 用来标识数据块的使用情况,inode Bitmap 用来标识 inode 的使用情况,每个比特位都对应一个块。换而言之,当你新建文件时,它并不是遍历 inode 区域,因为这样做太慢了,它只需要在系统启动时将 Blok Bitmap 和 inode Bitmap 预加载到系统中。要新建文件就把 inode Bitmap 的比特位由 0 置为 1,文件需要多少数据块,就把 Block Bitmap 的比特位由 0 置为 1。所以我们可以通过位图快速的完成 inode 的申请和释放,同时也能确认当前磁盘的使用情况。但是位图依然还是需要去遍历哪些使用和未使用,以及做位操作等,所以这里通过 Group Descriptor Table 来管理。
如何理解删除文件?
计算机中删除一个文件并不是真正的删除,而是把那块空间标识为无效,就像拷贝一部电影到 U 盘需要 1 分钟,但是删除 U 盘上的电影只需要短暂的一两秒;亦或是盖一个房子需要 1 年,而销毁一个房子只需要在墙上写上拆 。而现在理解的是不用把 inode 属性清空,不用把 inode 对应的数据块清空,只需要把两个位图中对应的比特位由 1 到 0,再把所在的目录下中的对应的映射关系去掉,此时空间就是无效的,当下一次再新建文件时,就可以直接把无效的空间覆盖。
按上面这样说,删除后的文件当然可以恢复,Windows 下的回收站就是一个目录,当你删除时就是把文件移动到回收站目录下,然后把其它目录下数据块中的映射关系移动到回收站目录下的数据块中。Windows 下就算把回收站的内容删除也是能恢复的,Linux 下如果要恢复删除的文件是有一些恢复工具的,但有可能在恢复过程中创建各种临时文件,可能就会把想恢复的文件的信息覆盖掉。如果想自己恢复删除的文件,就需要更深入的了解文件系统原理。
三、软硬链接
硬链接是通过 inode 引用另外一个文件,软链接是通过名字引用另外一个文件。
1、软链接
我们能看到test.exe与testlink建立了软链接,test,exe有独立的inode,说明是一个独立的文件。soft.link 中的数据块中保存着它指向的文件 testLink.txt 的路径
就类似于 Winodws 下的快捷方式,比如桌面看到的软件保存的是其它的路径,在系统中可能你要运行的可执行程序在一个很深的目录下,就可以在较上层的目录中建立软链接。
2、硬链接
test1.exe与testlink1建立硬链接,我们发现他们的inode和硬链接数是一样的。
硬链接本质上是在特定的目录数据块上新增文件名的指向,我们之前介绍了引用计数,在每个inode的内部都有一个叫做引用计数的计算器的文件的inode的映射。这个计数器记录的是有多少个文件名指向这个inode。
在这个硬链接中,相当于是给testlink1取了一个别名。
我们在删除文件时干了两件事情
- 在目录中将对应的记录删除。
- 将硬连接数 -1;如果为 0,则将对应的磁盘释放。
3、硬链接的应用
我们能看到一个"."和"..",一个点为当前目录的别名,两个点为上一个目录的别名。
我们在dir目录下创建了dir1目录,为什么硬链接数变成了3了呢?
在原来的2的基础上,在dir1目录下有".."隐藏文件,是dir的硬链接
所以硬链接能够维持Linux的目录结构。
通常用来进行路径的定位,采用硬链接可以进行目录间的转化。
硬链接不能给目录建立。
原因就是允许目录的硬链接可能会打破文件系统目录的有向无环图结构,可能创建目录循环,这可能会导致fsck以及其他一些遍历文件树的软件出错。这可能会出现一种你的父目录是你的子目录这种及其奇怪的事情出现,其实就是硬链接以后的目录可能是你的父目录,这导致很多遍历系统的命令如果不跟踪inode的话就没办法用了,因为可能会导致无限循环比如du,ls -r, pwd。
那为什么又有"." ".."与目录进行硬链接呢?
因为这是操作系统自己写的,也是特例,操作系统不允许与目录进行硬链接,限制的是用户,因为操作系统不相信任何用户。
4、软链接的应用
我们先创建一个文件
编译该文件形成可执行程序test.exe并移入我们创建的目录里。
进行软链接
最后我们发现运行exe.link就能直接运行我们移入目录里的可执行文件。
这个过程就是我们在上面写的,我们在window系统中点快捷方式就能直接运行该软件。