有关于Linux文件系统的发展历史可以参考http://e2fsprogs.sourceforge.net/ext2intro.html
虚拟文件系统VFS
Linux支持多种不同类型的文件系统:网络文件系统NFS,磁盘文件系统Extfs,特殊文件系统proc、tmpfs等。为了更方便的在系统中集成多个不同的文件系统,Linux实现了一层叫做Virtual File System的layer,其在系统中的层次结构如下图。
VFS直接处理文件操作相关的系统调用,然后根据文件系统类型将这些调用重定向到底层文件系统的接口,底层文件系统再通过buffer cache向设备发起I/O请求(仅限于块I/O,字符设备的I/O没有buffer cache参与)。
VFS定义了文件系统基本操作的函数集合,底层的文件系统(Ext2fs...)都要实现这个函数集合。kernel维护着一张记录了所有支持的文件系统类型的表,在这张表里面,记录了每个文件系统类型的名字和mount时要执行的函数的指针,当文件系统被mount到某个挂载点时,这个mount函数就会被执行,它会读取磁盘上的superblock,初始化一些数据,并且将mount后的文件系统描述符返回给VFS,VFS则通过这个文件系统描述符来访问底层文件系统。
Ext2fs
实际上Ext2fs之前还有Extfs,但是这个版本问题比较多,并且缺乏很多的基本特性支持(比如3个时间戳),没有得到广泛使用,所以我们直接从Ext2fs开始学习。
Ext2fs基本概念
Inode
Ext2fs中,每个文件都用如下图所示的inode结构来描述,用户空间操作的对象是文件路径和名称,系统kernel把路径名称解析成inode,通过inode号来访问它代表的文件。
转存失败重新上传取消转存失败重新上传取消转存失败重新上传取消
Mode:包含两个数据,文件类型(普通文件/目录/字符设备/块设备/符号链接/管道)和用户访问权限信息(0660)。
Owner info:文件属组信息。
Size:文件长度,单位是byte。
Timestamps:文件访问和修改的时间戳。
Links count:这个项在上图中没有体现,它记录了这个inode存在多少个链接,创建新文件时,其inode的links count应该为1,文件被删除后,这个inode的links count就变为0。
Data Blocks:指向真实的文件数据块,因为大文件可能会分配很多的block,直接在inode中保存所有的数据块指针将会比较困难,也会浪费掉很多空间,毕竟系统中大文件的数量是占少数的,所以设计了间接块指针(Indirect blocks)和二级块指针(Double blocks)来指向真实数据块。
实际上还应该包含了inode号。
目录
在Ext2fs中,目录被看做一种特殊文件,也用一个inode来描述,目录的data block中保存了目录下的所有内容,每条内容叫做一个entry,结构如下:
每条entry都保存了inode号、entry的长度、文件名长度、文件类型,并且都是4字节对齐。
特别地,每个目录下有两个特殊的子目录,'.'和'..',分别代表当前目录和上一级目录,这两个目录文件其实是硬链接。其中'..'有一个重要的作用:FS checker(可以把文件系统umount后手动执行e2fsck看看)在检查文件系统的时候,就会使用’..‘来检查目录是否可以追溯到挂载根目录,如果检查失败,目录便会被链接到挂载根目录下面的lost+found。
链接
为了方便系统内文件共享,Linux支持了两种基本的链接文件:硬链接和软链接(也叫符号链接)。
硬链接并不是一个独立的文件,不占用inode,只是在目录下创建了一条entry,其中inode号保存的是目标文件的inode号,访问硬链接时,文件系统通过inode将访问操作重定向到目标文件,实现了文件共享,所以硬链接就是多个文件名直接指向同一个inode,用stat命令也能看到其inode号就是目标文件的inode号,它的特点:
- 不能跨文件系统。
- 目标文件必须先存在(inode存在且link count不为0)。
- 只能对普通文件创建硬链接,目录不行。
- 删除一个硬链接文件并不影响其他有相同 inode 号的文件。
软链接是一个独立的文件,拥有自己的inode,其数据块存放的是目标文件的名称,访问软链接时,kernel先访问软链接的内容,拿到目标文件名,并重新启动路径解析,获取到目标文件inode号再向文件系统发起访问。软链接的特点:
- 可以跨文件系统。
- 文件和目录都可以。
- 可对不存在的文件或目标创建软链接。
- 软链接有自己文件属性和权限。
- 创建软链接时,链接计数 i_nlink 不会增加;
- 删除软链接并不影响被指向的文件,但若被指向的原文件被删除,则相关软连接被称为死链接(即 dangling link,若被指向路径文件被重新创建,死链接可恢复为正常的软链接)。
- 软链接的目标文件也可以是软链接,其解析过程是递归的。
注意:软链接创建时目标文件的路径指向使用绝对路径比较好,使用相对路径创建的软链接被移动后该软链接文件将成为一个死链接,因为链接数据块中记录的也是相对路径指向。
下面这个图清晰描述了硬链接和软连接之间的区别:
Ext2fs基本结构
在创建文件系统的时候,Ext2fs将设备(磁盘或者分区)划分成1K、2K或者4K的block,然后通过Block group来管理,Ext2fs/Ext3fs/Ext4fs的结构差不多(Ext2fs主要是少了日志功能相关的内容),大致如下图所示:
Super Block
Super Block是文件系统最重要的数据,它从设备开始位置偏移1024字节的地方开始存储,占用1个block。如果block的大小是1KB,那么Super Block就存放在block-1。如果block的大小是4KB,那Super block就存放在block-0。
在Ext2fs的第一个版本(reverson0),每个Block Group都会存储一份Super Block的一份副本,因为对空间浪费比较严重,后来的版本就只在部分Block Group(0、1、3、5、7、9这几个group)中保留了Super Block的副本,在这几个Group,和Super Block一起备份的还有Group Descriptor。当然,如果没有这么多Group,副本数量自然更少,在后面的demo中也可以看出来。其中Group-0中的Super Block叫作Primary Super Block,文件系统被mount时,VFS读取的也正是这份。
Super Block里面的具体数据包括:
- inode和block的总数,以及还有多少未分配。
- 每个Block Group有多少个inode和block。
- 文件系统唯一身份标识符(UUID),每个设备上的文件系统UUID都不一样。
- ...
GDT
Group Descriptor Table,GDT在文件系统中的layout紧跟Super block后面,是文件系统第二关键的数据,它主要用于存放所有Block Group的信息:
- block bitmap、inode bitmap和inode table的位置。
- block bitmap和inode bitmap的校验和。
- 未分配的block和inode的数量。
- ...
Ext2fs为GDT预留了一部分空间,用于文件系统扩容。
Block Bitmap
Block Bitmap记录了group中block的状态(allocated or unallocated),只占用一个block的空间,每个bit表示一个block。对于1KB大小的block,最多可以表示8192个block,所以每个Block group都有8192个block。
Inode Bitmap
Inode Bitmap记录了group中inode的状态(allocated or unallocated),只占用一个block的空间。
Inode Table
Inode table保存了Block Group内所有inode的详细数据。对于总大小52M,block size等于1KB的文件系统,每个inode占用128字节,每个group有1904个inode,所以占用238KB,也就是238个block。当总大小和block size变化时,inode占用的大小(可能是258个字节)和每个group的inode数量也会变化。
Data Blocks
真正存放文件数据的块。
Block Group
Block Group的概念包含了上面的Inode Bitmap、Inode Bitmap、Inode Table和Data Blocks等。Block Group划分的数目可以根据Block Bitmap来计算,比如总大小为52M,block size为1KB的文件系统,总共有53248个block,根据前面的计算Block Bitmap可以表示8192个block,所以划分出7个Block Group。如果同样的设备,block size设置成4KB,总共有13312个block,而Block Bitmap可以表示32768个block,所以就只有1个Block Group-0了。
Block Group对于文件系统其实并不是必须的,但是它的设计确实有一些优点:
- 通过冗余提高了文件系统可靠性:在多个group中保存了关键数据的冗余副本,包括super block、GDT,当这些关键数据损坏的时候,很容易从这些冗余副本中恢复。
- 提升性能:分成group后,inode table和data block之间的”距离“变近了,在执行I/O时,可能会减少磁头寻址的时间。
注:实际上inode size,每个group中的block数等参数都可以在创建文件系统的时候指定,具体命令参数参考man page。
Ext2fs的性能优化
为了提升I/O性能,Ext2fs内核代码也做了很多设计,其中有两个关键的技术:
- 提前读:当必须读取一个块时,内核代码在几个连续的块上请求I/O。通过这种方式,它试图确保要读取的下一个块已经加载到缓冲区缓存中。提前读通常在文件的连续读取期间执行,Ext2fs将它们扩展到目录读取,可以是显式读取(readdir(2)调用),也可以是隐式读取(namei内核目录查找)。
- 预分配:在将数据写入文件时,Ext2fs在分配新块时预先分配最多8个相邻块。具体预分配多少个块取决于block size:block size = 1KB,每次预分配2个block;block size = 2KB,每次预分配4个block;block size = 4KB,每次预分配8个block。当然,对于用touch创建的空文件是不会预分配block的。即使在非常满的文件系统上,预分配命中率也只有75%左右。这种预分配在负载较大的情况下可以获得良好的写性能,同时它还允许将连续的块分配给文件,从而加快未来的顺序读取。
Dump Demo
作为demo,用LVM创建了一个52M大小的逻辑卷,然后将卷信息用dumpe2fs获取出来,此时挂载目录下还没有任何用户数据,可以看到一个文件系统刚刚初始化的情况:
Filesystem volume name: <none>
Last mounted on: /home/hunk/fs/ext2.mount #最近一次挂载点
Filesystem UUID: 63fc1b4a-63a6-47ac-8c42-f8651eee2abb
Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic)
Filesystem features: ext_attr resize_inode dir_index filetype sparse_super large_file
Filesystem flags: signed_directory_hash
Default mount options: user_xattr acl
Filesystem state: not clean #这个状态涉及到文件系统自身的检查机制
Errors behavior: Continue #检查到文件系统错误时的行为,可以通过tunefs修改这个行为
Filesystem OS type: Linux
Inode count: 13328
Block count: 53248
Reserved block count: 2662
Free blocks: 50717 #未分配的block
Free inodes: 13317 #未分配的inode
First block: 1
Block size: 1024 #块的大小,可以是1024、2048、4096
Fragment size: 1024
Reserved GDT blocks: 207 #给GDT预留的空间
Blocks per group: 8192 #每个group包含8192个block
Fragments per group: 8192
Inodes per group: 1904 #每个group有1904和iNode
Inode blocks per group: 238 #每个inode占用128字节,128*1904/1024=238KB
Filesystem created: Sat Jan 5 22:12:20 2019
Last mount time: Sat Jan 5 22:18:50 2019
Last write time: Sat Jan 5 22:18:50 2019
Mount count: 1 #每次mount后计数加1,达到Maximum mount count后会提示做文件系统检查
Maximum mount count: -1
Last checked: Sat Jan 5 22:12:20 2019 #filesystem checker上一次检查的时间
Check interval: 0 (<none>)
Reserved blocks uid: 0 (user root)
Reserved blocks gid: 0 (group root)
First inode: 11 #文件系统本身占用了一些inode,比如挂载根目录文件
Inode size: 128 #每个inode占用的空间,也可能是256字节或者其它,取决于Ext2fs相关的分配算法
Default directory hash: half_md4
Directory Hash Seed: a376209a-1a8d-438d-b0ef-5b3b9bac11bc
Group 0: (Blocks 1-8192)
Primary superblock at 1, Group descriptors at 2-2 #前面1024字节(block0)空闲
Reserved GDT blocks at 3-209
Block bitmap at 210 (+209), Inode bitmap at 211 (+210)
Inode table at 212-449 (+211)
7729 free blocks, 1892 free inodes, 2 directories
Free blocks: 464-512, 514-8192
Free inodes: 13-1904
Group 1: (Blocks 8193-16384)
Backup superblock at 8193, Group descriptors at 8194-8194
Reserved GDT blocks at 8195-8401
Block bitmap at 8402 (+209), Inode bitmap at 8403 (+210)
Inode table at 8404-8641 (+211)
7743 free blocks, 1904 free inodes, 0 directories
Free blocks: 8642-16384
Free inodes: 1905-3808
Group 2: (Blocks 16385-24576)
Block bitmap at 16385 (+0), Inode bitmap at 16386 (+1)
Inode table at 16387-16624 (+2)
7952 free blocks, 1904 free inodes, 0 directories
Free blocks: 16625-24576
Free inodes: 3809-5712
Group 3: (Blocks 24577-32768)
Backup superblock at 24577, Group descriptors at 24578-24578
Reserved GDT blocks at 24579-24785
Block bitmap at 24786 (+209), Inode bitmap at 24787 (+210)
Inode table at 24788-25025 (+211)
7743 free blocks, 1904 free inodes, 0 directories
Free blocks: 25026-32768
Free inodes: 5713-7616
Group 4: (Blocks 32769-40960)
Block bitmap at 32769 (+0), Inode bitmap at 32770 (+1)
Inode table at 32771-33008 (+2)
7952 free blocks, 1904 free inodes, 0 directories
Free blocks: 33009-40960
Free inodes: 7617-9520
Group 5: (Blocks 40961-49152)
Backup superblock at 40961, Group descriptors at 40962-40962
Reserved GDT blocks at 40963-41169
Block bitmap at 41170 (+209), Inode bitmap at 41171 (+210)
Inode table at 41172-41409 (+211)
7743 free blocks, 1904 free inodes, 0 directories
Free blocks: 41410-49152
Free inodes: 9521-11424
Group 6: (Blocks 49153-53247)
Block bitmap at 49153 (+0), Inode bitmap at 49154 (+1)
Inode table at 49155-49392 (+2)
3855 free blocks, 1904 free inodes, 0 directories
Free blocks: 49393-53247
Free inodes: 11425-13328
下面是Ext2fs、Ext3fs和Ext4fs的一个简单对比:
- 只有Ext2fs的Filesystem state是not clean,Ext3fs和Ext4fs都是clean,Ext2fs刚被以读写模式mount时,这个state被设置成not clean,umount或者以只读模式mount时,state被设置成clean,启动时文件系统根据这个状态来决定是否要执行检查。
- Ext3fs/Ext4fs的Super block中多了关于日志功能的信息。
- Ext4fs的每个group多了校验和(checksum)数据。
root@hunk-virtual-machine:/home/hunk/fs# vimdiff ext2.mount/lv.ext2.dump ext3.mount/lv.ext3.dump ext4.mount/lv.ext4.dump
Filesystem volume name: <none> | Filesystem volume name: <none> | Filesystem volume name: <none>
Last mounted on: /home/hunk/fs/ext2.mount | Last mounted on: /home/hunk/fs/ext3.mount | Last mounted on: /home/hunk/fs/ext4.mount
Filesystem UUID: 63fc1b4a-63a6-47ac-8c42-f8651eee2abb | Filesystem UUID: 628034d1-32d8-4290-9707-1c3e2adf73b1 | Filesystem UUID: 810468bd-7db9-44c3-92af-1c90e7cfe829
Filesystem magic number: 0xEF53 | Filesystem magic number: 0xEF53 | Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic) | Filesystem revision #: 1 (dynamic) | Filesystem revision #: 1 (dynamic)
Filesystem features: ext_attr resize_inode dir_index filetype spa| Filesystem features: has_journal ext_attr resize_inode dir_index | Filesystem features: has_journal ext_attr resize_inode dir_index
Filesystem flags: signed_directory_hash | Filesystem flags: signed_directory_hash | Filesystem flags: signed_directory_hash
Default mount options: user_xattr acl | Default mount options: user_xattr acl | Default mount options: user_xattr acl
Filesystem state: not clean | Filesystem state: clean | Filesystem state: clean
Errors behavior: Continue | Errors behavior: Continue | Errors behavior: Continue
Filesystem OS type: Linux | Filesystem OS type: Linux | Filesystem OS type: Linux
Inode count: 13328 | Inode count: 13328 | Inode count: 13328
Block count: 53248 | Block count: 53248 | Block count: 53248
Reserved block count: 2662 | Reserved block count: 2662 | Reserved block count: 2662
Free blocks: 50717 | Free blocks: 46604 | Free blocks: 46621
Free inodes: 13317 | Free inodes: 13317 | Free inodes: 13317
First block: 1 | First block: 1 | First block: 1
Block size: 1024 | Block size: 1024 | Block size: 1024
Fragment size: 1024 | Fragment size: 1024 | Fragment size: 1024
Reserved GDT blocks: 207 | Reserved GDT blocks: 207 | Reserved GDT blocks: 207
Blocks per group: 8192 | Blocks per group: 8192 | Blocks per group: 8192
Fragments per group: 8192 | Fragments per group: 8192 | Fragments per group: 8192
Inodes per group: 1904 | Inodes per group: 1904 | Inodes per group: 1904
Inode blocks per group: 238 | Inode blocks per group: 238 | Inode blocks per group: 238
Filesystem created: Sat Jan 5 22:12:20 2019 | Filesystem created: Sat Jan 5 22:26:43 2019 | Flex block group size: 16
Last mount time: Sat Jan 5 22:18:50 2019 | Last mount time: Sat Jan 5 22:28:41 2019 | Filesystem created: Sat Jan 5 22:27:39 2019
Last write time: Sat Jan 5 22:18:50 2019 | Last write time: Sat Jan 5 22:28:41 2019 | Last mount time: Sat Jan 5 22:28:55 2019
----------------------------------------------------------------------| ----------------------------------------------------------------------| Last write time: Sat Jan 5 22:28:55 2019
Mount count: 1 | Mount count: 1 | Mount count: 1
Maximum mount count: -1 | Maximum mount count: -1 | Maximum mount count: -1
Last checked: Sat Jan 5 22:12:20 2019 | Last checked: Sat Jan 5 22:26:43 2019 | Last checked: Sat Jan 5 22:27:39 2019
Check interval: 0 (<none>) | Check interval: 0 (<none>) | Check interval: 0 (<none>)
----------------------------------------------------------------------| ----------------------------------------------------------------------| Lifetime writes: 4395 kB
Reserved blocks uid: 0 (user root) | Reserved blocks uid: 0 (user root) | Reserved blocks uid: 0 (user root)
Reserved blocks gid: 0 (group root) | Reserved blocks gid: 0 (group root) | Reserved blocks gid: 0 (group root)
First inode: 11 | First inode: 11 | First inode: 11
Inode size: 128 | Inode size: 128 | Inode size: 128
----------------------------------------------------------------------| Journal inode: 8 | Journal inode: 8
Default directory hash: half_md4 | Default directory hash: half_md4 | Default directory hash: half_md4
Directory Hash Seed: a376209a-1a8d-438d-b0ef-5b3b9bac11bc | Directory Hash Seed: 0790fe74-370c-4990-8074-4700ef11392f | Directory Hash Seed: 1fc0824e-9698-4c36-b249-df16c4a84c44
----------------------------------------------------------------------| Journal backup: inode blocks | Journal backup: inode blocks
----------------------------------------------------------------------| Journal features: (none) | Journal features: (none)
----------------------------------------------------------------------| Journal size: 4113k | Journal size: 4096k
----------------------------------------------------------------------| Journal length: 4096 | Journal length: 4096
----------------------------------------------------------------------| Journal sequence: 0x00000002 | Journal sequence: 0x00000002
----------------------------------------------------------------------| Journal start: 1 | Journal start: 1
| |
| |
Group 0: (Blocks 1-8192) | Group 0: (Blocks 1-8192) | Group 0: (Blocks 1-8192) [ITABLE_ZEROED]
----------------------------------------------------------------------| ----------------------------------------------------------------------| Checksum 0xa8f3, unused inodes 1892
Primary superblock at 1, Group descriptors at 2-2 | Primary superblock at 1, Group descriptors at 2-2 | Primary superblock at 1, Group descriptors at 2-2
Reserved GDT blocks at 3-209 | Reserved GDT blocks at 3-209 | Reserved GDT blocks at 3-209
Block bitmap at 210 (+209), Inode bitmap at 211 (+210) | Block bitmap at 210 (+209), Inode bitmap at 211 (+210) | Block bitmap at 210 (+209), Inode bitmap at 217 (+216)
Inode table at 212-449 (+211) | Inode table at 212-449 (+211) | Inode table at 224-461 (+223)
7729 free blocks, 1892 free inodes, 2 directories | 7729 free blocks, 1892 free inodes, 2 directories | 6289 free blocks, 1892 free inodes, 2 directories, 1892 unused inode
Free blocks: 464-512, 514-8192 | Free blocks: 464-512, 514-8192 | Free blocks: 1904-8192
Free inodes: 13-1904 | Free inodes: 13-1904 | Free inodes: 13-1904
Group 1: (Blocks 8193-16384) | Group 1: (Blocks 8193-16384) | Group 1: (Blocks 8193-16384) [INODE_UNINIT, BLOCK_UNINIT, ITABLE_ZEROE
----------------------------------------------------------------------| ----------------------------------------------------------------------| Checksum 0xe654, unused inodes 1904
Backup superblock at 8193, Group descriptors at 8194-8194 | Backup superblock at 8193, Group descriptors at 8194-8194 | Backup superblock at 8193, Group descriptors at 8194-8194
Reserved GDT blocks at 8195-8401 | Reserved GDT blocks at 8195-8401 | Reserved GDT blocks at 8195-8401
Block bitmap at 8402 (+209), Inode bitmap at 8403 (+210) | Block bitmap at 8402 (+209), Inode bitmap at 8403 (+210) | Block bitmap at 211 (bg #0 + 210), Inode bitmap at 218 (bg #0 + 217)
Inode table at 8404-8641 (+211) | Inode table at 8404-8641 (+211) | Inode table at 462-699 (bg #0 + 461)
7743 free blocks, 1904 free inodes, 0 directories | 7743 free blocks, 1904 free inodes, 0 directories | 7983 free blocks, 1904 free inodes, 0 directories, 1904 unused inode
Free blocks: 8642-16384 | Free blocks: 8642-16384 | Free blocks: 8402-16384
Free inodes: 1905-3808 | Free inodes: 1905-3808 | Free inodes: 1905-3808
References
http://www.science.unitn.it/~fiorella/guidelinux/tlk/node95.html
http://e2fsprogs.sourceforge.net/ext2intro.html //设计文档,强烈推荐
https://access.redhat.com/solutions/978773
https://www.ibm.com/developerworks/cn/linux/l-cn-hardandsymb-links/