作者简介
赵晨雨:西安邮电大学2018级陈莉君教授研究生,天真无邪小白一枚,已经爱上 linux 内核而不能自拔,正在成长为内核狂热爱好者 :japanese_ogre:
跟随陈老师学习linux内核两个月了,对linux内核产生了极大的兴趣,最近学习文件系统,有一些自己的看法,很荣幸能在linux内核之旅进行分享^_^
本篇文章使用尽量 通俗 的语言来说明linux内核文件系统中各个数据结构之间的关系,这是一个很复杂的结构关系,在学习这里的时候一定要和源代码一一对应起来,否则的话就会陷入迷茫之中。由于linux内核足够复杂,就会有多种解释方式,我认为所有关于linux内核的书籍,都是不同作者对内核的不同的看法,说不定这些看法对于linus本人来说都是很巧妙的,所以我在这里也大胆地提出自己对linux内核设计方式的一些看法。
话不多说,我们上图:
我使用这张结构图来进行说明,一共大概有10个结构体,我把它分成三条线来看,在图中也标记好了,在看每一条线时,我们把它从整体结构中隔离出来看。
第一条线( 绿色 )
这一条线是进程部分,也就是以进程的眼光来看文件系统。task_struct是一个非常复杂的结构体,我们在这里只看与文件系统相关的字段。从现在开始,把自己当成一个内核设计者,接下来将要介绍的数据结构都是为了给进程提供完好的服务,使得进程可以正常运行。(这里也可以体会到进程真的是OS的核心)
首先,进程在运行的时候,总归会使用到文件,那么就会用open()和close()两个函数,此时,就会产生图中的第1个结构体file,另外进程使用到的文件是很多的,所以会有很多个file(这里说明一下,file还有一个很好的作用是并发访问,不同的用户在打开相同文件的时候都会产生file,这样就可以实现互不干扰)产生,那么这个时候就需要对这些file结构体进行管理,按照内核的标准套路,就是使用唯一标号,我们叫它文件描述符,那么自然而然地,我们可以想到,这么多文件描述符我们怎么管理呢?再按照内核的标准套路,我们把它又封装成一个结构体,这个结构体最主要的任务就是把这么多file结构体进行很有条理的管理,从而方便进程的使用。这个结构体就是图中第2个files_struct结构体了,看图中的红线,一个二级指针指向fd_arry数组,数组里存放着一个又一个的file结构体的地址,最重要的我们要让PCB能够使用到这个结构体,就放入一个字段files指向files_struct。
(file结构体之后的dentry结构体部分先保留。)
第二条线( 红紫色 )
这一条线是纯正的文件系统线,也就是我们现在是文件系统,我现在需要正确的进入内核。
首先,每一种文件系统都需要符合VFS的规定和内核的标准套路,我是一个文件系统,那么我的条条框框就很多,也就是字段特别多,为了融入内核,我需要通过图中第3个结构体file_system_type来进行注册,换句话说,内核中有多少个文件系统,就有多少个file_system_type结构体。之后,我是文件系统,我自然而然就需要使用磁盘来存放文件了,那么这个时候有会有很多很多的细节,也就是存放在块设备上的管理信息,这些信息又有很多,那么按照内核的设计套路,再次封装成一个结构体,也就是图中第4个结构体super_block结构体。我在这里有一个疑问,就是一个file_system_type和一个super_block,这两个有什么区别又有什么关系呢?因为我觉得只需要一个就够了。在阅读大量书籍后,我自己给出的答案是file_system_type是描述这个文件系统的,而super_block是用来实际管理文件系统的,二者是不同的作用,就好比注册完以后,那张注册表还有别的作用吗?
super_block也是一个极其庞大的结构体,它既然处于管理的地位,所以具体的一个个文件就需要和它进行关联,按照内核标准套路,一个个具体的文件在内核中还是抽象成一个结构体,就是图中的第5个结构体inode结构体,(多说一句,这两个结构体之间是互联的,各自有指针指向对方,而且文件系统这里的指针真的很精彩!),那么inode代表了一个个实际的文件,这时候,自然而然就需要目录了,内核把目录也当做一个文件来处理,同样抽象成一个结构体,也就是图中的第6个结构体dentry结构体。
一二条线的交叉部分
这里的交叉部分很巧妙,我学习文件系统的时候,是从super_block开始学习的,所以顺着下来是inode结构体,但是当时就在 想 , 为什么不先是dentry目录,然后目录下再存放inode呢? 我自己的 答案 是:
-
站在第二条线来想,所谓目录,是对文件的划分,所以得先有文件,然后才能有目录。
-
站在第一条线来想,我要打开一个文件,这个文件已经存在了,那么我就需要从目录中去找,然后再往下找到inode
综合两条线来看,正是因为内核的这种设计方式,我们才会有在打开一个文件时先找目录的习惯。
再次来体会图中的交叉部分,dentry是很巧妙的。
第三条线( 蓝紫色 )
我们直接来看图中的第7个结构体,vfsmount结构体,这里先说一下为什么会有这么个结构,我们在第二条线中说过了文件系统注册时的file_struct_type结构体了,但这个信息还不够,我们要正确使用一种文件系统,就必须mount到根文件系统的某个目录上去,记得要root权限哦,这个时候还是为了管理上的方便,又抽象出来一个结构体vfsmount,它存放的就是文件安装的相关信息,它和PCB之间还需要建立连接,这里又通过第8个结构体来建立连接,它在2.6源码中名字是namespace(2.4中是mnt_namespace)。
从文件系统来看内核
这里假设大家已经细读了内核源码,我们可以发现,内核设计的标准套路,就是 抽象、管理、操作 , 抽象 是分为两种情况,一种是外部文件的抽象,一种是内部信息复杂而进行的抽象。那么是怎么进行 管理 的呢?举个例子来说,只要内核中有许多独立的结构体存在时,我们就通过双向链表,单项链表,哈希表这些方式进行管理。这里可以体会一下数据结构的精华所在。 操作 的对象就是我们所抽象的一个个数据结构。我们在上面仅仅介绍了这几个结构的关系,它们还只是数据结构而已,我们还需要操作,就是调用内核的API了,这些API的工作就是传递数据,从这个结构体中拿出数据放到另外的一个结构体中,所以所谓内核的运行,就是我们抽象出来的数据结构中数据的流动,在我们的脑海里可以形成一个很壮观的动态的场面。并且我始终坚信,内核所有解决问题的策略,都可以在我们现实生活中找到影子,毕竟,内核是人写出来的嘛!
学习内核的方法
这里推荐一下将内核划分的学习方法(这种方法在高剑林老师的书中有详细的介绍)。也就是将内核代码分成基础部分和应用部分(注意这里是将内核再划分哦)。我们在学习内核代码时,应用部分占用了大多数,基础部分的规模并不大,而且各个版本之间改动幅度很小,并且相当的短小精悍。我们可以这样来看,在用户台下,我们的程序要执行,需要调用内核的系统函数,那么我们可以类似的把内核再分成“用户态”和“内核态”,“用户态”就是应用部分,“内核态”就是基础部分,应用部分想要执行,就需要调用基础部分。
按照上面的介绍,基础部分也是一个小内核,那么它就需要给外界提供服务,换句话说,就是它的内部也具有自己的数据结构和函数:
-
数据结构:
-
双向链表
-
hash链表
-
单向链表
-
红黑树
-
radix树
-
服务:
-
内核中使用内存
-
内核中的任务调度
-
软中断和tasklet
-
工作队列
-
自旋锁
-
内核信号量
-
原子变量
这种方法主要对应前面介绍的内核设计套路中的管理环节,我们可以通过这种方法打破对内核的恐惧感,因为内核无非就是使用这些基础部分来管理数据,而数据的组织方式在内核中是最复杂的,例如说文件系统这里存在大量的全局链表,拿inode_in_use和inode_in_unused来说,我们就会很奇怪,为什么要有这两个链表?那么运用这种方法我们就可以这样来想,内核设计者在设计的时候,遇到了一个实际问题,这个问题一般可以从链表的名字看出来,这里就是遇到了区分inode有没有使用的问题,那么自然而然就可以想到,使用基础部分的各种链表来进行管理,因为这些链表在内核中就是负责处理这种问题的。所以,我们在学习内核的时候,心中有这些基本部分的概念,再来看内核就是另一种角度了。
由于自己接触linux内核时间不长,才疏学浅,班门弄斧了,如果有错误的地方欢迎大家指正,小赵万分感谢:-D
以上所述就是小编给大家介绍的《从文件系统的数据结构看 Linux 内核设计》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Linux 内核101:进程数据结构
- linux内核数据结构之kfifo
- 数据结构 – 用于构建文件系统的数据结构?
- 荐 用Python解决数据结构与算法问题(三):线性数据结构之栈
- 内核必须懂(六): 使用kgdb调试内核
- 数据库索引背后的数据结构
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
随机密码生成器
多种字符组合密码
HSV CMYK 转换工具
HSV CMYK互换工具