不知道你有沒有遇到過當一個文件夾下文件特別多,在下面執行ls
命令的時候要等好長時間才能展示出來的問題?若是有,你有想過這是爲何嗎,咱們該如何解決?
要想深刻理解這個的問題產生的緣由,咱們就須要從文件夾佔用的磁盤空間開始討論了。node
在《新建一個空文件佔用多少磁盤空間?》中我提到了每個文件會消耗其所在文件夾中的一點空間。文件夾呢,其實也同樣會消耗inode的。 咱們先看一下當前inode的佔用狀況linux
# df -i Filesystem Inodes IUsed IFree IUse% Mounted on ...... /dev/sdb1 2147361984 12785020 2134576964 1% /search
再建立一個空文件夾數組
# mkdir temp # df -i Filesystem Inodes IUsed IFree IUse% Mounted on ...... /dev/sdb1 2147361984 12785021 2134576963 1% /search
經過IUsed能夠看到,和空文件同樣,空的文件夾也會消耗掉一個inode。不過這個很小,個人機器上纔是256字節而已,應當不是形成ls
命令卡主的元兇。bash
文件夾的名字存在哪兒了呢?嗯,和《新建一個空文件佔用多少磁盤空間?》裏的文件相似,會消耗一個ext4_dir_entry_2
(今天用ext4舉例,它在linux源碼的fs/ext4/ex4.h文件裏定義),放到其父目錄的block裏了。根據這個,相信你也很快能想到,若是它本身節點下建立一堆文件的話,就會佔用它本身的block。咱們來動手驗證一下:服務器
# mkdir test # cd test # du -h 4.0K .
這裏的4KB就表示消耗掉了一個block。 空文件不消耗block,空目錄爲啥一開始就消耗block了呢,那是由於其必須默認帶兩個目錄項"."和".."。另外這個4K在你的機器上不必定是這麼大,它實際上是一個block size,在你格式化的時候決定的。操作系統
咱們再新建兩個空的文件,再查看一下:指針
# touch aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab # touch aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa # du -h 4.0K .
貌似,沒有什麼變化。這是由於code
那麼我再多建立一些試試,動用腳本建立100個文件名長度爲32Byte的空文件。對象
#!/bin/bash for((i=1;i<=100;i++)); do file="tempDir/"$(echo $i|awk '{printf("%032d",$0)}') echo $file touch $file done
# du -h 12K .
哈哈,這時咱們發現目錄佔用的磁盤空間變大了,成了3個Block了。當咱們建立到10000個文件的時候,開發
# du -h 548K .
在每個ext4_dir_entry_2
裏都除了文件名之外,還記錄着inode號等信息,詳細定義以下:
struct ext4_dir_entry_2 { __le32 inode; /* Inode number */ __le16 rec_len; /* Directory entry length */ __u8 name_len; /* Name length */ __u8 file_type; char name[EXT4_NAME_LEN]; /* File name */ };
咱們計算一下,平均每一個文件佔用的空間=548K/10000=54字節。也就是說,比咱們的文件名32字節大一點點,基本對上了。 這裏咱們也領會到一個事實,文件名越長,在其父目錄中消耗的空間也會越大。
一個文件夾固然也是要消耗磁盤空間的。
ext4_dir_entry_2
,保存本身inode號,目錄名。ext4_dir_entry_2
數組目錄下的文件/子目錄越多,目錄就須要申請越多的block。另外ext4_dir_entry_2
大小不是固定的,文件名/子目錄名越長,單個目錄項消耗的空間也就越大。
對於開篇的問題,我想你如今應該明白爲何了,問題出在文件夾的block身上。
這就是當你的文件夾下面文件特別多,尤爲是文件名也比較長的時候,它會消耗掉很是多的block。當你遍歷文件夾的時候,若是Page Cache中沒有命中你要訪問的block,就會穿透到磁盤上進行實際的IO。在你的角度來看,就是你執行完ls
後,卡住了。
那麼你確定會問,我確實要保存許許多多的文件,我該怎麼辦? 其實也很簡單,多建立一些文件夾就行了,一個目錄下別存太多,就不會有這個問題了。工程實踐中,通常的作法就是經過一級甚至是二級hash把文件散列到多個目錄中,把單目錄文件數量控制在十萬或萬如下。
貌似今天的實踐應該結束了,如今讓咱們把剛剛建立的文件所有刪掉,再看一下。
# rm -f * # du -h 72K .
等等,什麼狀況?文件夾下的文件都已經刪了,該文件夾爲何還佔用72K的磁盤空間?
這個疑惑也伴隨了我很長時間,後來纔算是解惑。問題關鍵在於ext4_dir_entry_2
中的rec_len
。這個變量存儲了當前整個ext4_dir_entry_2
對象的長度,這樣操做系統在遍歷文件夾的時候,就能夠經過當前的指針,加上這個長度就能夠找到文件夾中下一個文件的dir_entry
了。這樣的優點是遍歷起來很是方便,有點像是一個鏈表,一個一個穿起來的。
可是,若是要刪除一個文件的話,就有點小麻煩了,當前文件結構體變量不能直接刪,不然鏈表就斷了。
Linux的作法是在刪除文件的時候,在其目錄中只是把inode設置爲0就拉倒,並無回收整個ext4_dir_entry_2
對象。其實和你們作工程的時候常常用到的假刪除是一個道理。如今的xfs文件系統好像已經沒有這個小問題了,但具體咋解決的,暫時沒有深刻研究,若是你有答案,歡迎留言!
開發內功修煉之硬盤篇專輯:
個人公衆號是「開發內功修煉」,在這裏我不是單純介紹技術理論,也不僅介紹實踐經驗。而是把理論與實踐結合起來,用實踐加深對理論的理解、用理論提升你的技術實踐能力。歡迎你來關注個人公衆號,也請分享給你的好友~~~