哈喽,我是子牙老师,一个手写过操作系统、编程语言、Java虚拟机、docker、Ubuntu系统,玩透Windows内核、Linux内核的...硬核男人
这周想把课程《实战Linux内核》四期关于ext文件系统的部分结掉,本来想着把磁盘分区结构MBR与GPT搞透、再将ext、ext2、ext3、ext4涉及到的分区结构、块组、日志、直接块间接块寻址、extent搞明白,就可以轻松看懂ext4文件系统源码了,看的时候才发现,里面大量journal相关代码

journal是什么呢?是ext4的日志模块jbd2相关代码。ext4文件系统对目录文件的所有操作,为了防止突发情况导致的元数据不一致,都会封装成jbd2的事务,所以如果你想彻底看懂ext4的源码,必须先将jbd2玩明白

按照ChatGPT说的学习路径,课程《实战Linux内核》四期已经完成了1、2,现在把3搞明白,再去看ext4源码把4搞明白,ext4文件系统,你就能轻松看懂了


这里打个小广告,如果你想真正学会Linux内核,学习我的课程,一定没错!教Linux内核的课程本就不多,以实战的方式讲Linux内核的,仅我一人!彻底学会Linux内核的三个标准:1、能够搭建出单步调试Linux内核环境;2、能够编写驱动在Linux内核中实战;3、能够编写Linux内核模块扩展Linux内核功能,我的课程中全部教!
接下来开始整jbd2,enjoy
为什么要封装成事务
可能很多人不理解,ext4文件系统为什么要将所有操作封装成事务?为什么封装成事务就能保证元数据一致?元数据是什么?
先解释什么是元数据,你通过stat命令查看一个目录看到的信息,就是目录的元信息

在Linux内核层面,对应的就是ext4_inode、ext4_dir_entry_2


看看inode=2的信息,数据块是block 730。目录的数据块中存储的是什么?子目录的ext4_dir_entry_2

接下来说说ext4为什么要把所有操作封装成事务,你知道ext4文件系统创建一个目录要做哪些事情,你自然而言就懂了。就以mkdir为例,让你知道ext4创建一个目录的完整过程
这是ext4文件系统结构,比如在fs目录中创建目录ziya

创建目录ziya时,第一步就是去申请inode number,去哪申请?inode位图。一个block是1024B,每个位映射一个inode number,1024*8=8172,即一个块组最多可以申请8172个inode number。

如果申请到了inode,就要将对应的位改为1,比如块组0的inode位图位于block 85,需要将该block读出来,把对应的位改为1,然后写入,这是第一次写盘

申请到了inode number,就需要创建inode节点,完成初始化,然后落盘,这是第二次写盘

创建目录ziya时,会自动写入两个dentry:【.】与【..】,需要一个block作为数据盘。就得去读空闲块位图,block bitmap位于block 82,读出来,找到空闲位,改为1,写入,这是第三次写盘
还有将创建的目录ziya的信息写入父目录fs的数据块中,更新父目录fs的inode信息,父目录fs的数据块是block 730,读出来,写入,这是第四次写盘
父目录fs的inode=2,每个struct inode占128B,可以算出,父目录的inode信息在block 88上,读出来,更新数据,写盘,这是第五次写盘。更新哪些信息呢?比如

ext4文件系统的超级块所在block,也有可能发生数据变动,就涉及到第六次写盘

创建一个目录,要经历这么多次写盘,如果在某一步,停电了,比如在第二次写篇后停电了,后面的四次写篇都还没做,会发送怎样的结果?是不是就浪费了一个inode?
为了避免发生这样的事,ext4的解决方案是引入日志,即jbd2,将一次操作的所有写盘行为封装成一次事务,写入日志区域。Linux内核中的jbd2进程,会在合适的时机执行日志中的事务

合适的时机有哪些呢?

接下来就拿mkdir ziya生成的日志进行分析,看jbd2是怎么玩的
拿jbd2日志数据
开始实战!创建一个盘fs.img,打上ext4文件系统,挂载到fs目录中,进入fs目录,创建目录ziya

执行了上述操作,来看下事务情况,两条事务数据

mount会产生事务日志,匪夷所思吧。想不想知道mount产生的日志长啥样?等下带你看
接下来执行mkdir ziya,再看日志情况,多了两条日志

接下来咱们来分析jbd2产生的日志数据
分析jbd2日志数据
默认情况,jbd2只能使用1M空间,一个block是1024B,jbd2用了1024个block,1M空间是这样用的,结构长这样

日志超级块,固定使用inode=8,来看看它的信息

日志超级块的数据块是从8274到9297。我们把它所有日志dump出来进行分析

日志分层两段,第一段是mount时产生的,第二段是mkdir时产生的
block 1是执行mount时生成的Descriptor日志信息,block 3是执行mount时生成的Commit信息,那block 2是什么呢?从图中可以猜出,是要写盘的数据块,这个数据块是哪个block的呢?block 88的,如果你不信,可以来论证一下,先看看block 2的内容

再将block 88 dump出来,看下内容


是不是一毛一样。block 88是什么内容呢?inode数据区

mount时会读ext4文件系统的super block信息,这些信息就存在inode数据区中的第一个block中
再来看第二段,descriptor信息在block 4中,commit信息在block 12中,block 5、6、7、8、9、10、11,,7个block,是哪些block呢?这个信息ChatGPT没解析出来,自己来猜测解析吧

里面有六个数字:0x55、0x59、0x52、0xe8、0xda,转成十进制

85是inode位图,89是inode表,82是block位图,都是我们前面讲的创建目录要操作的盘,232、218是什么,就不知道了,找不到任何数据跟这两个数字关联
来看看block 5对应的是不是block 85,还真是,inode位图

我最开始猜测block 6是block 86,发现不是,然后猜测是block 89,发现也不是

最后我通过全盘搜索,发现它是block 3的内容。block 3的内容是什么呢?块组信息!想想也有道理,块组如果坏了,有再多信息也没用啊

block 7的信息是不是block 89呢?看下,发现是的!inode表

block 8的信息一看就是位图,就是block 82的内容,block位图

block 9的信息呢?有【.】与【..】,很明显目录ziya的数据区

ziya的数据区对应的是哪个block呢?


block 10呢?对应的inode 88,inode数据区,前面提过,创建目录ziya,需要修改父目录fs的inode信息

那block 11呢?父目录fs的数据区呗。数据区在哪个block上?


还真是!

至此,jbd2你已全然知晓!把jbd2讲到这个程度的,全网只有我,不信你可以去百度、Google,或者问任何AI
jbd2的1M空间结构可以优化一下了

这就是我对技术钻研的态度!也是我做课程的态度!我做的所有课程,都是奔着让你能真正学会去的,技术实力摆在这,我能通过实战让你真正看到!让你真正理解透!
如果你想更多了解我,欢迎去我公众号【硬核子牙】看我之前的文章及我的奋斗历程。白手起家程序员的职场心得,应该会对你有很大启发