抛开代码细节看f2fs文件系统组织方式

 一、前言

“文件系统是操作系统用于明确存储设备(常见的是磁盘,也有基于NAND Flash的固态硬盘)或分区上的文件的方法和数据结构;即在存储设备上组织文件的方法。操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统。文件系统由三部分组成:文件系统的接口,对对象操纵和管理的软件集合,对象及属性。从系统角度来看,文件系统是对文件存储设备的空间进行组织和分配,负责文件存储并对存入的文件进行保护和检索的系统。具体地说,它负责为用户建立文件,存入、读出、修改、转储文件,控制文件的存取,当用户不再使用时撤销文件等。”

“F2FS (Flash Friendly File System) 是专门为基于 NAND 的存储设备设计的新型开源 flash 文件系统。特别针对NAND 闪存存储介质做了友好设计。”         

文件系统就是管理和组织磁盘上文件的一种方式,本文就抛开抛开代码细节,使用T32/dump.f2fs这两个工具,观察磁盘上,F2FS 在创建,新增文件,删除文件等操作后,相关内容的变化。帮忙大家直观了解F2FS文件系统在磁盘上的组织方式。

二、F2FS文件系统总览

F2FS文件系统的磁盘上的组织结构如下

9571e4a23dcb6e635b9f4fa786849c8b.png

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。示意图如下

0c0458c8f41845ab8a370463934d75ec.png

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种数据类型。

b5b0b8031d9a98b1897dd9805ac85013.png

本章内容对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

对比差异如下    

9db6c9ceed6abfb9123651c7fc262fe8.png

整理成表格对比数据及说明如下

条目

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,所以                  
25600*4K=100MB                  
256000*4K=1000MB                  
这里的blocks数量是整个空间的大小,包括superblock/SIT/NAT/SSA/MAIN等

section_count                

[0x2a : 42]

[0x1ea : 490]

main area section数量                  
42=50-1(sb)-2(cp)-2(sit)-2(nat)-1(ssa)                  
490=500-1(sb)-2(cp)-2(sit)-4(nat)-1(ssa)

segment_count                

[0x31 : 49]

[0xf3 : 499]

segment数量,应该不包含superblock(2MB)                  
49*2MB=98MB=100MB-2MB                  
499*2MB=998MB=1000MB-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计算方式一样                  
main area segment数量                  
42=50-1(sb)-2(cp)-2(sit)-2(nat)-1(ssa)                  
490=500-1(sb)-2(cp)-2(sit)-4(nat)-1(ssa)

segment0_blkaddr             

[0x200 : 512]

[0x200 : 512]

segment0的blkaddr地址,上面计算可以看出来blkaddr从地址0开始的,segment数量是不包含superblock的,所以这里从512*4K=2MB开始计算

cp_blkaddr                   

[0x200 : 512]

[0x200 : 512]

cp的起始block位置,这个也好计算,就是2MB开始的地方                  
512*4K=2MB

sit_blkaddr                  

[0x600 : 1536]

[0x600 : 1536]

sit起始位置,应该固定为6MB                  
1536*4K=6MB=2MB(superblock)+2*2MB(cp)

nat_blkaddr                  

[0xa00 : 2560]

[0xa00 : 2560]

node address table起始位置,目前应该是                  
2560*4K=10MB=2MB(superblock)+2*2MB(cp)+2*2MB(SIT)

ssa_blkaddr                  

[0xe00 : 3584]

[0x1200 : 4608]

segment summary area起始地址,由于前面nat数量有差异,所以这里计算的起始地址就有差异了                  
100MB:3584*4K=14MB=2MB(superblock)+2*2MB(cp)+2*2MB(SIT)+2*2MB(NAT)                  
1000MB:4608*4K=18MB=2MB(superblock)+2*2MB(cp)+2*2MB(SIT)+4*2MB(NAT)

main_blkaddr                 

[0x1000 : 4096]

[0x1400 : 5120]

main area起始的blk地址                  
计算方式就是把前面的加起来                  
100MB:4096*4K=16MB=2MB(superblock)+2*2MB(cp)+2*2MB(SIT)+2*2MB(NAT)+1*2MB(SSA)                  
1000MB:5120*4K=20MB=2MB(superblock)+2*2MB(cp)+2*2MB(SIT)+4*2MB(NAT)+1*2MB(SSA)

