1. 程式人生 > >學習筆記:linux之檔案空洞

學習筆記:linux之檔案空洞

檔案空洞

我們知道lseek()系統呼叫可以改變檔案的偏移量,但如果程式呼叫使得檔案偏移量跨越了檔案結尾,然後再執行I/O操作,將會發生什麼情況? read()呼叫將會返回0,表示檔案結尾。令人驚訝的是,write()函式可以在檔案結尾後的任意位置寫入資料。在這種情況下,對該檔案的下一次寫將延長該檔案,並在檔案中構成一個空洞,這一點是允許的。從原來的檔案結尾到新寫入資料間的這段空間被成為檔案空洞。呼叫write後文件結尾的位置已經發生變化。

在Linux系統之中,檔案結束符EOF根本不是一個字元,而是當系統讀取到檔案結尾,所返回的一個訊號值(也就是-1),至於系統怎麼知道檔案的結尾,資料上說是通過比較檔案的長度。

檔案空洞佔用任何磁碟空間,直到後續某個時點,在檔案空洞中寫入了資料,檔案系統才會為之分配磁碟塊。空洞的存在意味著一個檔名義上的大小可能要比其佔用的磁碟儲存總量要大(有時大出許多)。向檔案空洞中寫入位元組,核心需要為其分配儲存單元,即使檔案大小不變,系統的可用磁碟空間也將減少。這種情況並不常見,但也需要了解。

下面看一個例子:(轉自http://blog.csdn.net/wangxiaoqin00007/article/details/6617801)

ls -l file        檢視檔案邏輯大小

du -c file     檢視檔案實際佔用的儲存塊多少

od -c file     檢視檔案儲存的內容

空洞檔案就是有空洞的檔案,在日常的常識中,我們使用的檔案存放在硬碟分割槽上的時候,有多大的內容就會佔用多大的空間,比如這個文字檔案裡面寫有1000個asc字元,那麼就會佔用磁碟上1000B的儲存空間,為了便於管理檔案,檔案系統都是按塊大小來分配給檔案的,假如這個檔案系統一個塊是4096的話,那麼這個檔案就會佔用一個塊的,無論實際的內容是1B還是4000B.如果我們有一個4MB的檔案,那麼它會在分割槽中佔用:4MB/4096B=1000個塊.

現在我們先做一個實際的無空洞檔案來看看:

#dd if=/dev/urandom of=testfile1 bs=4096 count=1000

這個命令會從/dev/urandom檔案複製1000個塊,每塊大小4096,到testfile1檔案去.

好了,我們已經有了testfile1這麼一個4M的檔案了,裡面填充了一些隨機的內容,你可以more一下.

然後用ls -l檢視這個檔案的大小是4096000,用du -h testfile1來檢視的話,檔案佔用的磁碟大小是4M,兩者是一樣的.

下來是我們的重點,空洞檔案,假如我們有一個檔案,它有4M的大小,但是它裡邊很大一部分都是沒有存放資料的,這樣可不可以呢?試一下:

#dd if=/dev/urandom of=testfile2 bs=4096 seek=999 count=1

這個命令跟前一個命令相似,不同的是,它其實複製了1個塊的內容,前面的999個塊都跳過了.

我們ls -l一下,發現檔案的大小還是4096000,用du -h testfile2檢視,佔用的塊大小是4K

我們發現,雖然檔案是4M,但是實際在磁碟上只佔用了4K的大小,這就是空洞檔案的神奇之處.

實際中的空洞檔案會在哪裡用到呢?常見的場景有兩個:

一是在下載電影的時候,發現剛開始下載,檔案的大小就已經到幾百M了.

二是在建立虛擬機器的磁碟映象的時候,你建立了一個100G的磁碟映象,但是其實裝起來系統之後,開始也不過只佔用了3,4G的磁碟空間,如果一開始把100G都分配出去的話,無疑是很大的浪費.

然後講一下底層的實現吧,其實這個功能關鍵得檔案系統支援,貌似FAT就不可以吧,linux下一直都很好的支援這一特性,我們舉個最簡單的ext的例子吧,ext中記錄檔案實際內容的對應資訊的東東是一個叫索引表的東西,裡面有十幾個條目,每個條目存放對應檔案內容塊的塊號,這樣就可以順序找到對應的檔案內容了,大家可能說,幾M的一個檔案,十幾個項哪夠啊,不必擔心,一般索引表前面幾個專案是直接指向檔案內容的,如果這幾個不夠的話,往後的第一個專案不會指向檔案內容塊,而會指向一個存放專案的塊,這樣一下多出N個專案來,如果這樣還不夠,下面的那個是存放指向指向的專案,不好意思,我也繞暈了,總之,前面的是直接指向,下面這個是二級指向,再下面的是二級指向,以此類推,這樣,檔案系統就可以處理T數量級別的檔案,看下圖:

到了空洞檔案這裡呢,我們只需要把指向沒有檔案內容部分的索引專案置NULL就好了,這樣就不會指向實際的資料塊了,也不會佔用磁碟空間了,就這麼easy~

至於btrfs這些新一代檔案系統呢,在空洞檔案這裡的原理跟ext還是類似的.

最後介紹一下linux對空洞檔案的處理,經過我最近的一些測試所得:

在同一檔案系統ext4下,cat一個空洞檔案到新檔案,新檔案不再是空洞檔案,cp一個空洞檔案到新檔案,新檔案仍然是空洞檔案.

在btrfs跟ext4之間做的結果同上面是一致的,但是在不同檔案系統之間cp,因為不同檔案系統分配的最小單元不同,所以du結果會不同.

在nfs的客戶端下,在nfs目錄下去cp,新檔案仍然是空洞檔案!!!但是cp會逐個的去比較檔案的內容,所以,受網路狀況搞得影響,過程有時候會很慢.