datasheet

Linux阅码场

文章数:367 被阅读:372613

账号入驻

赵晨雨:从文件系统的数据结构看Linux内核设计

2018-11-02
    阅读数:
作者简介

赵晨雨:西安邮电大学2018级陈莉君教授研究生,天真无邪小白一枚,已经爱上linux内核而不能自拔,正在成长为内核狂热爱好者👹

    跟随陈老师学习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阅码场精华文章汇总


扫描二维码关注"Linux阅码场" 

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: TI培训

北京市海淀区知春路23号集成电路设计园量子银座1305 电话:(010)82350740 邮编:100191

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2018 EEWORLD.com.cn, Inc. All rights reserved