root_ino                     

[0x3 : 3]

[0x 3 : 3]

对应的一些ino编号,后面再详细介绍                  
root_ino=3

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    

4c0150543bbe7e6c732c581c5fd0dc50.png

我们借助vmlinux,就可以很方便的以kernel中结构体中的内容查看对应superblock信息,具体命令如下

Superblock#0 对应命令V.v (struct f2fs_super_block*)0x400

Superblock#1 对应命令V.v (struct f2fs_super_block*)0x1400

我们对比100MB/1000MB superblock信息如下。对应保存的信息,及差异在上面一节中介绍过了,这里就不赘述    

0e770ac7f2ecc98867916acb8213e728.png

3.3 checkpoint信息

3.3.1 使用dump.f2fs查看checkpoint信息

与查看superblock命令一样,在输出superblock信息后,就会对应输出checkpoint信息。

因为checkpoint为了保持一致性的区域,100MB/1000MB对比是没有意义的,这里先不展开说明。后续在实际操作文件的时候,再展开说明。    

b9c9d2ff8363180d3af31bd86e559e39.png

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

ad82e591d1c0837b493a11e0e92d347f.png

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中会显示什么信息。

133d67c58631f80aec9811767fe6dc22.png

首先是说明了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类型。         

对应的结尾会有如下打印

af4b129c025273f7dcf16b9fee2b0927.png

当前valid_blocks数量,也就是前面各个segmentvblocks之和,7+4=11=0xb

以及当前多少个segment中有有内容,valid_segs:2,空闲的segment数量:40

可以计算出来总的segment数量是42个。与superblock中segment_count_main对应

c2d8ff6d5550eb293d752cd2c80b49f5.png

1000MB的信息也是类似,在结尾可以看到总segment数量是488+2=490个,也就是main area中共有490个segment

71a53ddb6cfafe991643d39171866196.png

3.4.2 使用T32查看SIT信息

Segment Info Table在磁盘上对应的结构体是struct f2fs_sit_entry,为了理解这个数据结构,贴一小段定义代码。    

012f861bdb46409b245d0c21f41857d5.png

一个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

对比数据如下

b1f457c841eea12925c0388114f407c3.png

看到一个刚格式化好的F2FS有4个nid,并且blkaddr的起始地址对应的就是main area的起始地址。

55d0a15f96f93a0e0bd50f75f21747b3.png

3.5.2 使用T32 查看NAT信息

Node address table在磁盘上的数据结构为struct f2fs_nat_entry    

6276e405773aeab0110aace596d6937f.png

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抓取出来的数据进行一下对比

e42265c85d71f0e4ee2b839258b92d1f.png      

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

422ec38f7f2b27bddc751760d46b1589.png

这里面就对应了所有segment数量

100MB=0x29+1=42个

1000MB=0x1e9+1=490个

41391c5e682deadb7a811e5bd8212aa0.png    

b0f7104148a239073d048daada372c6c.png  

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。

eca7cf6e913135f0c7d56152f3985198.png    

7beecc4e8360e0a8914ee677cf8ba64c.png

根据前面的介绍,我们可以计算出100MB/1000MB中,SSA区域的起始地址。

100MB SSA区域起始地址为14MB

1000MB SSA区域起始区域为18MB

07f65149f51272218279a48c5bb0c2ad.png

还是以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中内容)    

b5cafb2596bf9024f1aba97a5c65752f.png

4.3 CheckPoint变化——挂载

首次挂载时,checkpoint有一些变化

后续挂载主要是time和checkpoint_ver信息的变化了

a2bb7c454334fe359018a1376385b223.png

4.4 Segment Info Table变化——挂载

首次挂载时,将segment #0 的一些block,移动到了segment #1上,后续挂载没有观察到相关现象了

868170773d4bd9c8461d1220b6a651a2.png            

前面章节提到,使用T32查看这SIT区域中数据时是空的,此时实际查看SIT中的数据,依旧为空(依旧保存在checkpoint区域中)

4.5 Node Address Table变化——挂载

