哦哇資訊網

一個 100G檔案 0.2 秒複製完?背後到底是誰的功勞?

由 Linux資訊速推 發表于 家居2021-12-06

cp 引發的思考

今天同事用  命令,把他給驚到了!背景是這樣的:他用   複製了一個 100 G的檔案,竟然一秒不到就複製完成了!

用   看一把檔案,顯示檔案確實是 100 G。

但是copy起來為什麼會這麼快呢?

一個 SATA 機械盤的寫能力能到 150 M/s (大部分的機械盤都是到不了這個值的)就算非常不錯了,正常情況下,copy 一個 100G 的檔案至少要 682 秒 ( 100 G/ 150 M/s ),也就是 11 分鐘。

實際情況卻是  一秒沒到就完成了工作,驚呆了,為啥呢?

更詭異的是:他的檔案系統只有 40 G,為啥裡面會有一個 100 G的檔案呢?

同事把我找來,看看這個詭異的問題。

分析檔案

我讓他先用  命令看一下,卻只有 2M ,根本不是100G,這是怎麼回事?

再看  命令顯示的資訊:

命令輸出解釋:

Size 為 107374182400(知識點:單位是位元組),也就是 100G ;

Blocks 這個指標顯示為 4096(知識點:一個 Block 的單位固定是 512 位元組,也就是一個扇區的大小),這裡表示為 2M;

劃重點:

Size 表示的是檔案大小,這個也是大多數人看到的大小;

Blocks 表示的是物理實際佔用空間;

同事問道:“

檔案大小和實際物理佔用,這兩個竟然不是相同的概念 !為什麼是這樣?

“看來,我們必須得深入檔案系統才能理解了,來,我給你好好講講。”

檔案系統

檔案系統聽起來很高大上,通俗話就用來存資料的一個容器而已,本質和你的行李箱、倉庫沒有啥區別,只不過檔案系統儲存的是數字產品而已。

我有一個影片檔案,我把這個影片放到這個檔案系統裡,下次來拿,要能拿到我完整的影片檔案資料,這就是檔案系統,對外提供的就是存取服務。

現實的存取場景

例如你到火車站使用寄存服務:

存行李的時候

,是不是要登記一些個人資訊?對吧,至少自己名字要寫上。可能還會給你一個牌子,讓你掛手上,這個東西就是為了標示每一個唯一的行李。

取行李的時候

,要報自己名字,有牌子的給他牌子,然後工作人員才能去特定的位置找到你的行李

劃重點:存的時候必須記錄一些關鍵資訊(記錄ID、給身份牌),取的時候才能正確定位到。

檔案系統

回到我們的檔案系統,對比上面的行李存取行為,可以做個簡單的類比;

登記名字就是在檔案系統記錄檔名;

生成的牌子就是元資料索引;

你的行李就是檔案;

寄存室就是磁碟(容納東西的物理空間);

管理員整套執行機制就是檔案系統;

上面的對應並不是非常嚴謹,僅僅是幫助大家理解檔案系統而已,讓大家知道其實檔案系統是非常樸實的一個東西,思想都來源於生活。

空間管理

現在思考檔案系統是怎麼管理空間的?

如果,一個連續的大磁碟空間給你使用,你會怎麼使用這段空間呢?

直觀的一個想法,我把進來的資料就完整的放進去。

這種方式非常容易實現,屬於眼前最簡單,以後最麻煩的方式。因為會造成很多空洞,明明還有很多空間位置,但是由於整個太大,形狀不合適(資料大小),哪裡都放不下。因為你要放一個完整的空間。

怎麼改進?有人會想,既然整個放不進去,那就剁碎了唄。這裡塞一點,那裡塞一點,就塞進去了。

對,思路完全正確。

改進的方式就是切分,把空間按照一定粒度切分

。每個小粒度的物理塊命名為 Block,每個 Block 一般是 4K 大小,使用者資料存到檔案系統裡來自然也是要切分,儲存到磁碟上各個角落。

圖示標號表示這個完整物件的 Block 的序號,用來複原物件用的。

隨之而來又有一個問題:你光會切成塊還不行,取檔案資料的時候,還得把它們給組合起來才行。

所以,要有一個表記錄檔案對應所有 Block 的位置,這個表被檔案系統稱為inode。

寫檔案的流程是這樣的:

先寫資料:資料先按照 Block 粒度儲存到磁碟的各個位置;

再寫元資料:然後把 Block 所在的各個位置儲存起來,即inode(我用一本書來表示);

讀檔案流程則是:

先讀inode,找到各個 Block 的位置;

然後讀資料,構造一個完整的檔案,給到使用者;

一個 100G檔案 0.2 秒複製完?背後到底是誰的功勞?

inode/block 概念

好,我們現在來看看inode,直觀地感受一下:

一個 100G檔案 0.2 秒複製完?背後到底是誰的功勞?

