Linux铁三角 - IO与文件系统(二)文件系统的架构

该文章参考宋宝华老师的视频课程,详细可以去听阅码场宋老师的课程。

文件系统的架构

  • 一切都是文件:VFS
  • 字符设备文件、块设备文件
  • 超级块、目录、inode
  • 符号链接与硬链接
  • 目录的组织
  • icache和dcache,slab shrink
  • 块映射
  • 发现并读取/usr/bin/xxx的全流程
  • 用户空间的文件系统:FUSE

最近大家都在谈的虚拟化技术,就是要充分利用系统的资源,这里充分利用系统资源的意思就是:

CPU不闲着,IO也不闲着,CPU不等IO, IO不等CPU,它们之间没有相互等的情况。

在linux系统中,我们访问一个硬盘设备,我们有两个方法:

1、裸设备访问 。file_operations 在 fs/block_dev.c 中实现。

2、挂载后以文件系统访问。file_operations 在 fs/ext4/file.c 中实现, 文件系统会把你的读写转化为硬盘的哪个扇区、磁道等,所以我们在写硬盘驱动的时候就不用写

     file_operations了,因为这一层已经被文件系统实现了。

我们知道一个文件 1.txt 它不仅仅是文件的具体内容,它还有一些元数据,比如文件的创建者,创建时间,文件大小,权限等等信息。那这些

信息都要存储到硬盘里来的,我们知道硬盘里最小的存储单位是扇区,一般来说一个扇区是 512个字节,那一个 10M大小的文件,要存到硬盘里,

可能文件的内容部分就需要20480个扇区来存储,那每个扇区的位置也要存储起来,文件的元数据也要存储起来,那这些东西都是文件系统来做的。

经典的文件系统结构如下:

superblock 主要存储

  •    inode bitmap 和 data blocks bitmap 的大小 和 位置。
  •    inode 的位置和数量
  •    data blocks 的位置和数量
  •    root inode 的索引

linux系统中,everything is a file , 每个文件都有一个身份证号,这个身份证号就是 inode ,  这个 身份证号是唯一的,如果有两个文件身份证号是一样的,那就是它们两个

是完全一样的,比如我们建立一个硬链接,ln 1.txt  2.txt 。

inode bitmap 就是用每一个bit 来表示 inode 是否是空闲的,有没有被占用。在创建文件的时候,在 inode bitmap 中就会有一个bit 被占用,当你删除的时候,这个bit 就会被标记为空闲。 

接下来是 datablock bitmap , 假如我们要存储的文件大小是 10M , 假设每一个data block 是 4k , 那每一个bit 就代表哪些4k的block是空闲的。我们需要记录 data block 中哪些 block 是否被用掉,datablock bitmap 就是来记录这个的。比如我们要给一个文件的末尾增加16k的内容,文件系统就会在 datablock bitmap 中寻找4个空闲的bit, 然后标记成1。

 inode table 不再是一个bitmap, 而是相同大小的一个等分块,假设每个 inode table 是 128byte ,当然不同的文件系统的 inode table的大小不一样,有的64byte ,有的256byte,这里以 128byte为例,这128byte里面存储了 文件的元数据,大小、时间、作者等,还有最最重要的就是 这个文件的每个数据块的位置。这个用指针来描述的。

下面来描述一下,当我们创建一个文件 xxx.c 的全过程。

touch xxx.c ,首先会在 inode bitmap 中为这个文件,寻找一个身份证号,比如 inode=11 , bitmap的第11位就会写成1。

比如这个文件的大小是 16k , 接下来需要在 datablock bitmap 中找到 4个 空闲的比特位,然后写成1,这4个比特位代表了 datablock中哪4个4k的block是空闲的,可以用的。

接下来就会在 inode table 中找到 inode=11  的 那个 inode table , 然后把文件的元数据写进去,最终要的是要把 用到的那4个 datablock的位置,用指针写进去。那这里就需要4个指针,32个byte 就可以解决。

到这里整个文件就创建完成了。

但是我们知道,每一个 inode table 就只有 128 byte ,就算都拿来存储指针,也能存储的文件大小也是有限的,那到底该如何解决呢?

直接映射的方法,存储的文件是有限的。可以采用间接映射的方法,一个指针指向一个数据块,这个4k的数据块里存储的1024个指针,这就是2级的映射,可以最大存储 4M的文件,当然也有3级的映射,最大可以存储 4GB 的大小。

 

Ext4 中有个 extents 的概念,对于 地址连续存储的数据库,有更节省空间的做法。

硬盘里不可能上来就直接管理 n 个 block ,就像一个公司就一个部门,这样管理起来就特别麻烦了,文件系统会分为几个group ,每个group 

还是咱们上面说的文件系统的组织结构。