首次挂载时,nid有一些变化,这个和上面提到的SIT是想匹配的。Blk对应移动了位置,那NAT也会有对应变化。

64d1e03f56efdd236513d2713ba86ea5.png

4.6 Segment Summary Area变化——挂载

和上面对应,SSA 是从block反推到ino,所以上面block移动后,对应的SSA区域也有变化,后面挂载就没有变化了。

be44086bde6bfd0fd240585f007d9761.png

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

b824c35f150eef08c049eb24cfcdb7bb.png

我们卸载这个文件系统,然后还是按照之前的套路,进行对比

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数量的,实际创建文件并不会引起明显变化    

8f2ce521374877632220ef6236f19b38.png

5.1.2checkpoint变化——创建小文件

Checkpoint是保持数据一致性的。简单来看,是记录的一些valid_block数量,node起始位置等信息发生了变化。    

7f8c0df886075a16031accbee2a2954e.png

对比主要差异如下

创建小文件

未创建小文件

说明

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位置也发生了改变。但是没有发现改变的规律。    

3ef9011d72ebb30d328a93e79316d910.png

前面章节提到过,我们之前在格式化及挂载后,通过T32都无法实际查看到SIT部分相关信息。那现在我们已经实际创建了一个文件,再次查看一下看是否有相关信息。依旧为空,所以这些信息还是从checkpoint区域中解析出来的

5.1.4 Node Address Table变化——创建小文件

NAT部分的话,主要是多了额一个ino=7的成员,这个和上面创建文件的inode对应

a0e6dc97f18340eb0708899f5fc52689.png

另外就是初始化的一些inode的blkaddr也发生了变化,这个就和上面的SIT中的信息匹配。

704acf7b0ae2cd1f4c2281add2888e48.png

我们以新增的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

7377723f2cf72c9fe1ef9e3054c01be2.png

5.1.5 Segment Summary Area变化——创建小文件

这里的信息也是和上面联动的,可以通过blk地址反向计算出来这个blk所属的ino信息。我们先看一下上面计算的新建文件的信息,新建文件在segno1+7blk位置上,对应的SSA这个位置上保存的ino是7。这样也匹配上了    

d2596f6b6b8176e762a3cbf882fa6d9b.png

前面提到了checkpoint中保存的node/data segno对应的下一个可用的block信息

3d19fcaed2323984b443d3eba6aad0bc.png

c4a622801c101debf56872512fb5870b.png

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中

a6840938e6643785ca7d27289a655fa6.png      

那node在盘上数据结构为f2fs_node,node有三种类型,inode,direct_node,indirect_node,这个主要是索引方式不同。最后还有一个node_footer来保存一些ino,blkaddr等信息

7164e14167b9e65c8dc23d1f9d6f5374.png    

我们再用T32实际查看一下这段内容

V.v (struct f2fs_node *)0x1206000

这样就可以看到这个文件相关的元数据信息,以及存放在node末尾的footer信息,里面保存了nid/ino/checkpointversion等信息。

d6674e6926b1cf403e5d1ae5306f53a9.png

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

98e7dfae964918306b3c7246caf7ae07.png

还是按照之前套路,将SIT,NAT,SSA信息导出(superblock,CP先忽略)

5.2.1 Segment Info Table变化——创建大文件

可以看到主要是node区域新增了一个blk,这个很好理解,每新增一个文件,就需要新增一个node进行管理,并且因为数据在node下已经保存不下了,所以需要真正分配data segment来存放这些数据    

85341e388ed079ae460458a3f267a702.png

5.2.2 Node Address Table变化——创建大文件

这部分和新增一个小文件相同,增加了一个ino为8的索引,对应的存放node管理信息存放的blkaddr为4625    

16655b6c34124c10c2dbee432d4c02dc.png

5.2.3 Segment Summary Area变化——创建大文件

这个与小文件一样,已经分配的blk,可以通过SSA精确的找到他是属于哪个nid的

a2ce06ee5d1077189f8ad5d0ec5b628d.png

5.2.4 Main area变化——创建大文件

从前面章节介绍可以看到,新增的大文件的Node 所在的blkaddr为4625,对应的Data主要是放在了segno14中的block中。