這個inode有檔案元資料和Block陣列(長度是15),陣列中前兩項指向Block 3和Block 11,表示資料在這兩個塊中存著。

你肯定會意識到:Block陣列只有15個元素,每個Block是4K, 難道一個檔案最大隻能是 15 * 4K =  60 K ?

這是絕對不行的!

最簡單的辦法就是:把這個Block陣列長度給擴大!

比如我們想讓檔案系統最大支援100G的檔案,Block陣列需要這麼長:

Block陣列中每一項是4個位元組,那就需要(26214400*4)/1024/1024 =

100M

為了支援100G的檔案,我們的Block陣列本身就得100M !

並且對每個檔案都是如此 !即使這個檔案只有1K!

這將是巨大浪費!

肯定不能這麼幹,解決方案就是間接索引,按照約定,把這 15 個槽位分作 4 個不同類別來用:

前 12 個槽位(也就是 0 - 11 )我們成為

直接索引;

第 13 個位置,我們稱為 1 級索引;

第 14 個位置,我們稱為 2 級索引;

第 15 個位置,我們稱為 3 級索引;

一個 100G檔案 0.2 秒複製完?背後到底是誰的功勞?

直接索引:能存 12 個 block 編號,每個 block 4K,就是 ,也就是說,48K 以內的檔案,前 12 個槽位儲存編號就能完全 hold 住。

一級索引:

也就是說這裡儲存的編號指向的 block 裡面儲存的也是 block 編號,裡面的編號指向使用者資料。一個 block  4K,每個元素 4 位元組,也就是有 1024 個編號位置可以儲存。所以,一級索引能定址 空間 。

一個 100G檔案 0.2 秒複製完?背後到底是誰的功勞?

二級索引:

二級索引是在一級索引的基礎上多了一級而已,換算下來,有了 4M 的空間用來儲存使用者資料的編號。所以二級索引能定址  的空間。

一個 100G檔案 0.2 秒複製完?背後到底是誰的功勞?

三級索引:

三級索引是在二級索引的基礎上又多了一級,也就是說,有了 4G 的空間來儲存使用者資料的 block 編號。所以二級索引能定址 4T (4G/4 * 4K) 的空間。

一個 100G檔案 0.2 秒複製完?背後到底是誰的功勞?

所以,在這種檔案系統(如ext2)上,透過這種間接塊索引的方式,最大能支撐的檔案大小 = 48K + 4M + 4G + 4T ,約等於 4 T。

這種多級索引定址效能表現怎麼樣?

在不超過 12 個數據塊的小檔案的定址是最快的,訪問檔案中的任意資料理論只需要兩次讀盤,一次讀 inode,一次讀資料塊。訪問大檔案中的資料則需要最多五次讀盤操作:inode、一級間接定址塊、二級間接定址塊、三級間接定址塊、資料塊。

為什麼cp那麼快?

接下來我們要寫入一個奇怪的檔案,這個檔案很大,但是真正的資料只有8K:

在[0,4K]這位置有4K的資料

在[1T , 1T+4K] 處也有4K資料

中間沒有資料,這樣的檔案該如何寫入硬碟?

建立一個檔案,這個時候分配一個 inode;

在 [ 0,4K ] 的位置寫入 4K 資料,這個時候只需要 一個 block,把這個編號寫到  這個位置儲存起來;

在 [ 1T,1T+4K ] 的位置寫入 4K 資料,這個時候需要分配一個 block,因為這個位置已經落到三級索引才能表現的空間了,所以需要還需要分配出 3 個索引塊;

寫入完成,close 檔案;

實際儲存如圖:

一個 100G檔案 0.2 秒複製完?背後到底是誰的功勞?

這個時候,我們的檔案看起來是超大檔案,size 等於 1T+4K ,但裡面實際的資料只有 8 K,位置分別是  [ 0,4K ] ,[ 1T,1T+4K ]。

由於沒寫資料的地方不用分配物理block塊,所以實際佔用的物理空間只有8K。

重點:檔案 size 只是 inode 裡面的一個屬性,實際物理空間佔用則是要看使用者資料放了多少個 block ,沒寫資料的地方不用分配物理block塊。

這樣的檔案其實就是稀疏檔案, 它的邏輯大小和實際物理空間是不相等的。

所以當我們用cp命令去複製一個這樣的檔案時,那肯定迅速就完成了。

總結

好,我們再深入思考下,檔案系統為什麼能做到這一點?

首先,最關鍵的是把磁碟空間切成離散的、定長的 block 來管理;

然後,透過 inode 能查詢到所有離散的資料(儲存了所有的索引);

最後,實現索引塊和資料塊空間的後分配;

這三點是層層遞進的。

後記

我把這點小知識給小夥伴講了一小時,看到他感動欲哭的表情,我覺得他學fei了,非常滿意。

是我想太多了嗎?中午吃飯都沒叫我。

END

TAG: 索引BLOCK4K檔案檔案系統