一、前言
“文件系统是操作系统用于明确存储设备(常见的是磁盘,也有基于NAND Flash的固态硬盘)或分区上的文件的方法和数据结构;即在存储设备上组织文件的方法。操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统。文件系统由三部分组成:文件系统的接口,对对象操纵和管理的软件集合,对象及属性。从系统角度来看,文件系统是对文件存储设备的空间进行组织和分配,负责文件存储并对存入的文件进行保护和检索的系统。具体地说,它负责为用户建立文件,存入、读出、修改、转储文件,控制文件的存取,当用户不再使用时撤销文件等。”
“F2FS (Flash Friendly File System) 是专门为基于 NAND 的存储设备设计的新型开源 flash 文件系统。特别针对NAND 闪存存储介质做了友好设计。”
文件系统就是管理和组织磁盘上文件的一种方式,本文就抛开抛开代码细节,使用T32/dump.f2fs这两个工具,观察磁盘上,F2FS 在创建,新增文件,删除文件等操作后,相关内容的变化。帮忙大家直观了解F2FS文件系统在磁盘上的组织方式。
二、F2FS文件系统总览
F2FS文件系统的磁盘上的组织结构如下
2.1 分割单位定义
F2FS文件系统划分单位,从大到小排列如下
划分单位 | 大小 | 说明 |
Zone | 通常为2MB | 若干个Section组成一个Zone,通常Zone只包含1个Section |
Section | 通常为2MB | 若干个Segment组成一个Section,通常Section只包含一个Segment |
Segment | 512*4KB=2MB | 一个Segment包含512个block,所以通常大小为2MB |
Block | 4KB | 一个block大小为4KB |
2.2 SuperBlock
SuperBlock在F2FS文件系统起始位置,占据1个Segment,包含两个SuperBlock信息,每个SuperBlock信息为4K。实际使用的起始位置为1KB。示意图如下
2.3 CheckPoint
CheckPoint占据2个Segment(4MB),两个CP区域互为备份,这个区域的目的是为了保持数据一致性。
2.4 Segment Info Table
Segment Info Table 占据大小由Main Area中Segment数量决定。主要用于记录Main Area中Segment的信息,例如哪些Segment可用,Segment最后修改时间等信息。
2.5 Node Address Table
用于记录node id与真实地址之间的映射关系,后面实际分析数据时可以看到。
2.6 Segment Summary Area
反向索引的作用,前面SIT,NAT可以正向索引到Main Area中对应的block。而这区域的作用是提供快速反向索引,通过block可以快速找到这个block所属的Node
2.7 MainArea
保存实际的数据,有两种类型,Node和Data
Node保存管理数据
Data保存普通数据
无论是Node或data都占据一个Block大小,另外一个segment也只能由一种数据类型,也就是一个segment中不然全是Node数据,不然全是Data数据,不允许混合保存。
另外根据使用频率,还分为Hot/Warm/Cold类型
也就是(node,data)*(hot,warm,cold) 6种数据类型。
本章内容对F2FS文件系统的基本单位,以及关键数据组织方式进行了简单介绍。如果看到这里不理解不要紧,在后面我们实际查看F2FS文件系统中的组织方式时,再多回头看看这些概念,就慢慢理解了。
三、新建F2FS文件系统中的关键内容
本章会使用命令新建(格式化)一个F2FS文件系统,里面不做任何操作,我们实际分析一下新格式化后的F2FS文件系统中的信息。帮助我们理解上面的内容。
3.1 格式化文件系统
我们格式化两个不同大小的文件系统,对比分析其中关键数据的差异。空文件大小分别为100MB和1000MB
首先使用如下命令构造两个空文件
dd if=/dev/zero of=/data/local/tmp/f2fs_100MB.bin bs=1M count=100
dd if=/dev/zero of=/data/local/tmp/f2fs_1000MB.bin bs=1M count=1000
然后使用如下命令格式化两个文件系统
make_f2fs -f -d1 -g android -O compression -O extra_attr /data/local/tmp/f2fs_1000MB.bin
make_f2fs -f -d1 -g android -O compression -O extra_attr /data/local/tmp/f2fs_100MB.bin
之后,我们就分析这个刚格式化好的文件系统中的关键数据。
3.2 SuperBlock信息
3.2.1 使用dump.f2fs命令对比SuperBlock
命令如下
dump.f2fs -d 1 f2fs_100MB.bin
dump.f2fs -d 1 f2fs_1000MB.bin
对比差异如下
整理成表格对比数据及说明如下
条目 | 100MB | 1000MB | 说明 |
magic | [0xf2f52010 : 4076150800] | [0xf2f52010 : 4076150800] | magic number,这个是固定值0xf2f52010 |
major_ver | [0x 1 : 1] | [0x1 : 1] | |
volum_name | [] | [] | |
minor_ver | [0x10 : 16] | [0x10 : 16] | |
log_sectorsize | [0x9 : 9] | [0x9 : 9] | |
log_sectors_per_block | [0x3 : 3] | [0x 3 : 3] | |
log_blocksize | [0xc : 12] | [0xc : 12] | |
log_blocks_per_seg | [0x9 : 9] | [0x 9 : 9] | |
segs_per_sec | [0x1 : 1] | [0x1 : 1] | 每个section中保存1个segment |
secs_per_zone | [0x1 : 1] | [0x1 : 1] | 每个zone中保存1个section |
checksum_offset | [0x0 : 0] | [0x 0 : 0] | |
block_count | [0x6400 : 25600] | [0x3e800 : 256000] | block数量,每个block的size为4K,所以 |
section_count | [0x2a : 42] | [0x1ea : 490] | main area section数量 |
segment_count | [0x31 : 49] | [0xf3 : 499] | segment数量,应该不包含superblock(2MB) |
segment_count_ckpt | [0x2 : 2] | [0x2 : 2] | check point的segment数量,固定为2个segment |
segment_count_sit | [0x2 : 2] | [0x 2 : 2] | segment info table的segment数量,固定为2个segment |
segment_count_nat | [0x2 : 2] | [0x4 : 4] | node address table的segment数量。这个数量后面计算一下 |
segment_count_ssa | [0x1 : 1] | [0x1 : 1] | segment summary area的segment数量 |
segment_count_main | [0x2a : 42] | [0x1ea : 490] | 和上面section计算方式一样 |
segment0_blkaddr | [0x200 : 512] | [0x200 : 512] | segment0的blkaddr地址,上面计算可以看出来blkaddr从地址0开始的,segment数量是不包含superblock的,所以这里从512*4K=2MB开始计算 |
cp_blkaddr | [0x200 : 512] | [0x200 : 512] | cp的起始block位置,这个也好计算,就是2MB开始的地方 |
sit_blkaddr | [0x600 : 1536] | [0x600 : 1536] | sit起始位置,应该固定为6MB |
nat_blkaddr | [0xa00 : 2560] | [0xa00 : 2560] | node address table起始位置,目前应该是 |
ssa_blkaddr | [0xe00 : 3584] | [0x1200 : 4608] | segment summary area起始地址,由于前面nat数量有差异,所以这里计算的起始地址就有差异了 |
main_blkaddr | [0x1000 : 4096] | [0x1400 : 5120] | main area起始的blk地址 |
root_ino | [0x3 : 3] | [0x 3 : 3] | 对应的一些ino编号,后面再详细介绍 |
node_ino | [0x1 : 1] | [0x1 : 1] | node_ino=1 |
meta_ino | [0x2 : 2] | [0x2 : 2] | meta_ino=2 |
cp_payload | [0x0 : 0] | [0x0 : 0] | |
crc | [0x0 : 0] | [0x 0 : 0] | |
version | 5.15.119-qki-consolidate-android13-8-o-03186-g42a55cce9ac3 | 5.15.119-qki-consolidate-android13-8-o-03186-g42a55cce9ac3 | 版本信息 |
Th[0] Info: | Segments per section = 1 | Segments per section = 1 | section包含的segment数量,每个section包含1个segment |
Th[0] Info: | Sections per zone = 1 | Sections per zone = 1 | zone包含的section数量,每个zone包含1个section |
Th[0] Info: | total FS sectors = 204800 (100 MB) | total FS sectors = 2048000 (1000 MB) | 总大小数量 |
Th[0] Info: | CKPT version = 4009c261 | CKPT version = 5b4bb5b1 | cp版本信息 |
3.2.2 使用T32加载查看superblock
同样我们也可以通过T32+vmlinux,来查看文件系统中的内容。
具体方法如下:
打开T32,进行对应设置,并加载相近版本的vmlinux(不用完全匹配,主要f2fs结构体没变化就可以)。执行如下命令加载vmlinux及对应的文件系统内容
sys.cpu armv8-a
Sys.up
Data.LOAD.Binary f2fs_100MB.bin 0x0
Data.LOAD.elf vmlinux
这样就将vmlinux(用于获取kernel中F2FS相关结构体)和对应的文件系统加载起来了。
Superblock在磁盘上的结构体是struct f2fs_super_block
这样我们可以通过如下命令,分别读取到super_block#0,super_block#1信息
根据之前的介绍
Superblock#0的起始地址为1K
Superblock#1的起始地址为5K
这样就可以通过如下命令查看对应偏移量的superblock内容了。
Superblock#0 对应命令data.dump 0x400
Superblock#1 对应命令data.dump 0x1400
我们借助vmlinux,就可以很方便的以kernel中结构体中的内容查看对应superblock信息,具体命令如下
Superblock#0 对应命令V.v (struct f2fs_super_block*)0x400
Superblock#1 对应命令V.v (struct f2fs_super_block*)0x1400
我们对比100MB/1000MB superblock信息如下。对应保存的信息,及差异在上面一节中介绍过了,这里就不赘述
3.3 checkpoint信息
3.3.1 使用dump.f2fs查看checkpoint信息
与查看superblock命令一样,在输出superblock信息后,就会对应输出checkpoint信息。
因为checkpoint为了保持一致性的区域,100MB/1000MB对比是没有意义的,这里先不展开说明。后续在实际操作文件的时候,再展开说明。
3.3.2 使用T32查看checkpoint信息
Checkpoint在磁盘上对应的结构体是struct f2fs_checkpoint。根据之前介绍,有两个segment保存checkpoint信息。
对应的起始地址为2MB,和4MB
这样就可以通过如下命令查看checkpoint信息
v.v (struct f2fs_checkpoint*)0x200000
v.v (struct f2fs_checkpoint*)0x400000
3.4 Segment Info Table信息
3.4.1 使用dump.f2fs查看checkpoint信息
可以通过如下命令查看segment info table信息
dump.f2fs -s 0~-1 f2fs_1000MB.bin
dump.f2fs -s 0~-1 f2fs_100MB.bin
对应会在执行路径下生成dump_sit信息
我们看一下dump_sit中会显示什么信息。
首先是说明了segment类型,如之前介绍SIT时说的,共有(node,data)*(hot,warm,cold) 6种数据类型
对应的就是
segment_type(0:HD, 1:WD, 2:CD, 3:HN, 4:WN, 5:CN)
下面一个segno 就是segment对应的编号,这个编号是从main area区域开始编号的。每个segment对应512个block,这样在编号下方就是对应的512个block是否被使用的状态,vblocks表示由多少个block已经被使用,seg_type就是上面提到的当前segment类型。
对应的结尾会有如下打印
当前valid_blocks数量,也就是前面各个segmentvblocks之和,7+4=11=0xb
以及当前多少个segment中有有内容,valid_segs:2,空闲的segment数量:40
可以计算出来总的segment数量是42个。与superblock中segment_count_main对应
1000MB的信息也是类似,在结尾可以看到总segment数量是488+2=490个,也就是main area中共有490个segment
3.4.2 使用T32查看SIT信息
Segment Info Table在磁盘上对应的结构体是struct f2fs_sit_entry,为了理解这个数据结构,贴一小段定义代码。
一个segment 被一个f2fs_sit_entry进行管理,f2fs_sit_entry中有三部分组成
a.vblocks:这个其实是一个复合结构vblocks的vblocks[9:0]表示当前segment有多少个有效的block,vblocks[15:10]表示segment的类型
b.valid_map:大小为__u8*64=512位,使用位图管理当前segment中,哪些block被使用
c.mtime从表示当前segment最后一次gc的时间
Segment info table的起始位置从之前的supber block一张介绍中也可以看到,SIT起始位置一般都是6MB的地方。
我们使用T32实际查看一下segno 0中保存的信息
可以通过如下命令进行查看
v.v (struct f2fs_sit_entry*)0x600000
或者更优雅的方式,通过superblock中的sit_blkaddr进行计算
v.v (struct f2fs_sit_entry*)(((struct f2fs_super_block*)0x400)->sit_blkaddr*4096)
3.5 Node Address Table信息
3.5.1 使用dump.f2fs 查看NAT信息
可以通过如下命令查看Node Address Table信息
dump.f2fs -n 0~-1 f2fs_1000MB.bin
dump.f2fs -n 0~-1 f2fs_100MB.bin
对比数据如下
看到一个刚格式化好的F2FS有4个nid,并且blkaddr的起始地址对应的就是main area的起始地址。
3.5.2 使用T32 查看NAT信息
Node address table在磁盘上的数据结构为struct f2fs_nat_entry
Node address table的起始位置从superblock章节介绍中可以得出。起始位置为10MB。应为f2fs_nat_entry的下标就是对应的nid,所以nid为3对应的f2fs_nat_entry可以通过如下命令得出
V.v (struct f2fs_nat_entry*)0xA00000[3]
或者还是和之前一样,通过superblock更优雅的计算出出来
V.v (struct f2fs_nat_entry*)(((struct f2fs_super_block*)0x400)->nat_blkaddr*4096)+3
我们用T32计算出来的数据,和dump.f2fs抓取出来的数据进行一下对比
3.6 Segment Summary Area信息
3.6.1 使用dump.f2fs 查看SSA
可以通过如下命令查看SSA信息
dump.f2fs -a 0~-1 f2fs_1000MB.bin
dump.f2fs -a 0~-1 f2fs_100MB.bin
这里面就对应了所有segment数量
100MB=0x29+1=42个
1000MB=0x1e9+1=490个
3.6.2 使用T32 查看segment summary Area
Segment summary Area在磁盘上的数据结构为struct f2fs_summary_block,对应如下数据结构
每个f2fs_summary_block占用一个block大小(4K大小),每个f2fs_summary_block管理一个segment。对应的struct f2fs_summary entries共有512个,也就是每个f2fs_summary 管理一个segment下的block。
根据前面的介绍,我们可以计算出100MB/1000MB中,SSA区域的起始地址。
100MB SSA区域起始地址为14MB
1000MB SSA区域起始区域为18MB
还是以100MB的F2FS文件系统进行查看,查看segment为0时,SSA对应的管理区域,与dump.f2fs命令中的区域进行比较
V.v (struct f2fs_summary_block*)0xE00000
或者更优雅的方式
V.v (struct f2fs_summary_block*)(((struct f2fs_super_block*)0x400)->ssa_blkaddr*4096)
但是此时遇到和SIT一样的问题,实际对应位置中并没有保存,这个原因后续再研究。
3.7 Main area区域
Main area区域就是保存数据的地方了,前面提到过,这个区域由分为(node,data)*(hot,warn,cold) 6中segment类型。因为我们目前只是格式化了文件系统,并没有真正挂载或创建文件。所以这个区域在本章节中不展开分析,等后面章节再展开分析。
至此,我们就通过对比两个刚格式化好的100MB,1000MB的f2fs文件系统,对文件系统的基本组织方式有了一个了解。下面就在实际的文件系统中,进行文件增删改查,再观察一下F2FS文件系统的工作细节
四、挂载文件系统后F2FS文件系统变化
4.1 进行挂载操作
之前我们只是格式化了文件系统,但是并没有真正挂载。接下来,我们就通过挂载/卸载文件系统,观察上面介绍的信息是否会变化。
我们通过如下命令进行挂载,卸载,执行两次,每次都保存前面章节中的内容,然后进行对比
mkdir /mnt/temp_100MB_mountpoint
mount f2fs_100MB.bin /mnt/temp_100MB_mountpoint
umount /mnt/temp_100MB_mountpoint
dump.f2fs -d 1 f2fs_100MB.bin
dump.f2fs -s 0~-1 f2fs_100MB.bin
dump.f2fs -n 0~-1 f2fs_100MB.bin
dump.f2fs -a 0~-1 f2fs_100MB.bin
4.2 SuperBlock变化——挂载
挂载对superblock来说没有变化,(checkpoint相关信息不属于superblock中内容)
4.3 CheckPoint变化——挂载
首次挂载时,checkpoint有一些变化
后续挂载主要是time和checkpoint_ver信息的变化了
4.4 Segment Info Table变化——挂载
首次挂载时,将segment #0 的一些block,移动到了segment #1上,后续挂载没有观察到相关现象了
前面章节提到,使用T32查看这SIT区域中数据时是空的,此时实际查看SIT中的数据,依旧为空(依旧保存在checkpoint区域中)
4.5 Node Address Table变化——挂载
首次挂载时,nid有一些变化,这个和上面提到的SIT是想匹配的。Blk对应移动了位置,那NAT也会有对应变化。
4.6 Segment Summary Area变化——挂载
和上面对应,SSA 是从block反推到ino,所以上面block移动后,对应的SSA区域也有变化,后面挂载就没有变化了。
4.7 Main Area变化——挂载
因为文件系统中并没有实际数据,所以这里还是不关注了
五、新建文件后F2FS文件系统变化
5.1 新建一个小文件相关内容变化
本章内容,我们观察一下新建文件后,F2FS文件系统在磁盘上的变化。
首先挂载文件系统,之后创建一个名为Oplus_small_file.txt的文件,里面写入100遍”smallpaper_N ”字符串。通过如下命令生成这个文件
mkdir /mnt/temp_100MB_mountpoint
mount f2fs_100MB.bin /mnt/temp_100MB_mountpoint
i=0
while [ $i -le 100 ]
do
echo "smallpaper_$i " >> /sdcard/Oplus_small_file.txt
let i++
done
之后整体拷贝到f2fs下
cp /sdcard/Oplus_small_file.txt /mnt/temp_100MB_mountpoint/
查看这个文件已被创建,大小约1.4K
ls -alhi /mnt/temp_100MB_mountpoint/Oplus_small_file.txt
我们卸载这个文件系统,然后还是按照之前的套路,进行对比
umount /mnt/temp_100MB_mountpoint
dump.f2fs -d 1 f2fs_100MB.bin > dump_d_1_100MB.txt
dump.f2fs -s 0~-1 f2fs_100MB.bin
dump.f2fs -n 0~-1 f2fs_100MB.bin
dump.f2fs -a 0~-1 f2fs_100MB.bin
5.1.1 SuperBlock变化——创建小文件
创建一个小文件,superblock部分是没有变化的。这个也很好理解,superblock主要是保存的各个区域的起始地址,segment数量的,实际创建文件并不会引起明显变化
5.1.2checkpoint变化——创建小文件
Checkpoint是保持数据一致性的。简单来看,是记录的一些valid_block数量,node起始位置等信息发生了变化。
对比主要差异如下
创建小文件 | 未创建小文件 | 说明 | |
checkpoint_ver | [0x4009c26c : 1074381420] | [0x4009c268 : 1074381416] | Checkpoint version,正常递增 |
user_block_count | [0x3c00 : 15360] | [0x 3c00 : 15360] | |
valid_block_count | [0xc : 12] | [0xb : 11] | Valid block数量,创建了一个小文件(MA中只增加了一个node,所以这个数量+1) |
rsvd_segment_count | [0xa : 10] | [0xa : 10] | |
overprov_segment_count | [0xc : 12] | [0xc : 12] | |
free_segment_count | [0x 24 : 36] | [0x 24 : 36] | |
alloc_type[CURSEG_HOT_NODE] | [0x0 : 0] | [0x0 : 0] | |
alloc_type[CURSEG_WARM_NODE] | [0x0 : 0] | [0x0 : 0] | |
alloc_type[CURSEG_COLD_NODE] | [0x0 : 0] | [0x0 : 0] | |
cur_node_segno[0] | [0x0 : 0] | [0x0 : 0] | node记录,要结合cur_node_segno和下面的cur_node_blkoff看 |
cur_node_segno[1] | [0x1 : 1] | [0x1 : 1] | |
cur_node_segno[2] | [0x2 : 2] | [0x2 : 2] | |
cur_node_blkoff[0] | [0x5 : 5] | [0x4 : 4] | 这表示node_segno[0]的下一个可用的blk偏移为5 |
cur_node_blkoff[1] | [0xa : 10] | [0x3 : 3] | 这个表示node_segno[1],也就是segno 1的下一个可用的blk偏移为10,可以结合下面的SSA解析结果看,能看到segno 1前0个blk已经编号了 |
cur_node_blkoff[2] | [0x0 : 0] | [0x0 : 0] | |
alloc_type[CURSEG_HOT_DATA] | [0x0 : 0] | [0x0 : 0] | |
alloc_type[CURSEG_WARM_DATA] | [0x0 : 0] | [0x0 : 0] | |
alloc_type[CURSEG_COLD_DATA] | [0x0 : 0] | [0x0 : 0] | |
cur_data_segno[0] | [0x3 : 3] | [0x3 : 3] | |
cur_data_segno[1] | [0x14 : 20] | [0x 14 : 20] | |
cur_data_segno[2] | [0x9 : 9] | [0x9 : 9] | |
cur_data_blkoff[0] | [0xb : 11] | [0x7 : 7] | 与上面相同,表示data 类型的segment 3,下一个可用的blkoffset为11,可以结合SSA解析结果看(下面SSA解析结果中有说明) |
cur_data_blkoff[1] | [0x0 : 0] | [0x0 : 0] | |
cur_data_blkoff[2] | [0x0 : 0] | [0x0 : 0] | |
ckpt_flags | [0x1c5 : 453] | [0x 1c5 : 453] | |
cp_pack_total_block_count | [0x6 : 6] | [0x6 : 6] | |
cp_pack_start_sum | [0x1 : 1] | [0x1 : 1] | |
valid_node_count | [0x5 : 5] | [0x4 : 4] | 新增一个文件,新增了node |
valid_inode_count | [0x5 : 5] | [0x4 : 4] | 新增一个文件,新增了node |
next_free_nid | [0x8 : 8] | [0x7 : 7] | |
sit_ver_bitmap_bytesize | [0x40 : 64] | [0x40 : 64] | |
nat_ver_bitmap_bytesize | [0x40 : 64] | [0x40 : 64] | |
checksum_offset | [0xffc : 4092] | [0xffc : 4092] | |
elapsed_time | [0x1b : 27] | [0x6 : 6] | |
sit_nat_version_bitmap[0] | [0x0 : 0] | [0x0 : 0] |
5.1.3 Segment Info Table变化——创建小文件
SIT来看,vblock总数是多了一个block,并且之前的一些vblock位置也发生了改变。但是没有发现改变的规律。
前面章节提到过,我们之前在格式化及挂载后,通过T32都无法实际查看到SIT部分相关信息。那现在我们已经实际创建了一个文件,再次查看一下看是否有相关信息。依旧为空,所以这些信息还是从checkpoint区域中解析出来的
5.1.4 Node Address Table变化——创建小文件
NAT部分的话,主要是多了额一个ino=7的成员,这个和上面创建文件的inode对应
另外就是初始化的一些inode的blkaddr也发生了变化,这个就和上面的SIT中的信息匹配。
我们以新增的ino7为例
他对应的blkaddr 为4614
Main area的起始blkaddr为=4096,每个segment占据512个blk
所以ino7 对应的blk存放位置应该是4614-4096+1blk=1*segment+7blk,也就是segment NO1 的+7个blk位置
对应的SIT信息,也就是如下位置放置的ino7中的信息。这个segment type=4,所以对应blk存放的warm node
5.1.5 Segment Summary Area变化——创建小文件
这里的信息也是和上面联动的,可以通过blk地址反向计算出来这个blk所属的ino信息。我们先看一下上面计算的新建文件的信息,新建文件在segno1+7blk位置上,对应的SSA这个位置上保存的ino是7。这样也匹配上了
前面提到了checkpoint中保存的node/data segno对应的下一个可用的block信息
5.1.6 Main area变化——创建小文件
从前面的介绍,我们已经能看到新建文件的Node 信息保存在segNO1+7blk位置上。
我们用T32直接查看一下这个区域的内容。
通过很多方式都可以计算出偏移量,例如直接从NAT信息中,可以看到blk地址为4614
那它的偏移量就是4614*4K=0x1206000
用T32查看
dump.data 0x1206000
我们可以看到如下信息。还记得之前的介绍吗?Main area是将管理数据和实际数据分开的,也就是node应该保存的是管理数据,data才应该保存的是文件中的数据,那为什么这个node中保存了data数据呢?
这是因为F2FS为了节省空间,在数据较少的时候(小于3492字节),将data数据直接存放到node中
那node在盘上数据结构为f2fs_node,node有三种类型,inode,direct_node,indirect_node,这个主要是索引方式不同。最后还有一个node_footer来保存一些ino,blkaddr等信息
我们再用T32实际查看一下这段内容
V.v (struct f2fs_node *)0x1206000
这样就可以看到这个文件相关的元数据信息,以及存放在node末尾的footer信息,里面保存了nid/ino/checkpointversion等信息。
5.2 创建大文件相关内容变化
前面介绍是创建了一个小文件,相关信息变化。我们发现实际文件的data信息是保存在node中的。接下来,我们创建一个大文件,保证相关数据是大于node可容纳的data数据大小的。看看文件系统是如何进行排布的。还是和之前方式类似,创建一个比之前大1000倍的文件
mkdir /mnt/temp_100MB_mountpoint
mount f2fs_100MB.bin /mnt/temp_100MB_mountpoint
i=0
while [ $i -le 100000 ]
do
echo "largepaper_$i " >> /sdcard/Oplus_large_file.txt
let i++
done
cp /sdcard/Oplus_large_file.txt /mnt/temp_100MB_mountpoint
ls -alhi Oplus_large_file.txt
还是按照之前套路,将SIT,NAT,SSA信息导出(superblock,CP先忽略)
5.2.1 Segment Info Table变化——创建大文件
可以看到主要是node区域新增了一个blk,这个很好理解,每新增一个文件,就需要新增一个node进行管理,并且因为数据在node下已经保存不下了,所以需要真正分配data segment来存放这些数据
5.2.2 Node Address Table变化——创建大文件
这部分和新增一个小文件相同,增加了一个ino为8的索引,对应的存放node管理信息存放的blkaddr为4625
5.2.3 Segment Summary Area变化——创建大文件
这个与小文件一样,已经分配的blk,可以通过SSA精确的找到他是属于哪个nid的
5.2.4 Main area变化——创建大文件
从前面章节介绍可以看到,新增的大文件的Node 所在的blkaddr为4625,对应的Data主要是放在了segno14中的block中。
dump.f2fs命令也提供查看blk相关信息的命令
dump.f2fs -b 4625 f2fs_100MB.bin
可以看到这个blck属于哪个inode,名字是什么 文件size等相关信息
同样的查看data对应的blk也是一样的
前面看到segno值为0x14
所以他对应的blk size也可以计算出来,4096+0x14*512=14336
dump.f2fs -b 14336 f2fs_100MB.bin查看对应data blk属于哪个文件
接下来用T32查看node信息
计算一下node的起始地址为4625*4k=0x1211000
Data.dump 0x1211000
可以看到之前小文件存放data数据的区域,已经不再直接存放data了,而是放了另一些数据,具体放的什么数据我们后面介绍
V.v (struct f2fs_node *)0x1211000
可以查看到对应的node 管理信息
重点是有一个i_addr成员,这个成员指向了保存数据的blkaddr地址
共有923个成员
我们创建的文件也只有1MB,如果文件更大的话,node还会变为signle-indirect,double-indirect,Triple-indirest
类似内存管理中的三级映射,可以管理的文件逐步增加。这里就先不展开,有兴趣可以分析一个更大的文件看看。
本章内容,从创建一个两个文件入手,介绍了F2FS文件系统在盘上的存储内容及对应的变化,进一步加深了对SIT,NAT,SSA,MA 等区域的组织管理方法。
六、修改文件后F2FS文件系统变化
6.1 修改小文件
在前面操作的基础上,我们对之前创建的小文件进行修改,看F2FS文件系统会有什么变化
我们不改变文件的大小,只改变其中的字符,用”Nsmal”替换到”small”试试看
mkdir /mnt/temp_100MB_mountpoint
mount f2fs_100MB.bin /mnt/temp_100MB_mountpoint
i=0
while [ $i -le 100 ]
do
echo "Nsmalpaper_$i " >> /sdcard/Oplus_New_small_file.txt
let i++
done
之后覆盖之前的文件,mount文件系统后,进行覆盖操作
cp /sdcard/Oplus_New_small_file.txt /mnt/temp_100MB_mountpoint/Oplus_small_file.txt
umount /mnt/temp_100MB_mountpoint
之后还是之前的讨论
dump.f2fs -d 1 f2fs_100MB.bin > dump_d_1_100MB.txt
dump.f2fs -s 0~-1 f2fs_100MB.bin
dump.f2fs -n 0~-1 f2fs_100MB.bin
dump.f2fs -a 0~-1 f2fs_100MB.bin
6.1.1 superblock变化——修改一个小文件
Superblock部分依旧没有什么变化
6.1.2 Checkpoint变化——修改一个小文件
cp区域,cur_node_blkoff 增加了1,其他没有变化,checkpoint可以结合前面一章介绍分析,这里先不展开
6.1.3 Segment Info Table变化——修改小文件
SIT区域vblock数量没有变化,只是node区域的blk存放位置发生了变化
6.1.4 Node address table变化——修改小文件
ino:7对应的blk发生了变化
6.1.5 Segment Summary address——修改小文件
SSA中信息就比较有意思了,只是segno 0x1的18 blk新增了一个 SSA信息。这个偏移量计算出来就是
4096+0x1*512+18=4626,也就是新的node所在位置
但是之前5.1.6章节中,介绍的老文件的node还在
6.1.6 Main area变化——修改小文件
对于MA区域,关注新/旧两个node
先用dump.f2fs命令查看
旧:dump.f2fs -b 4614 f2fs_100MB.bin
新:dump.f2fs -b 4626 f2fs_100MB.bin
用dump.f2fs命令查看后,看到旧的blk还是可以查询出来的
在T32上继续对比
旧:data.dump 0x1206000
新:data.dump 0x1212000
实际查看就的blk中的内容已经为空了
从上面的介绍来看,修改一个文件的话,对应的SSA中是保存的对应文件的信息的。其他模块,以及对应老的blk中,是没有相关信息的。
七、删除文件后F2FS文件系统变化
删除一个大文件后,整体的效果和修改一个文件类似。NAT/SIT中,对应信息都会释放掉,但SSA中,还会保留之前的blk分配信息。有兴趣的可以按照前面两章介绍的对比方式进行对比。这里就不再整理了
至此,F2FS文件系统在磁盘上的组织方式就介绍完了。本文把F2FS文件系统的逻辑当成了一个黑盒,只关注F2FS文件系统在相关文件操作后,实际落盘到磁盘上的内容变化,希望大家可以通过这篇文件,了解F2FS文件系统的基本概念,后续阅读源码时能更容易理解相关逻辑。
下期预告:深入代码细节看f2fs在磁盘上的组织方式
往
期
推
荐
长按关注内核工匠微信
Linux内核黑科技| 技术文章| 精选教程