dump.f2fs命令也提供查看blk相关信息的命令

dump.f2fs -b 4625 f2fs_100MB.bin

可以看到这个blck属于哪个inode,名字是什么 文件size等相关信息

d9905fb2189e5543f31558475658c3d0.png

同样的查看data对应的blk也是一样的

前面看到segno值为0x14

所以他对应的blk size也可以计算出来,4096+0x14*512=14336

dump.f2fs -b 14336 f2fs_100MB.bin查看对应data blk属于哪个文件    

0d9d1b02a893fde3bba75e3250101908.png

接下来用T32查看node信息

计算一下node的起始地址为4625*4k=0x1211000

Data.dump 0x1211000

可以看到之前小文件存放data数据的区域,已经不再直接存放data了,而是放了另一些数据,具体放的什么数据我们后面介绍

3ba2834508175448164e3f3027e7d3e2.png         

V.v (struct f2fs_node *)0x1211000

可以查看到对应的node 管理信息

a5c6519da0f431c3f50ac681a2b60b31.png

重点是有一个i_addr成员,这个成员指向了保存数据的blkaddr地址    

0ac51532c849bab8a57bc3e62c493afa.png

共有923个成员

0c262e7d35aabb3ff89ab79ffa9bb117.png           

我们创建的文件也只有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部分依旧没有什么变化

fc88a76e1faff2f9113113eba6d5078d.png

6.1.2 Checkpoint变化——修改一个小文件

cp区域,cur_node_blkoff 增加了1,其他没有变化,checkpoint可以结合前面一章介绍分析,这里先不展开    

3cf451daeed7b51a02c07a6d76cabe3e.png

6.1.3 Segment Info Table变化——修改小文件

SIT区域vblock数量没有变化,只是node区域的blk存放位置发生了变化

63b813eb0f81c2582520fbb8935c16c7.png

6.1.4 Node address table变化——修改小文件

ino:7对应的blk发生了变化

11bed2e66fd062077d9b0a46e163c37b.png

6.1.5 Segment Summary address——修改小文件    

SSA中信息就比较有意思了,只是segno 0x1的18 blk新增了一个 SSA信息。这个偏移量计算出来就是

4096+0x1*512+18=4626,也就是新的node所在位置

但是之前5.1.6章节中,介绍的老文件的node还在

593857c4883cd72b0d641d08b02bf652.png

6.1.6 Main area变化——修改小文件

对于MA区域,关注新/旧两个node

先用dump.f2fs命令查看

旧:dump.f2fs -b 4614 f2fs_100MB.bin

新:dump.f2fs -b 4626 f2fs_100MB.bin

b776f8b36487dd22f270b5b024c0ed65.png

用dump.f2fs命令查看后,看到旧的blk还是可以查询出来的    

在T32上继续对比

旧:data.dump 0x1206000

新:data.dump 0x1212000

实际查看就的blk中的内容已经为空了

33bd458bb6eb90ffa909a032b0308235.png

从上面的介绍来看,修改一个文件的话,对应的SSA中是保存的对应文件的信息的。其他模块,以及对应老的blk中,是没有相关信息的。        

七、删除文件后F2FS文件系统变化

删除一个大文件后,整体的效果和修改一个文件类似。NAT/SIT中,对应信息都会释放掉,但SSA中,还会保留之前的blk分配信息。有兴趣的可以按照前面两章介绍的对比方式进行对比。这里就不再整理了     

至此,F2FS文件系统在磁盘上的组织方式就介绍完了。本文把F2FS文件系统的逻辑当成了一个黑盒,只关注F2FS文件系统在相关文件操作后,实际落盘到磁盘上的内容变化,希望大家可以通过这篇文件,了解F2FS文件系统的基本概念,后续阅读源码时能更容易理解相关逻辑。    

下期预告:深入代码细节看f2fs在磁盘上的组织方式

全方位剖析内核抢占机制

Linux V4L2子系统与视频编解码设备介绍

内核调度客制化利器:SCHED_EXT

18055a1eed53abc06d56957133a3d395.gif

长按关注内核工匠微信

Linux内核黑科技| 技术文章| 精选教程

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

内核工匠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值