目录
前言:
前文我们介绍了磁盘,介绍磁盘的原因是因为我们需要在理解文件系统之前,通过磁盘的了解,介绍一些文件相关的内容,比如文件是如何在磁盘里面存储的,什么是CHS定址法,为什么OS不使用CHS定址法,什么是LBA块等。
我们今天的介绍顺序是,先简单搭建起来对文件系统的理解,再深挖细节问题。
那么,进入今日的话题吧!
简单理解文件系统
对于文件系统框架的搭建,我们可以先从这个指令进入:
即stat指令,查看文件的信息,这个信息,是文件的属性?还是文件的内容呢?
我们知道文件 = 属性 + 内容,但是我们常常关注的都是文件的内容,对于文件的属性我们关注的并没有那么常见。所以要理解文件系统的框架,我们要从文件的属性进入。
由stat提供的信息来看,我们看到了File:main.c,这个代表的是文件名,Size代表的是文件的大小,Blocks?IO Block? Inode?这些都是什么?
不急,我们再次引入上文的话题:
对于上文,将磁盘从非线性的转换为了线性的,然后线性的空间我们看成了数组,那么数组,我们可以通过位图的类似做法,找到数据的存储地址。但是因为磁盘取数据的特殊点,取数据都是一次性的取4kb数据,所以OS为了方便将数据分为了多个块,也就是LBA块,分好之后,找到一整块空间就十分容易了。
所以我们对于文件的管理从磁盘,到了CHS,到了数组,到了LBA,最后无非就是搞清楚LBA里面究竟有什么就可以了:
形象的图片就是这样,那么块组里面,我们分清楚了有Super Block, Group Deseciptor Table, Block Bitmap, Inode Table, Data Blocks。
那么我们从哪里开始讨论呢?
从Data Blocks开始讨论:Data Blocks是数据块的翻译,也就是文件的内容都是放在这里的,但是因为方便介绍,我们将Data Blocks的大小缩小成了和其他差不多的大小。对于该块,占据的空间大小应该是整个块组的95%以上。
那么对于Data Blocks里面都有什么呢?
看起来是非常抽象的,因为Data Blocks里面存储的都是一个一个的数据块,大小都是为4kb的,取的时候就直接将该数据块丢出去就行了。
对于这么多的数据块,都是只存储文件的内容的。
并且,我们知道,文件 = 内容 + 属性,对于文件内容属性而言,Linux特定的文件系统是将文件的属性和内容分开存储的,这点我们先记住。对于Data Blocks我们就探讨到这里。
那么下一个,就是Block Bitmap,相信在C++学习的时候,同学们都是知道位图这个概念的。
最开始介绍位图的时候,都是通过的判断数据是否在一堆数据的集合里面,这里同理,引入block Bitmap就是为了判断是否某个数据块是否存在数据,这里位图就不多介绍了,但是引入了位图,确实能在遍历数据块上节省极大部分的时间。
下一个就是非常重要的inode Table,inode Table成为i节点表,存放的是文件的属性,文件的大小,所有者,最近的修改时间等。这是inode Table。
那么文件的属性一般都有什么呢?
cpp
struct inode
{
int size;
mode_t mode;
int creater;
int time;
...
int inode_number;
int datablocks[N];
};
我们拿几个非常常见的出来举例,文件大小,文件的权限,创建时间等,但是最重要的,我们应该关心inode_number和datablocks[N]。而这个块的名字是inode_table,所以它所处的空间自然是这么多个结构体所处的集合。
所以!!文件的属性不过是一个一个完全相同的结构体!!
而在里面,inode的结构体如何分区的呢,都是通过inode_number进行分区的,那么datablocks的作用是什么呢?我们使用文件的时候,通过了inode_number找到了对应的文件属性结构体,我们需要找到内容,就需要datablocks,这个数据指向的内容就是block Data的空间:
像这样,比如N等于12的时候,前11个直接指向的数据块,但是我们没有办法找到对应的大文件,所以第12个,指向的Data Blocks里面也存放的指针,指向了其他的数据块,就有一种指数的感觉。
那么数据还不够,13 14后面都可以指向,并且就不是一层关系这么简单了,是一层指向一层,一层又指向一层,套好几层最后才指向数据,这样就能找到大文件的数据了。
这种查找数据的方法叫做ext2文件系统,我们目前大多数使用的都是ext2,还有ext3 ext4等。
所以对于inode结构体我们就知道了个所以然,那么inode bitmap?那不就是同理了吗!!
通过inode bitmap找到表里面的某个位置是否存在文件的属性,然后进行后续的操作。
那么现在,我们就清楚了inode table, inode bitmap, data blocks, data bitmap。对于剩下的两个,比如GDT,也就是Group Descriptor table,翻译过来叫做块组描述符,描述的是块组的属性信息,其实就是这个块的信息了,这更加体现了一种分治的思想。
对于Super blocks,它的名字可就厉害了,叫做超级块,存放的是文件系统本身的结构信息,而且不是每个组都有的,可能几个组才回有一个。比如存放的有inode使用的个数,未使用的个数,datablocks的使用个数,datablocks未使用的个数。这些都是超级块所要记录的内容。
那么提问,既然是记录所有的结构信息,为什么要整这么多个呢?
因为磁盘是可能损坏的,如果损坏的时候,刚好磁头给一个超级块的内容消除了,那么这不就完蛋了吗??所有多存储几个,增加了容错率!!
说了那么多,inode是十分重要的,查看inode是-i:
细节理解
对于上文来说,我们已经理解了块里面的6个成员,现在我们来谈论具体细节问题:
第一个细节:
如何通过inode找到对应的data blocks?
因为inode和data都是以分区为单位的,所以inode可以通过数组datablocks找到的!
第二个细节:
我们使用的不是一直都是文件名吗?好像并没有使用inode,可是我们改变文件内容的时候,一直使用的都是文件名啊?
这个时候,我们就要谈谈目录了,目录 = 文件属性 + 文件内容,这里可不要认为目录就不是文件了,提问,目录的文件内容是什么呢?
目录的文件内容 = 文件名和inode的编号关系,当我们创建好了一个文件,文件所在的目录就会记录该文件和inode的关系,方便找到并进行修改。
所以目录里面的文件名就是和inode映射关系!!
到了这里,相信同学们能理解为什么一个目录下不能创建多个同名文件了吧?因为如果创建了同名文件,映射关系一乱,整个文件系统就瘫痪了。
可是!!当我们修改目录的时候,用的还是目录名啊!!又是什么存储的目录和inode的关系呢?
看这个:
pwd打印出来了路径,是因为环境变量PATH,而我们在该目录下修改文件,需要找到对应的inode,找到inode之前,我们需要找到该文件所在的目录,找到该文件所在的目录,我们就应该要找到目录的目录,最后直接到了根目录!!
而在根目录这里,就是OS已经提前加载好了有关路径缓存的信息了,这里涉及到了知识点有结构体dentry,格式化,挂载等。我们先不管,我们只需要知道,Linux会缓存路径信息,从而我们可以通过文件 目录来修改对应内容。
现在,我们顺便回顾一下目录的r w,当我们将r去掉之后,也就是没有可读权限:
此时目录就不允许我们读取文件内容,本质就是不让我们知道文件名和inode的映射关系。
这里就不演示了,当我们没有了w权限之后,我们无法创建文件,本质就是因为不要我们创建文件名和Inode的映射关系!!
所以,对于细节2,我们可以得出结论:
1 目录下不能创建同名文件,因为会打乱映射关系
2 查找的文件顺序是通过文件名查找对应的inode编号
3 r是让我们找到对应的映射关系,w是不让我们写入对应的映射关系
第三个细节:
我们如何理解文件的增删查改呢?
文件增,就是找到对应的映射关系,在datablock里面增加数据,查就是找对应的映射关系,改也是通过inode,修改datablocks里面的数据关系。
那么删除呢?
其实这里的删除是一种伪删除。
即将Inode bitmap , datablock bitmap都置为0即可。这是一种删除。
我们对于文件系统的框架有了简单的理解,那么理解接下来的软硬连接动态库就会轻松很多了。
感谢阅读!