存儲(chǔ)基礎(chǔ) | 神奇!我的文件有個(gè)“洞”
本文轉(zhuǎn)載自微信公眾號(hào)「奇伢云存儲(chǔ)」,作者奇伢 。轉(zhuǎn)載本文請(qǐng)聯(lián)系奇伢云存儲(chǔ)公眾號(hào)。
聊聊背景
文件還能打洞?
支持稀疏文件語義的文件系統(tǒng)就可以。
支持稀疏語義的文件系統(tǒng)有什么基本特征?
- 實(shí)現(xiàn) fallocate 接口,能夠滿足文件空間預(yù)分配和打洞;
- 實(shí)現(xiàn) fiemap 的功能,返回文件的具體物理塊分配信息;
打洞是什么意思?
英文是“punch hole”,就是在保證文件其他屬性不變(比如,文件大小,inode 編號(hào),權(quán)限等等)的條件下,主動(dòng)釋放一段文件所占的物理空間。
關(guān)于承諾的語義?
文件系統(tǒng):punch hole 成功,文件系統(tǒng)可能釋放,也可能沒釋放這部分空間,此結(jié)果不對(duì)用戶承諾。
程序猿:反而是程序猿要遵守承諾,一旦 puhch hole 成功,用戶將不能對(duì)這部分?jǐn)?shù)據(jù)做任何假設(shè),要當(dāng)它已經(jīng)沒了,無論它是不是真的沒了。
創(chuàng)建實(shí)分配的文件
為了打洞,我們需要先創(chuàng)建一個(gè)實(shí)際占用 4M 的文件,用 dd 命令如下:
- root@ubuntu:~/temp# dd if=/dev/urandom of=./test.txt.4M bs=1M count=4
可以用 du 命令看一下實(shí)際的物理空間:
- root@ubuntu:~/temp# du -sh ./test.txt.4M
- 4.0M ./test.txt.4M
確實(shí)是 4M,再用 stat 命令看一下:
- root@ubuntu:~/temp# stat ./test.txt.4M
- File: './test.txt.4M'
- Size: 4194304 Blocks: 8192 IO Block: 4096 regular file
- Device: fc00h/64512d Inode: 1335860 Links: 1
- Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
文件 Size 4194304 字節(jié),物理占用 Blocks 數(shù)是 8192,這里每個(gè) Block 單位是 512 字節(jié),所以物理占用也是 4194304 字節(jié),剛好 4M。
文件打個(gè)洞
原材料準(zhǔn)備好了,Go 程序怎么給文件打個(gè)洞呢 ?
關(guān)鍵在于 fallocate 系統(tǒng)調(diào)用。
這是一個(gè)跟平臺(tái)強(qiáng)相關(guān)的系統(tǒng)調(diào)用,非系統(tǒng)兼容的,下面以 Linux 為例。
由于這個(gè)非系統(tǒng)兼容的,類似于這類調(diào)用,一般都是用 syscall 這個(gè)標(biāo)準(zhǔn)庫,直接下發(fā)系統(tǒng)調(diào)用。
完整程序示例如下:
代碼關(guān)鍵幾個(gè)事項(xiàng):
- 文件頭部要加上 // +build linux ;
- 調(diào)用的是 syscall.Fallocate 接口;
好了,編譯一下吧:
- go build -gcflags "-N -l" ./punchhole.go
把編譯出的二進(jìn)制 punchhole 和 test.txt.4M 這兩個(gè)放在一個(gè)目錄下,實(shí)驗(yàn)一下效果:
- root@ubuntu:~/temp# ./punchhole
- 2021/09/08 22:22:21 punch hole success.
du 看下文件結(jié)果:
- root@ubuntu:~/temp# du -sh ./test.txt.4M
- 2.0M ./test.txt.4M
嗷,確實(shí)變成了 2M,stat 再看一下:
- root@ubuntu:~/temp# stat ./test.txt.4M
- File: './test.txt.4M'
- Size: 4194304 Blocks: 4096 IO Block: 4096 regular file
- Device: fc00h/64512d Inode: 1335860 Links: 1
- Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
- Access: 2021-07-26 15:39:06.672000000 +0800
文件大小還是 4M,實(shí)際物理空間變成了 2M( 4096 * 512 ),inode 編號(hào)、權(quán)限都沒變。完美,一個(gè)空洞文件就誕生了。
文件解析:
這個(gè)文件 [ 0,2M ] 的位置是空洞,不占物理空間,讀出來會(huì)是 0 數(shù)據(jù);
[ 2M,4M ] 的數(shù)據(jù)還是原來從 /dev/urandom 設(shè)備讀出來的數(shù)據(jù),占用實(shí)際物理空間;
思考題
拋出幾個(gè)關(guān)鍵的思考問題,大家可以自行驗(yàn)證。
如果 punchhole 傳參是非 4k 對(duì)齊的,會(huì)怎么樣?
劃重點(diǎn):由于文件系統(tǒng)內(nèi)部都是按照 4k 的單位管理空間的,所以非 4k 對(duì)齊的空間是釋放不掉的。 punch hole 一定要注意按照 4k 對(duì)齊。
特別還要注意一點(diǎn),雖然非 4k 對(duì)齊釋放不掉,但是 fallocate 調(diào)用也不會(huì)報(bào)錯(cuò),這點(diǎn)很重要。最開始就提過,文件系統(tǒng)沒給你承諾過啥時(shí)候釋放啥。
大家可以手動(dòng)驗(yàn)證下。
文件 test.txt.4M 的 [ 0,2M ] 被打洞之后,這個(gè)區(qū)域會(huì)是什么數(shù)據(jù)?
**全 0 數(shù)據(jù),這個(gè)是稀疏文件系統(tǒng)給你的語義。**這個(gè)上面也提到過了。
奇伢教你快速用 hexdump 命令看一下:
- root@ubuntu:~/temp# hexdump ./test.txt.4M|more
- 0000000 0000 0000 0000 0000 0000 0000 0000 0000
- *
- 0200000 80e3 2c11 f8d8 256b 23b5 a191 fb80 eb5e
- 0200010 f454 e3e2 cb8b 664a a893 6f5a 2df0 99dd
- 0200020 9d30 4f19 144f b4f1 f2cd 7312 c16c 719f
- 0200030 2ef7 3195 48a1 b2c0 03f1 a08a aff3 a022
- .................
- .................
看到了嗎?
0x0000000 - 0x0200000 這個(gè)區(qū)域都是 0 數(shù)據(jù)。這是 16 進(jìn)制表示,換算成 10 進(jìn)制,就是 [ 0 ,2M ] 的區(qū)域。
大家也可以用程序去 read 驗(yàn)證下。
總結(jié)
總結(jié)幾個(gè)關(guān)鍵點(diǎn):
文件打洞用的是系統(tǒng)調(diào)用 fallocate ;
文件打洞的時(shí)候要注意 4k 對(duì)齊,不然非對(duì)齊部分釋放不掉,并且不會(huì)報(bào)錯(cuò);
文件系統(tǒng)沒承諾什么,所以當(dāng)沒 4k 對(duì)齊的時(shí)候,雖然沒釋放空間,也不會(huì)報(bào)錯(cuò);
程序猿要遵守承諾,一旦聲明了某段空間要釋放,以后不能對(duì)此空間內(nèi)容做假設(shè);
文件打洞的位置,不占物理空間,后續(xù)讀是返回 0 數(shù)據(jù);


































