Linux文件空洞与稀疏文件
From:http://www.topjishu.com/8277.html
From:http://blog.csdn.net/clamercoder/article/details/38361815
Linux_File_Hole_And_Sparse_Files
參考unix環境高級編程第三版 54頁和90頁!!! ( 文件I/O章節 ?lseek函數 。文件和目錄章節 文件長度)
文件空洞
在這種情況下,對該文件的下一次寫將延長該文件,并在文件中構成一個空洞。位于文件中但沒有寫過的字節
都被設為 0。
在文件里創造“空洞(hole)”。
沒有被實際寫入文件的所有字節由重復的 0 表示。空洞是否占用硬盤空間是由文件系統(file system)決定
的
?
稀疏文件(Sparse File)
空間。
下面是稀疏文件的創建與查看
Linux文件系統inode數據塊存儲
索引節點采用了多重索引結構,主要體現在直接指針和3個間接指針。直接指針包含12個直接指
針塊,它們直接指向包含文件數據的數據塊,緊接在后面的3個間接指針是為了適應文件的大小
變化而設計。
Linux稀疏文件inode數據塊存儲
文件系統存儲稀疏文件時,inode索引節點中,只給出實際占用磁盤空間的Block 號,
數據全零且不占用磁盤空間的文件Block并沒有物理磁盤Block號。
Linux稀疏文件inode數據塊存儲
- 文件空洞部分不占用磁盤空間
- 文件所占用的磁盤空間仍然是連續的
實例:
[root@localhost mnt]# du -sh sparse-file 20K sparse-file [root@localhost mnt]# ls -lh sparse-file -rw-r--r-- 1 root root 1.1G Oct 15 10:36 sparse-file [root@localhost mnt]# debugfs: stat sparse-file Inode: 49153 Type: regular Mode: 0644 Flags: 0x0 Generation: 3068382963 User: 0 Group: 0 Size: 1073742848 File ACL: 0 Directory ACL: 0 Links: 1 Blockcount: 40 Fragment: Address: 0 Number: 0 Size: 0 ctime: 0x507b76af -- Mon Oct 15 10:36:31 2012 atime: 0x507b765f -- Mon Oct 15 10:35:11 2012 mtime: 0x507b76af -- Mon Oct 15 10:36:31 2012 BLOCKS: (IND):106496, (256):106497, (DIND):106504, (IND):106505, (262144):106506 TOTAL: 5Linux文件系統數據塊存儲多重索引
- Linux文件系統數據存放采用inode多
重索引結構,有直接指針和3個間接指
針。
類似于編程中的變量定義:
unsigned long blk;
unsigned long *blk;
unsigned long **blk;
unsigned long ***blk; - 直接指針直接指向保存數據的Block
號。 - 一級指針指向一個Block,該Block中
的數據是Block指針,指向真正保存數
據的Block。
?二級三級指針以此類推。
如Blocks大小為4096,則前12個直接指針就可以保存48KB文件。
假設每個指針占用4個字節,則一級指針指向的Block可保存4096/4個
指針,可指向1024個Blocks。一級指針可存儲文件數據大小為1024*4096 =
4MB。
同樣按照Blocks大小為4096,則二級指針可保存的Block指針數量為(4096/4) *
(4096/4) = 1024*1024。則二級指針可保存的文件數量大小為(1024*1024)*4096
= 4GB。
以一級、二級指針計算方法類推,三級指針可存儲的文件數據大小為
(1024*1024*1024)*4096 = 4TB。
1.什么是空洞文件?
“在UNIX文件操作中,文件位移量可以大于文件的當前長度,在這種情況下,對該文件的下一次寫將延長該文件,并在文件中構成一個空洞,這一點是允許的。位于文件中但沒有寫過的字節都被設為 0。” ? ? ?--摘自“百度百科”
從上面的描述可以將空洞文件的特點表述為:offset > 實際文件大小。那這又有什么表現和意義呢?我們下面慢慢分析。
( http://blog.csdn.net/shenlanzifa/article/details/44016537?)
文件空洞
我們知道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 ? ? 查看文件存儲的內容 (od命令:http://blog.csdn.net/freeking101/article/details/78182731)
空洞文件就是有空洞的文件,在日常的常識中,我們使用的文件存放在硬盤分區上的時候,有多大的內容就會占用多大的空間,比如這個文本文件里面寫有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會逐個的去比較文件的內容,所以,受網絡狀況搞得影響,過程有時候會很慢.
2.怎么獲得一個空洞文件?
在linux下,利用lseek人為的修改offset可以獲得一個空洞文件。
[cpp]?view plain?copy
上面的程序創建了一個空洞文件和一個同樣大小的非空洞文件,接下來我們將以這兩個文件作為基礎進行空洞文件的分析。
3.空洞文件的表現
空洞文件特點就是offset大于實際大小,也就是說一個文件的兩頭有數據而中間為空,以‘\0‘填充。那文件系統會不會不做任何處理的將其存放在硬盤上呢?答案是否定的,文件系統沒有傻到這種程度,因為這實際是中浪費,也是一種威脅,因為一旦黑客利用這個漏洞不斷侵蝕磁盤資源,計算機就崩潰了。所以說,文件系統肯定會做相應的處理,下面我就來驗證一下。
用ls來展現兩個文件:
我們在用du來展現兩個文件(du命令用于報告文件所使用的磁盤空間總量):
可以看到,用ls展現的空洞和非空洞的大小完全相同,而用du命令展現的則有差別,一個占用了8個1024的字節塊,而一個占用了16個1024的字節塊。這里有個問題,文件大小為15014,算下來最多就15個block,為什么是16個呢?在《APUE》中有這樣的解釋:"文件系統使用了若干塊以存放指向實際數據塊的各個指針"。
為什么會這樣呢?原因是ls展現的文件的邏輯大小,也就是文件在文件系統表現出來的大小,而du展現的是文件物理大小,也就是文件在磁盤上實際所占的block數。所以說,空洞文件在文件系統表現的還是和普通文件一樣的,但是實際上文件系統并沒有給他分配所表現出來的那么多空間,只是存放了有用的信息。
接下倆我們再來看一個現象:
首先我們用cat來輸出空洞文件中內容,然后重定向到一個新的文件中:
我們再來用cp去復制一個文件:
可以看到,用cat得到的文件,文件實際占用的block增加了,而cp的沒有。那是因為cat在復制空洞文件時會將空洞補齊,將空洞填以0,因為cat命令就是簡單的read和write的操作,read在遇到空洞時讀出0,write則寫入0,這時文件就變成了非空洞文件,而cp在復制文件時不會,cp命令會去判斷文件是否有空洞,如果有,則會調用lseek進行空洞的模擬,所以還是會保持和源文件的一致性。
4.空洞文件有什么用?
空洞文件作用很大,例如迅雷下載文件,在未下載完成時就已經占據了全部文件大小的空間,這時候就是空洞文件。下載時如果沒有空洞文件,多線程下載時文件就都只能從一個地方寫入,這就不是多線程了。如果有了空洞文件,可以從不同的地址寫入,就完成了多線程的優勢任務。
感覺這并不是空洞文件的全部作用,后續將進行補充。。。。
總結
以上是生活随笔為你收集整理的Linux文件空洞与稀疏文件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TCP/IP校验和(浅析+实例)
- 下一篇: Linux 源码安装 Python3 和