Linux:基础IO---inode
文章目录
- 1. inode
- 1.1 未被打开的文件
- 1.2 认识硬件
- 1.3 对磁盘进行逻辑抽象(解构)
- 1.4 文件系统
- 序:在上一个章节缓冲区中,我通过将几个实例进行对比,引出了C语言级别即用户级别的缓冲区的概念,将用户级缓冲与内核级缓冲区进行了比较,从而了解了什么是缓冲区,缓冲区的缓冲加载策略是什么,而本章将深入探寻文件系统,去了解文件的本质是什么?未打开的文件又是如何存储的,存储在哪里,又是如何被访问的?诸君且听我娓娓道来!!!
1. inode
1.1 未被打开的文件
在之前的文章(Linux:基础IO—文件描述符)中曾提到过已经打开的文件是如何调用的,从而了解到了文件系统的概念,而现在我们要研究的对象是未被打开的文件。
问题一:之前我们的研究对象是已经打开的文件,如果一个文件没有被打开呢?
如果一个文件没有被打开,那么该文件一定是在磁盘中存储的。
问题二:那我们要关心什么问题呢?
1. 路径问题
2. 存储问题
3. 获取问题(属性和文件内容)
4. 效率问题
文件 = 文件属性+文件内容—>磁盘上存储的文件 = 存文件的内容和存文件的属性
文件的内容 — 数据块
文件的属性 — inode
Linux的文件在磁盘中存储,是将属性和内容分开存储的!
1.2 认识硬件
在日常生活中,我们总能见到一种用来存储音频的光盘(CD)
同样可以类比,如果我们要存储文件,就一定要有一个载体,这个载体就是磁盘!!!
如下图所示:
其中一个盘片有两个盘面,上下个一个,磁头是一面一个,磁头和盘面不接触。磁盘是永久性存储介质,可以一直存储数据,而内存是断电易失性存储介质,内存需要不断的通过电流来进行数据传输,一但断电,就会丢失所有数据。我们的未被打开的文件就存储在磁盘当中。
磁盘的存储构成:
磁盘被访问的最基本单位是扇区—512字节(0.5KB)甚至是4KB。
我们可以将磁盘看做由无数个扇区构成的存储介质。(数组)
要把数据存到磁盘,第一个要解决的问题是定位一个扇区:那一面(定位哪个磁头),哪一个磁道,哪一个扇区。
对于磁盘来说:
- 运动越少,效率越高
- 运动越多,效率越低
所以,在软件设计上,一定要有意识的将相关数据放在一起。
CHS寻址方式:
Cylinder | Header | Sector |
---|---|---|
磁道 | 磁头 | 扇区 |
1.3 对磁盘进行逻辑抽象(解构)
一条磁带在物理上就是一个绕在一个柱子上的同心圆,如果我们将该磁带延展开,那么这个磁带就是一个线性结构,我们将这个磁带进行抽象,和我们的磁盘进行结合,就会发现,其实这条磁带上的某一块区域,对应的就是一个盘面(所以,这就是一个基于扇区的数组!),而一个盘面上又有很多个扇区,其中,某块区域的扇区就是一个磁道。
例子:任意一个扇区都有下标(和数组下标一样),如果现在,假设:每个盘面有2w个扇区,每个盘面有50个磁道,每个磁道有400个扇区。
假设一个扇区的编号为28888,我们怎么定位到该扇区呢?
哪一面:
28888/20000=1
28888%20000=8888哪一磁道:
8888/400=22
8888%400=88哪一扇区:88
所以,该扇区就是第1面的第22个磁道上的第88个扇区!!!
物理地址(CHS地址):
C磁道:22
H磁头:1
S扇区:88逻辑扇区地址(LBA地址):28888
其中LBA地址和 CHS地址可以互相转换!!!
回归硬件:不仅仅CPU有“寄存器”,其他设备(外设)也有,其中磁盘就有他自己的寄存器。
磁盘中的寄存器有控制寄存器、数据寄存器、状态寄存器和地址寄存器。
控制寄存器里面存的是IO方向,告诉磁盘我是要读入还是写入数据。
数据控制器里面就是要读入的地址或要写入的数据等
地址寄存器里面是LBA地址,如果要写入数据,就要存往哪里写,该LBA地址会被磁盘转为CHS地址!!!
状态寄存器里面存的是此次读入或写入是否成功
1.4 文件系统
问题一:对于一块800G的硬盘,我们怎么去管理呢?
我们是不是会把他分开啊!就像电脑,一共有800G,但是我们将这800G硬盘分成了C盘、D盘、E盘、F盘等,这种分治思想就是,只要管好了每个小的盘,大的盘也就管理好了。
但我们知道即使分成了这么多盘,拿D盘来说,里面可能还有200G,我们又要怎么管理者200G呢?
答案还是一样,分区,对这个盘进行区域划分,就像对程序地址空间那样,定义一个结构体,里面有start和end,我们将一个盘划分为多个这样的结构体,所以,我们只要管理好了这个结构体内的东西,就能管理好整个盘,盘管理好了,800G的内容也就管理好了!
struct partition{
int strat;
int end;
}
struct partition part[N];
我们将这样的几个结构体通过数组来管理起来,假设,此时,200G的内容已经被我们进行区域分化,划分为很多个10G大小的区域,我们又该怎么管理呢?— 先描述,再组织!!!
现在让我们来探寻一下,操作系统是怎样管理这一个个的分区,从而实现了对整个大内存的管理!!!
对于一个200G的盘,我们这样进行划分,其中Block group 0就是数组下标为0所对应的结构体的范围。
其中Boot Block通常会记录操作系统启动的信息,里面存放着开机相关的字段,放在开头,与区域划分有关,一但出了问题,计算机就会瘫痪。
Data blocks:存文件内容的区域,将n个扇区以块的形式呈现出来(n == 2/4/8)— 文件系统的块的大小,其中,一个扇区有512个字节,一个块可能是(1024(1kb)/2048(2kb)/4096(4kb)字节)
inode Table:其中的inode是单个文件的所有属性,占128字节,一般而言,一个文件只会有一个inode,inode有唯一编号(ls -li可以查询某个文件的inode编号),所以inode Table就是存inode的一张表。其中,文件的属性和内容都是分开存储的。
在Linux中,文件的属性中,不包含文件的名称!!!
Linux系统里面标识文件用的是inode编号!!!
对于一个文件,操作系统会生成他的struct,来记录该文件的几乎所以属性,其中就有inode编号、文件类型、文件权限、引用计数、拥有者、所属组、ACM时间和block数组等,其中该block数组就是用来指向该文件所对应的inode的数据块,假设该数组大小为15,其中包含12个直接索引,2个索引,1个三级索引。
直接索引:里面直接存的是数据块的下标,直接去数据块里面找内容
两级索引:里面存的不是文件内容,而是存储我们的文件的块号,一个数据块大概能存8MB。的数据块下标。
三级索引:能存的更多了,是以多叉树的形式来存数据块的下标,能存储大文件。
一般而言,inode的编号是连续的,但如果你删了一个文件,该编号可能会被重新被操作系统分配
问题二:我怎么知道,这些块中哪些被使用,哪些没有被使用呢???
Block Bitmap:比特位的位置和块号映射起来,比特位的内容如果是0,表示该块没有被使用过或已经失效,如果为1,就说明正在被使用!
问题三:那么,我们删一个文件的时候,用不用把块(文件内容)清空呢?
答案是不用,当我们要删除一个文件的时候,只需要通过他的inode找到对应的块数据,然后在对应的block bitmap置0,然后再讲对应的inode bitmap置0就行,对应的内容没必要去删,很麻烦会降低IO效率,直接将对应的块数据置0,在后面被别人访问的时候该块的数据就会被覆盖。
总结:删除一个文件是对该文件编号所对应的Block Bitmap和inode Bitmap清零,inode Table和Data blocks都没动!!!
inode Bitmap:比特位的位置和inode的编号映射起来,比特位的内容决定了inode是否是有效的!!!
Super Block:文件系统的基本信息,是用来描述整个分区的基本使用情况
比如:该分区一个有多少组、每个组的大小、每个组的inode数量、每个组的block数量、每个组的起始inode和文件系统的类型与名称…
其中Super Block不一定会在每一个分组中,因为更新数据,要统一更新,很麻烦,但是如果只有一个组有Super Block,万一该部分的数据出了问题,操作系统不就崩了吗?于是就让Super Block点点星星放在组中,有的组有,有的组没有,于是该Super Block里面有一个魔数(一种随机数),在Super Block的一个固定位置,只要将某几个组的Super Block的信息一比较,就能知道这个数据是否是正确的了!!
每个分区在被使用前,都必须提前将部分文件系统的属性信息设置进对应的分区中,方便我们后续使用这个分区或分组 — 格式化。
Group Descriptor Table(GDT):块组描述符,描述块组属性信息
GDT内容如下:
//Structure of a blocks group descriptor
struct ext2_group_desc
{
__le32 bg_block_bitmap; /* group中block bitmap所在的第一个block号 */
__le32 bg_inode_bitmap; /* group中inode bitmap 所在的第一个block号 */
__le32 bg_inode_table; /* group中inodes table 所在的第一个block号 */
__le16 bg_free_blocks_count; /* group中空闲的block总数 */
__le16 bg_free_inodes_count; /* group中空闲的inode总数*/
__le16 bg_used_dirs_count; /* 目录数 */
__le16 bg_pad;
__le32 bg_reserved[3];
};
问题四:新建/删除/查找/修改一个文件,操作系统要做什么?
** A. 如果我们要新建一个文件,操作系统可以通过该新建文件的路劲知晓该文件在哪个分区的哪个组内,操作系统会检查inode Bitmap内没有被使用过的位置,然后操作系统会给该文件分配一个inode(不同分区的组的inode分配范围不同,所以知道了该文件的inode就知道了该文件在哪个组里面),然后通过这个inode找到该文件在inode Table中的位置,然后将该文件的属性填入到里面去,然后将他对应的inode Bitmap置“1”,如果该文件还有内容要写入,操作系统会先确认你要写入的内容的大小,操作系统就会通过该文件的inode去查看Block Bitmap内没有被分配的数据块,然后将这些块号填入到inode Table中对应的inode的属性当中,然后将要写入的内容直接写到对应的数据块中!!!**
** B. 当我们要删除一个文件时,操作系统会拿到该文件的inode去找他inode Table中对应的属性,通过属性中存放数据的数据块的下标,将对应的Block Bitmap的位置置“0”,然后在将该inode对应inode Bitmap的位置置“0”,就将该文件删除了,数据块中的内容不用去删除,很麻烦,效率也不高。删除了该文件对应的数据块,就表示该数据块可以被别人申请访问,并覆盖该数据块的内容!!!**
** C. 当我们要查找一个文件,就要通过他的inode找到他在inode Table中对应的属性,从而找到该文件的相关内容,查找该文件的内容,就去访问该文件的数据块,查找该文件的权限,就去访问该文件的权限等**
** D. 修改一个文件也是同理!!!**
在Linux操作系统中,一个文件一个inode,每一个inode都有自己的编号,要注意的是:inode表示文件的所有属性,文件名并不属于inode内的属性
我怎么知道文件的inode编号,使用者从不关心inode编号,只知道文件名?接下来让我们通过理解目录来解答这个问题!
问题五:如何理解目录?
目录也是文件,也有自己的inode,也就是说,目录也有自己的属性。
1. 目录有内容吗?
有,因为文件 == 内容+属性2. 既然有内容,那目录需不需要对应的数据块?
一定要,目录下也是有文件的。3. 数据块里面放着什么呢?
放该目录下,文件名和对应文件的inode映射关系,我们可以将该文件名看出对应inode编号的key值,将inode编号看出value值。
1. 为什么同一个目录下,不能有同名文件?
因为文件名是作为一个key值来映射对应的文件的inode编号的,所以不能有同名文件,不然就会有歧义。2. 为什么目录下,没有w权限,我们就无法创建文件?
没有写权限,即使真的创建了文件,操作系统也没法将创建文件的文件名和对应映射关系的inode编号写入该目录的数据块中,也就无法创建。3. 为什么目录下,没有r权限,我们就无法查看文件?
因为我们要查看该目录下的文件的信息,就需要去访问该目录下的文件名对应映射的inode编号,而我们没有读权限,就没法读这个inode编号,也就无法查看文件了。4. 为什么目录下,没有x权限,我们就无法进入这个目录?
当我们通过上一级目录或得该目录的inode时,由于没有执行权限,我们根本就无法通过访问该目录改变当前的PWD。
问题六:目录也是文件,也有inode编号,那么目录的inode编号怎么来的呢?
我们知道一个目录的inode就要去看他的上一级路径的目录去查他的inode,这样一直递归上去,直到root路径。由于root的文件名是确定的,我们就通过该文件名找到与之映射关系的inode编号,然后在递归回去,就能知道某个目录的inode编号了,这就是为什么,系统要访问一个文件,都要带一个路径。
总结:
本章,从硬件出发,由宏观到微观,由具体到抽象,围绕研究对象未被打开的文件来讲解,操作系统是如何对一个大块的磁盘进行管理的,还是那6个字,先描述,再组织!!!通过对一小块的分组的管理从而管理好一个分区,从而将一整块磁盘管理好,而后通过引入inode的概念讲述了什么是文件系统,什么是目录,文件是如何被访问的,又会存储在哪里!!!希望我的总结能帮助到大家,谢谢!!!