前言
在 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 什么的
接下来是 查找 group, 分配文件节点, 这里可以看得出来 ext4 文件系统对于 文件数量的限定是一个 逻辑上的限定
接下来是初始化 inode/ext4_inode 的相关其他信息
普通文件的 i_fop 为 ext4_file_operations
另外就是创建了文件之后, 还需要将文件的相关信息 添加到文件夹中记录
这里 ext4_find_dest_de 会找到 父文件夹的数据内容中存放当前文件的地址, 如果有则找到 已有的文件的地址, 如果没有在父文件夹的数据内容末尾 记录当前新增文件的信息
这里 就是设置文件夹中 新增/更新文件元数据的地方
这里记录几个信息项 inode, rec_len, name_len, file_type, name
查看 /jerry 的文件夹的内容, buffered_head
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 的实现是将 用户空间待写出的数据 写出到给定的 物理页, 然后 系统会定时将脏页刷出到磁盘
读取文件的时候 根据 mapping, index 获取磁盘文件块, 加载到 page
这一部分的内容参见 从磁盘读取数据到内存的调试
copy_page_to_iter 即为读取的操作, 将物理页的数据拷贝到 buf 中
如何根据 path 获取到上下文的数据?
上面在 i_op->create 中创建了 inode, 但是并没有提到将 inode 注册到 dir 中或者怎么怎么样
那么 读取文件的时候怎么关联找到这个 inode 呢?
然后 之后才是走的上面的 i_op->lookup 在父目录的内容中查找 目标文件的条目[ext4_dir_entry_2], 这里可以拿到 i_no, 然后使用 ext4_iget_normal 来获取对应的 inode 的信息, 这里也会缓存一份 dentry 的数据到 dcache
然后 在之后 读取该文件的时候, lookup_fast 的时候去 dcache_hashtable 中查询对应的 dentry 的信息
进而拿到 inode, 以及 inode 相关附加信息
进而是根据 inode, 以及上下文, 封装 file 对象
完