1 掉电与文件系统一致性
由上一节文件系统的布局分析可知,当操作一个文件时,比如往 / a 目录下添加一个 b,即添加 /a/ b 文件,需要修改 inode bitmap, inode table, block bitmap, data block。
这一系列的操作是非原子的,假如任何一个环节掉电,造成某些步骤丢失,就会造成数据的不完整,文件将无法正常访问。
2 append 一个文件的全流程
而硬件是不可能原子执行的,因此会造成不一致性。
3 模拟文件系统不一致性案例
(1) 做一个 image,用来模拟磁盘
dd if=/dev/zero of=image bs=1024 count=4096
(2). 格式化为 ext4 文件系统
mkfs.ext4 -b 4096 image
(3).mount 到 test 目录,写入一个 ok.txt 文件
sudo mount -o loop image test/
cd test/
sudo touch ok.txt
cd ..
sudo umount test
(4). 查看磁盘详细信息
(base) leon\@pc:\~/io\$ dumpe2fs image
dumpe2fs 1.42.13 (17-May-2015)
Filesystem volume name: <none>
Last mounted on: /home/leon/io/test
Filesystem UUID: 759835e3-9508-4c57-b511-9c4f7e13f0ad
Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic)
…
Inode count: 1024
Block count: 1024
Reserved block count: 51
Free blocks: 982
Free inodes: 1012
First block: 0
Block size: 4096
Fragment size: 4096
Blocks per group: 32768
Fragments per group: 32768
Inodes per group: 1024
…
First inode: 11
Inode size: 128
Default directory hash: half_md4
Directory Hash Seed: 9cf91d57-8528-4d39-b6ba-5f8e2e86fcb7
Group 0: (Blocks 0-1023) [ITABLE_ZEROED]
Checksum 0x240a, unused inodes 1012
主 superblock at 0, Group descriptors at 1-1
Block bitmap at 2 (+2), Inode bitmap at 18 (+18)
Inode 表位于 34-65 (+34)
982 free blocks, 1012 free inodes, 2 directories, 1012 个未使用的 inodes
可用块数: 8-17, 19-33, 67-1023
可用 inode 数: 13-1024
(base) leon\@pc:\~/io\$
可以看到 inode bitmap 在 18 个块。
(5). 查看 inodebitmap 块
dd if=image bs=4096 skip=18 | hexdump -C -n 32
由于 ext4 默认用掉 11 个 inode,新创建的 ok.txt 文件后,inode bitmap 用掉 12 位。
(6). 现在模拟掉电,修改 inode bitmap
vim -b image
:%!xxd –g 1
:%!xxd –r
找到 inode bitmap 块对应地址 4096*18=0x12000
将 bitmap 改为 ff 07
(7). 重新 mount 访问,查看出错信息
(base) leon@pc:~/io/test$ sudo touch bad.txt
touch: 无法创建 'bad.txt': 输入 / 输出错误
(base) leon@pc:~/io/test$ dmesg
掉电导致的不一致性,会出现各种奇怪的问题,甚至都无法修复;
任何软件的手段只能保持一致性,无法保证不丢失数据。
4 fsck
人为破坏 data block,用 fsck 修复
修复原理,扫描 bitmap 和 inode table 的一致性。
早期 Linux/Windows 系统异常掉电后启动,都用 fsck 修复磁盘,速度很慢。
为提高速度,新系统都采用日志系统方式。
5 文件系统的日志
将要修改的行为,记录为一个日志,若操作磁盘过程掉电,开机根据日志回放,将磁盘操作全部重做一遍。磁盘操作完成,删除日志。
优点:保持文件系统的一致性,也提高速度。
EXT2/3/ 4 都采用日志系统。
日志的几个阶段:
1. 开始写日志
2. 日志区写完日志,commited;
3. 执行完一条日志,磁盘操作完成,checkpoint。
4. 操作完成,free 日志。
完整的日志方式,相当于每个数据都写了两遍,让系统变很慢,实际工程上会根据数据情况,做部分日志,即日志方式分为三种:速度递增,安全性递减
data=journal: 完整日志;
data = ordered: 只写元数据,且先写完数据块,再写元数据
data=writeback 只写元数据,循序不确定;ubuntu 默认方式;
这样日志就分为 5 个阶段:
6 文件系统的调试工具
创建一个文件 t.txt,df –h
(base) leon@pc:~/io$ **sudo debugfs -R 'stat /home/leon/io/t.txt' /dev/sda7**
sudo: 无法解析主机:pc: 连接超时
debugfs 1.42.13 (17-May-2015)
Inode: 13896517 Type: regular Mode: 0664 Flags: 0x80000
Generation: 507799365 Version: 0x00000000:00000001
User: 1000 Group: 1000 Size: 17
File ACL: 0 Directory ACL: 0
Links: 1 Blockcount: 8
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x5f40e439:2182bba0 -- Sat Aug 22 17:24:09 2020
atime: 0x5f40e43b:c9589800 -- Sat Aug 22 17:24:11 2020
mtime: 0x5f40e439:2182bba0 -- Sat Aug 22 17:24:09 2020
crtime: 0x5f40e439:208e98b8 -- Sat Aug 22 17:24:09 2020
Size of extra inode fields: 32
EXTENTS:
(0):55636171
(END)
得到文件的数据块 55636171
查看数据块内容
sudo **blkcat** /dev/sda7 55636171
sudo dd if=/dev/sda of=1 skip=$((55636171*8+824123392)) bs=512c count=1
debugfs 根据块号查 inode 号
sudo debugfs -R 'icheck 55636171' /dev/sda7
根据 inode 号,查文件路径
sudo debugfs -R 'ncheck 13896517' /dev/sda7
7 Copy On Write 文件系统: btrfs
不用日志,实现文件系统一致性。每次写磁盘时,先将更新数据写入一个新的 block,当新数据写入成功之后,再更新相关的数据结构指向新 block。
COW 稳健性系统的实现方式,有利于实现子卷和 snapshot,类似 git 的思想: