莫度编程网

技术文章干货、编程学习教程与开发工具分享

Linux内核源码分析之文件系统

最近开始研究Linux文件系统,希望通过阅读源码,了解文件系统的设计要素、控制逻辑,最终自己能够实现出一个简易的文件系统。

经历过内核源码阅读的小伙伴,相信你在最初面对如此庞大的内核源码时,一定会有种“面对茫茫大海,无从下手”的感觉,即便幸运的找到了逻辑入口,却在繁琐的函数跳转中晕头转向,无法把握住整个控制路径上的重难点。代码阅读之后,无法上升到整体设计,提炼出实现者的出发点,对代码逻辑,常常感觉迷惑,无法洞悉代码背后的原理。

鉴于此,在研究该部分代码前,我从实现的角度出发,根据文件系统的功能往下进行推敲,提炼出文件系统的重难点,有方向性地阅读代码、印证想法、解决疑问。

挖掘出技术难点,带着问题有方向性的阅读代码,这样能够做到事半功倍,同时对整体结构的理解,更加透彻

1. 文件系统是什么

首先,我们先来考虑下“文件系统是什么?”,在早期操作系统还未出现文件系统这个概念的时候,如果程序需要存储数据,那么它必须自己实现文件存储、文件管理的代码,程序本身需要与硬盘驱动器打交道。最令人头痛的是,你存储文件的地方可能被其他程序覆盖,导致数据的丢失。

为了解决这个“各自为营,重复创造”的现象,大家提出采用统一的中间层,管理应用程序与硬盘之间的文件交互。这样做的好处就是:

  • 应用程序不再关注文件的存储与管理,能够专注于自身的应用逻辑控制

  • 统一的中间层知道所有文件存储的信息,能够避免文件覆盖现象的出现,进行更有效的数据管理

总结来说:

文件系统是应用程序与块设备(磁盘等)之间的桥梁,是对文件进行统一管理的中间层。

对上: 向上层用户提供读写文件的操作接口

对下: 将文件在磁盘上进行存储及有效的管理

2. 文件系统涉及什么

2.1 文件系统挂载

在Linux环境下,磁盘作为一种特殊的文件(Linux系统中一切皆文件),允许用户进行读写操作,但是如果我们想要正常使用,在其上创建文件夹,文件等,则必须经过以下几道工序:

  • 采用某种文件系统格式化磁盘

mkfs -t ext3 /dev/sdb

  • 为磁盘创建挂载点

mkdir /sdb_dir

  • 挂载磁盘到指定目录下

mount /dev/sdb /sdb_dir

  • 修改/ext/fstab配置文件,设置开机自启

/dev/sdb /sdb_dir ext3 defaults 0 0

我们可以将上面的步骤大体分为两步:

(1)磁盘格式化 : 写入超块信息,按照特有的数据布局,对磁盘进行格式化

(2)文件目录入口: 在整个系统文件目录上,为其寻一入口,以后由此进入

2.2 技术点初总结

从上面的步骤中,我们可以发现磁盘无法直接使用,首先需要经过某种文件系统的格式化,而这个格式化过程,包括注册超块信息,将磁盘划分成数据块进行管理,也就是说:文件是由多个数据块联接在一起进行表示。从这里出发,我们来思考一下,文件系统可能涉及哪些技术点:

1. 文件存储方式

  • 应用程序直接打交道的是文件,文件是采用何种方式持久化在磁盘上呢?

  • 如果采用数据分块的方式,数据块如何有效组织在一起,表示成一个文件?

2. 读、写、定位的实现

  • 如何快速定位文件中一个数据块的位置?比如要查看文件某个位置的数据。

3. 元数据的管理

  • 元数据有哪些?目录树,空闲块的维护?

4. 数据一致性保证

  • 内存的数据与硬盘上的数据如何来往? 何时将内存中的数据固化到磁盘,以防断电等造成的数据丢失。

上面我是想到啥,说到啥,整体感觉,有些凌乱,可能会遗漏某些技术点。接下来,从文件系统的基本功能“读写文件”出发,我们来尝试挖掘出一些有用的技术点。

2.3 读写流程分析

在这里,我们尝试分析下读写逻辑,整理其控制路径,当然这里只是一个粗糙的版本,毕竟此时我们还未读代码,只是猜想其过程,同时暂且不讨论Page Cache,均采用Direct IO的方式:

(1)根据文件路径,查找目录树,获得当前文件目录项

(2)通过文件目录项,可以获得该文件起始块,文件大小,权限等信息

(3)通过文件起始块,文件偏移,以某种方式,得到目标数据块位置 (这里需要研读下如何快速定位某数据块)

(4)读整个目标数据块,数据缓存于内存当中 (是否将整个目标数据块读出来,这里不确定,有待源码印证)

(5)拷贝数据到读请求的buffer当中

(1)根据文件路径,查找目录树,获得当前文件目录项

(2)通过文件目录项,可以获得该文件起始块,文件大小,权限等信息

(3)通过文件大小,如果发现文件偏移超过文件大小,则通过“空闲块管理”获取新的数据块位置。否则,通过文件起始块,文件偏移,以某种方式,得到目标数据块位置;

(4)将数据buffer中的数据写入对应磁盘位置(这里可能使用写buffer,不知如何实现的)

2.3 技术点总结

通过上面的控制逻辑推敲,我们对文件系统想必有更加清晰的认识了,这次,我们再来总结总结,可能涉及的知识点:

正常读写流程

应用读写请求(文件描述符,位置,长度,数据buffer)

读:文件系统如何填充到buffer当中,在内核态到用户态buffer,是否会有一次拷贝

写:将buffer当中的数据以IO的方式,发送到下层

目标:

1. 读写经过的路径:系统调用 --> 文件系统 --> 下层调用接口

2. 元数据之间的交互:目录树,空闲块的使用

3. buffer中数据的来龙去脉,能观察到经过了多少次拷贝

磁盘数据固化

  • 数据分布:超级块?目录树?空闲块?数据块? 这些如何记录在案

  • 数据固化:数据一致性保证,目录树这些在内存中的元数据,何时固化到磁盘上?采用什么方式,保证数据在突然断电的情况下,不会丢失

目录树

查找文件的入口地址,希望维护快,查找快

  • 数据结构:目录树采用何种数据结构进行维护:BTree?红黑树?

  • 磁盘固化:关机之后, 如何在硬盘上进行存储?

  • 加载方式:运行时,是将所有的目录信息均加载到内存当中,还是按需加载,缓存频繁的目录数据块?

空闲块表

记录哪些数据块还未被使用

  • 数据结构:采用何种数据结构进行维护:Bitmap?BTree?红黑树?

猜测:构造排序二叉树(红黑树啥的),维护关键字<空闲长度,位置>,这样就能快速找到满足长度的一段联系空闲块??

  • 磁盘固化:关机之后, 如何在硬盘上进行存储?

通过上面这些,让我们清楚认识哪些问题需要去注意,能够有方向性的去阅读代码。小伙伴们,让我们带着这些问题,去探索,阅读代码吧!!

当然每个人的关注点可能不一样,在此分享自己源码阅读的方式,希望抛砖引玉,大家相互交流。

如果你在工作学习中存在什么技术难题,请关注大数据架构师公众号:xinsz08,我们会努力帮你解答,每天1篇必读运维大牛亲笔绝密干货贴,1000万IT人的干货资源库。

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言

    Powered By Z-BlogPHP 1.7.4

    蜀ICP备2024111239号-43