UNIX系统中每个文件都有一个唯一的索引节点index_node,其中包含文件所有者、存取权限、文件长度、文件数据在文件系统中的位置等;
我们在使用文件的时候,使用文件路径来表示文件,核心将文件路径转换成文件的索引节点;
本章的算法的层次处在上一章所解释的高速缓冲算法之上;
iget:返回一个先前标识了的索引节点;
iput:释放索引节点;
其中iget和iput操作的内存中的索引节点;
bmap:做字节偏移量到磁盘块号之间的映射;
namei:做路径名与索引节点的映射;
alloc与free:分配与释放磁盘块;
ialloc ifree:为文件分配及释放索引节点;
索引节点存在两份:磁盘上的和内存上的,之所有把其放置到内存上一份是为了做缓冲,提高效率,同时这里做的缓冲完全是使用的第三章的buffer设计;
索引节点以静态形式存在于磁盘上,核心把它们读进内存索引节点表中以操纵它们,由如下字段组成:
注意:这里索引节点的缓冲方式和块设备的缓冲方式类似,即都会包含一个空闲表、散列队列用来管理可用和不可用的缓冲节点;
内存中的索引节点拷贝除了磁盘索引节点所包含的那些字段以外,还包含:
那么这个引用计数有什么用呢?
获取的是内存中的索引节点,也就是说这部分算法直接交互的对象还是buffer,即缓冲区;
[外链图片转存失败(img-YRKw05sl-1567060305156)(en-resource://database/3308:1)]
核心用文件系统和索引节点号来标识特定的索引节点;iget算法和getblk算法如出一辙,核心将设备号和索引节点号映射到一个散列队列上,并且搜索该队列以便找到该索引节点,如果找不到,就从空闲表中分配一个索引节点,并且对它上锁;
然后就去磁盘中找对应的磁盘块号咯,块号=((索引节点号-1)/每块的索引节点数目)+索引结点表的起始块号;
知道了块号之后还需要使用缓冲区里的函数:bread,读出该块,然后计算该索引结点在该块中的字节偏移量:((索引节点号-1) mod (每块的索引节点数目))*磁盘索引节点大小;找到之后,拷贝到内存索引节点中,把它放到正确的散列队列中,并且将它的内存引用计数置为1;
当进程打开一个文件时,它就把索引节点引用计数增1,当引用结束的时候,比如当进程关闭一个文件时,就把索引节点啊引用计数间减1,当减到0的时候,意味着锁结束了;
算法iget,如果核心试图从空闲表中取出一个索引节点,但发现空闲表是空的,则报告一个错误;这与处理磁盘缓冲区是不同的,思考为什么不同?
算法iget会返回一个上了锁的索引节点数据结构,而且其引用计数比原来大1,上面图片中的文字还是挺重要的,一定记得需要从空闲表中将其摘除出来;
也是针对的缓冲区里的索引节点,这里和之前高速缓冲不同的地方是加入了空闲统计;
当核心释放一个索引节点时iput,将它的索引节点引用计数值减1;如果该计数值降为0,且它的内存拷贝和磁盘拷贝不同,则还需要写磁盘;
其中文件数据的改变、文件存取时间的改变、文件所有者或存取许可权的改变都会引起它的内存拷贝与磁盘拷贝的不同;
核心会把该索引节点放置到空闲索引节点表中;这里和磁盘缓冲区的算法十分类似。
三级索引结构;
索引节点如何管理文件对应的磁盘块的分配?
下面的算法完成了从字节流的字节偏移量到磁盘块号的转换,就是算就完事了!!!
思考大文件的存取,需要读取三个磁盘块才能索引到3级索引的数据,而且中间可能会有等待缓冲的过程,会非常耗时耗力;所以得出结论:读取小文件是很快的,存取大文件是比较费劲的;
这里还是很有学问的,先简单了解下其基本原理和思想叭,华为有做的超级文件系统,据说很牛!!!
所以不要仅仅止步于Unix和Linux0.11,还要有更宽广的视野去探究,探索之外的世界;
目录虽然可以当成普通文件对待,但是目录对应的磁盘块的内容却是不一样的;
目录在文件名到索引节点的转换中扮演了重要角色;
目录也是文件,只是它的数据是一系列登记项,每个登记项由一个索引节点号和一个包含在这个目录中的文件名组成;
UNIX系统V把分量名限制为14个字符,索引节点号占两个字节,所以一个目录登记项长度为16个字节;
每个目录都包含.和…文件名,分别是本目录和它的父目录的索引节点号;
对于目录的权限的解释:
读目录与搜索目录的差别?一个是一层遍历,另一个是多层嵌套遍历?
这一小节实际上讲解的就是:如何根据路径名搜索索引节点;
系统中的第一个进程-0的当前目录是根目录。其他进程的当前目录都是从该进程被创建时它的父进程的当前目录出发的;
上述算法中使用工作索引节点的中间索引节点;
其中的核心是验证工作节点确实是目录;不然就违反了“非目录文件仅能是文件系统树的树叶节点”的要求;同时进程还需要具有搜索目录的许可权;
搜索到之后,释放块brelse(因为遍历该目录下的文件的时候用到了bread),以及释放旧的工作索引节点input,并且分配匹配了分量的索引节点;
超级块由如下字段组成,总结来说,文件系统玩的就是inode+磁盘块,而这里的超级块就是inode和磁盘块信息的总结;
核心是使用到了超级块中的空闲表;
文件系统包含一个索引节点线性表,如果它的类型字段为0,则说明这个索引节点是空闲的;当一个进程需要一个新的索引节点时,理论上说,核心能搜索索引节点表,以寻找一个空闲节点,但是这样太慢了,很多时候甚至需要去磁盘找,所以,超级块中包含了一个数组,以便把文件系统中空闲的索引节点号缓存起来;
解释下“铭记”的索引节点:如果超级块中没有空闲,即去磁盘搜索,当搜索到超级块装不下时停止搜索,此时记录下最高序号的索引节点,即下次搜索时从铭记索引节点开始;
释放一个索引节点的算法ifree:
P116,的确有竞争条件的地方都比较复杂,所以好好考虑竞争条件,毕竟涉及到IO的地方会频繁涉及到sleep;
mkfs程序把一个文件系统的数据块组织到一张链表中,表中的每个链是一个磁盘块,块中包含的是一个数组,数组的分量是空闲磁盘块号,并且数组中有一个分量是下一个链表的块号,示意图如下,蕴含着一些智慧,别小看这些实现细节,这些实现细节决定了效率呢!
alloc算法:
程序mkfs试图组织好空闲块号的原始链接表,使得分配给一个文件的那些块号互相靠近,这样读文件时就能减少磁盘寻找时间和等待时间;所以有益于提高性能;
释放磁盘块的算法free:
索引节点和磁盘块的缓冲策略是不一样的,索引节点没有形成链表,而磁盘块的缓冲却形成了链表,所以原因有如下:
其实这些东西,我认为可能需要获得用户使用的具体情况去优化,比如统计出一个比较平均的磁盘块资源消耗次数和索引节点消耗次数,那么再去重新设计,就很优化了;
其实那些所谓的基于AI去优化多少有点扯淡,不过是通过统计数据去重新进行设计,比如缓冲区分配多少,如果才能做到最优,就是利用数学方法去做而已;
除了目录、普通文件外还有两个文件类型:管道文件和特殊文件;
emmm管道···这种进程间通信方式不应该涉及到IO吧?不造~~~
管道的关键点是先进先出;
特殊文件:
块设备特殊文件和字符设备特殊文件,两种文件都指明了设备,因此它们不引用任何数据;
索引节点含有两个称为主与次的设备号;
主设备号指出终端、磁盘等设备类型;
次设备号指出这类设备的装置号;
我只觉得很乱,很乱,很乱,复习了一遍发现理清楚了很多思路;
索引节点有两个版本:磁盘拷贝和内存拷贝;
在系统调用:creat, mknod, pipe, unlink期间,算法ialloc, ifree控制着给文件分配磁盘索引节点;
在进程存取文件时,算法iget, iput控制着内存索引节点的分配;
算法bmap根据字节流中的字节偏移量来确定磁盘块的位置;
算法namei将文件名转换成内部使用的索引节点;
最后核心使用算法alloc和free给文件分配新磁盘块;
算法之间的交互作用所引起的竞争条件是复杂性的体现;
这里使用的算法是:链接表、散列队列、线性数组,这也决定了算法的简明性;
Come On, 复习走起;
不造,好像也不是hash函数的原因;
别的进程可能影响了原有的数据,需要重新确定是否满足要求鸭
先找内存版本的索引节点
找到之后获取磁盘索引节点号
根据磁盘索引节点号找对应的磁盘号
然后写相应的字节,完事!
意味着可抢占,实时性提高,并且iget和iput中要对应sleep所引起的数据不同步问题
多用%运算呗
很有意思的,chmod -r junk之后ls -ld junk可以运行,ls -l junk运行时发生权限问题;
挺有意义的,原来chmod 可以用±符号来操作鸭
最大值,应该非常大吧,首先,一个索引节点能指示10个直接索引,1个1级···,然后将这些直接索引和间接索引所指示的磁盘中的内容全部填上16字节的表项,能填太多太多太多太多了叭!!!!!
首先绝对不可能任意长度,因为任何一块磁盘的容量都是有上限的;
其次,我知道怎么去扩展文件名,还是采用内核中一贯采用的思想,就是说目录项里不保存文件名,而是保存指针,该指针指向一个磁盘块,该磁盘块保存文件名,如果一个磁盘块1K的话,那么再假设一个字符占1B,那么文件长度就扩展为1024个字符了;
同理,如果你觉得一个磁盘块所能容纳的文件名还是太短的话,目录表项不保存我们之前说的指针了,而是保存指向一个表项,该表项包含直接索引和间接索引,当然间接索引越多,获取对应的长文件名的效率就越低;
当然有可能删除了,因为namei没有对当前工作索引节点加锁,自然可以删除;
如何阻止?那就是对工作节点加锁咯;
散列:
所以n元树可能比较靠谱:
这是为什么呢?
充其量和buffer.c一样呗,不设计了叭!
先来回想下使用块的链接表来记录空闲磁盘块的方案:
那么再来考虑位示图方案:
然后再来分析下它的使用过程: