前言

在 linux 中常见的文件系统 有很多, 如下 

基于磁盘的文件系统, ext2, ext3, ext4, xfs, btrfs, jfs, ntfs 
内存文件系统, procfs, sysfs, tmpfs, squashfs, debugfs 
闪存文件系统, ubifs, jffs2, yaffs  

文件系统这一套体系在 linux 有一层 vfs 抽象, 用户程序不用关心 底层文件系统的具体实现, 用户只用操作 open/read/write/ioctl/close 的相关 系统调用, 这一层系统调用 会操作 vfs 来处理响应的业务 

vfs 会有上面各种文件系统对应的 读写 相关服务, 进而 将操作下沉到 具体的文件系统 

我们这里看一下 ext4 文件系统 

将业务数据存储在磁盘的一个文件系统, 读写数据 最终会提交到磁盘设备 

将数据暂存在内存中物理页, 动态刷出到磁盘 

如何分配inode ?

在创建该文件的时候, ext4fs 会创建对应的 inode, 并将改 inode 添加到 父级节点

创建 inode 操作来自于, 父级目录的 i_op->create, ext4fs中对应于 ext4_create

这里主要是基于 ext4fs 的 super_block 新建 inode, 初始化 i_no, i_mapping, i_op, i_fop 什么的 

44 ext4 文件系统_linux

接下来是 查找 group, 分配文件节点, 这里可以看得出来 ext4 文件系统对于 文件数量的限定是一个 逻辑上的限定

44 ext4 文件系统_文件系统_02

接下来是初始化 inode/ext4_inode 的相关其他信息

44 ext4 文件系统_vfs_03

普通文件的 i_fop 为 ext4_file_operations

44 ext4 文件系统_ext4_04

另外就是创建了文件之后, 还需要将文件的相关信息 添加到文件夹中记录

这里 ext4_find_dest_de 会找到 父文件夹的数据内容中存放当前文件的地址, 如果有则找到 已有的文件的地址, 如果没有在父文件夹的数据内容末尾 记录当前新增文件的信息

44 ext4 文件系统_数据_05

这里 就是设置文件夹中 新增/更新文件元数据的地方

这里记录几个信息项 inode, rec_len, name_len, file_type, name  

44 ext4 文件系统_linux_06

查看 /jerry 的文件夹的内容, buffered_head

44 ext4 文件系统_ext4_07

0xffff88007dc08630:     29 '\035'       0 '\000'        0 '\000'        0 '\000'        36 '$'  0 '\000'        26 '\032'       1 '\001'
0xffff88007dc08638:     84 'T'  101 'e' 115 's' 116 't' 49 '1'  51 '3'  83 'S'  105 'i'
0xffff88007dc08640:     103 'g' 110 'n' 97 'a'  108 'l' 80 'P'  97 'a'  114 'r' 101 'e'
0xffff88007dc08648:     110 'n' 116 't' 65 'A'  110 'n' 100 'd' 67 'C'  104 'h' 105 'i'
0xffff88007dc08650:     108 'l' 100 'd' 0 '\000'        0 '\000'        30 '\036'       0 '\000'        0 '\000'        0 '\000'
0xffff88007dc08658:     36 '$'  0 '\000'        28 '\034'       1 '\001'        84 'T'  101 'e' 115 's' 116 't'
0xffff88007dc08660:     49 '1'  51 '3'  83 'S'  105 'i' 103 'g' 110 'n' 97 'a'  108 'l'
0xffff88007dc08668:     80 'P'  97 'a'  114 'r' 101 'e' 110 'n' 116 't' 65 'A'  110 'n'
0xffff88007dc08670:     100 'd' 67 'C'  104 'h' 105 'i' 108 'l' 100 'd' 46 '.'  99 'c'
0xffff88007dc08678:     31 '\037'       0 '\000'        0 '\000'        0 '\000'        -120 '\210'     1 '\001'        5 '\005'        1 '\001'
0xffff88007dc08680:     50 '2'  46 '.'  116 't' 120 'x' 116 't' 0 '\000'        0 '\000'        0 '\000'
0xffff88007dc08688:     0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'

# 这里内存中的 0xffff88007dc08678 开始 为 /jerry 目录下面 我们新增的 2.txt 的数据条目 
inode_no 为 0x00 00 00 1f 
rec_len 为 0x01 88
name_len 为 0x05 
file_type 为 0x01 
name 为 2.txt

/jerry/xx 是在哪里创建的 ?  

这个创建取决于实际的业务, 业务/内核 代码向 ext4fs 中写出具体的数据到具体的文件 

那么执行 该代码的时候 就会创建对应的文件

如何分配存储空间 ? 

存储空间是来自于 pagecache, 然后 具体的数据是来自于 磁盘设备 

  从磁盘读取数据到内存的调试 

如何读写数据 ?

a_ops->write_begin 会获取 maping, pos 对应的物理页, 如果不存在 则向 pagecache 进行申请 

然后 iov_iter_copy_from_user_atomic 的实现是将 用户空间待写出的数据 写出到给定的 物理页, 然后 系统会定时将脏页刷出到磁盘

44 ext4 文件系统_文件系统_08

读取文件的时候 根据 mapping, index 获取磁盘文件块, 加载到 page 

这一部分的内容参见 从磁盘读取数据到内存的调试 

44 ext4 文件系统_ext4_09

 copy_page_to_iter 即为读取的操作, 将物理页的数据拷贝到 buf 中

44 ext4 文件系统_ext4_10

如何根据 path 获取到上下文的数据? 

上面在 i_op->create 中创建了 inode, 但是并没有提到将 inode 注册到 dir 中或者怎么怎么样 

那么 读取文件的时候怎么关联找到这个 inode 呢? 

然后 之后才是走的上面的 i_op->lookup 在父目录的内容中查找 目标文件的条目[ext4_dir_entry_2], 这里可以拿到 i_no, 然后使用 ext4_iget_normal 来获取对应的 inode 的信息, 这里也会缓存一份 dentry 的数据到 dcache

44 ext4 文件系统_ext4_11

然后 在之后 读取该文件的时候, lookup_fast 的时候去 dcache_hashtable 中查询对应的 dentry 的信息 

进而拿到 inode, 以及 inode 相关附加信息

44 ext4 文件系统_linux_12

进而是根据 inode, 以及上下文, 封装 file 对象

44 ext4 文件系统_数据_13

完