Linux 中 目录是一个特殊的文件,everything is a file , 它也有 inode 编号,跟普通文件一样,特殊的地方就是它的内容,它的内容是 文件的名字 和 inode的映射关系。

 比如我们要检索 /usr/bin/emacs 这个文件,首先会 查 / 目录的 inode = 1 , 然后根据 第1个 inode table 中的 指针信息 找到 / 的数据内容,然后查到 /usr 目录的inode 编号2,根据这个编号,可以查到 /usr 的inode table 中指针信息,查到它的数据内容,进而查到 /usr/bin的inode 编号为11 , 再查inode = 11的 inode table  的指针信息得到 /usr/bin/的数据内容,也就得到了 /usr/bin/emacs的inode 编号为119 ,于是我们查到了 emac这个文件在硬盘中存放的位置。

硬链接的概念就更好理解了

作为一个linux 工程师,理解的时候就不能像  m.txt 是 a.txt 的硬链接这么简单,要理解成 m.txt  a.txt 在硬盘里的存储和实现,是完全对等的,唯一不同的地方就是

在一个数据表项里面它们的名称不一样而已,要知道 inode 编号才是文件唯一的身份证号!把硬链接就理解成是一个别名,地位上是完全对等的。

但是删除m.txt ,a.txt 还是存在的。硬链接的本质就是 在一个目录表项里多了一条和 inode的映射关系。把 m.txt 删除了,只是删除了这一条映射关系。

ln -a  a b 软连接是不一样的,b 也是有自己的inode编号的,也有自己的inode table , 不过它的内容是指向原原件的。

----------

之前我们举的例子,检索 /usr/bin/emacs 这个文件出来,这个过程是比较漫长的,在linux 中,每一个路径都对应有一个 dentry , 如果建立一个硬链接,ln /usr/bin/emacs  /usr/bin/emacs-ln , 这两个就是不同的dentry , 每一个dentry被读过之后,linux 就会给它简建立一个dentry cache中,每一个inode 的inode table 被读过一遍之后,linux 就会给它建立一个 inode cache , 这就是我们在linux 中常说的 icache 和 dcache , 这两个cache 是文件系统级别的cache  和 CPU中的cache不一样。

上图中的标注:SLAB_RECLAIM_ACCOUNT 说明是可以会回收的。 

这两个cache都是slab级别申请的,是可以自动回收的。这里和 kmalloc 和 kfree不一样,kmalloc 虽然也是slab级别的申请,但是需要自己去回收释放。

当这个值我们把它加大的时候,系统在做内存回收的时候,就会尽量帮我们回收 icache dcache 。

回收的时候用的是LRU的算法。大部分是LRU的算法。

如果想 清掉 系统的 pagecache :  

如果想 清掉 系统的 icache 和 dcache : 

 

如果想 清掉 系统的 pagecache , icache 和 dcache :  

一个inode  对应一个 pagecache 

 

比如1.txt 2.txt 是硬链接的关系,有三个进程,分别打开 这两个文件,同一个路径对应一个dentry , 但实际上linux 是以 inode 来区分文件的,所以这三个进程打开的文件,都会对应一个inode,

注意:

1、每个进程运行,都对应一个 task_struct , 每打开一个文件,都对应一个 file 的结构体,file里的 file_operations其实是 inode->i_fops 来填充的。

2、一个inode  对应一个 pagecache ,比如打开的是一个 1Mbyte 的文件,每次读写这个文件,都会在缓存在内存里,叫 pagecache ,它在内存里是如何的结构呢?其实是以一个 radix tree 基数树来组织的。

    每一个小块就是一个4k的页,比如要读这个文件的 第16k 的位置,如果 radix tree 里没有,会在radix tree 中申请要读写大小的page , 然后从 磁盘里 通过 address_space_operations() 来读写到radis tree中 申请的pages中。

 i_mmap 是管理地址空间的,一般包括两部分,它的关联的数据结构就是 address_space , 地址空间包含两部分,第一部分就是 radix tree 管理的Pages  在不在,基数的意思大概是文件的偏移地址,根据这个基数去在tree上查,然后查到 要读的偏移地址的页的位置,如果pagecache 不命中,就会通过a_ops-> address_space_operations 从硬盘里去读。

 如果我们自己要实现一个文件系统,要做社么事情呢?

首先 申请一个 file_system_type , 实现 mount  umount 

填充 super block , 如何申请 和 销毁 inode , 置空 可以分配为系统通用的接口。

填充 inode_operations , 主要是 如何创建、查找 inode , 如何 mkdir 

file 结构体中的 file_operations 

可以自己写一个文件系统

GitHub - 21cnbao/simplefs: A simple, kernel-space, on-disk filesystem from the scratch

  主要包含两部分,一个是内核模块 mkfs-simplefs.ko ,一个格式化工具mkfs-simplefs

一种用户态 文件系统 FUSE 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值