本文翻譯自Large Directory Causes ls to Hang
你一定遇到過這種情況,在一個有幾百萬文件的目錄中執(zhí)行l(wèi)s命令贰剥,ls就卡在那了,是吧?
用ls -1 -f命令可以立即顯示出文件脆荷。如果你想刪除當(dāng)前目錄中的所有文件,使用如下命令:
ls -1 -f | xargs rm
在清理大量不需要的文件后懊悯,會留下一個巨大稀疏的目錄對象(directory object)蜓谋。假如一個目錄有300萬個文件,除了這些文件占用空間外炭分,目錄對象本身也會占用超過100M的空間桃焕。
你也許想重建一個目錄來回收那100M空間。但是捧毛,如果目錄是/tmp观堂,那就要小心了,只能在單用戶模式下操作呀忧。
ls命令為什么會卡资邸?
默認(rèn)情況下而账,ls命令會將輸出排序胰坟。為了排序,ls命令先將所有文件的名稱讀入內(nèi)存福扬。當(dāng)遇到一個非常大的目錄時腕铸,它就在那里不斷地讀入文件名,并且內(nèi)存占用越來越大铛碑,直到將所有文件一次性以字母數(shù)字順序列出來狠裹。
而ls -1 -f命令并不執(zhí)行排序操作,只是讀取目錄然后立即顯示文件汽烦。
下面舉個例子涛菠,有個目錄,包含300萬個文件,文件名稱形如test_file_a_1, test_file_a_2, ..., test_file_a_3000000. 用Perl腳本以文件名中的數(shù)字編號順序來創(chuàng)建這些文件俗冻。
可以用ls -1 -f命令立即列出頭幾個文件:
bash-4.2$ time ls -1 -f | head
.
..
test_file_a_2531963
test_file_a_467778
test_file_a_2677947
test_file_a_329896
test_file_a_835701
test_file_a_1266060
test_file_a_261887
test_file_a_311007
real 0m0.006s
user 0m0.000s
sys 0m0.008s
現(xiàn)在礁叔,去掉上面命令中的-1和-f標(biāo)志,ls命令花了大約10000倍長的時間:
bash-4.2$ time /bin/ls | head
test_file_a_1
test_file_a_10
test_file_a_100
test_file_a_1000
test_file_a_10000
test_file_a_100000
test_file_a_1000000
test_file_a_1000001
test_file_a_1000002
test_file_a_1000003
real 0m57.880s
user 0m55.644s
sys 0m2.121s
除了變得更慢外迄薄,后者還占用了大量內(nèi)存琅关。當(dāng)ls命令真正開始打印文件名的時候,它已經(jīng)在內(nèi)存中存儲了300萬個文件名讥蔽,使用了大約507M內(nèi)存涣易。相反,ls -1 -f命令內(nèi)存占用從未超過4.5M冶伞,少了100倍新症。
EXT3/EXT4文件系統(tǒng)的Bug?
遍歷EXT3/4文件系統(tǒng)花這么長時間和這么多資源有時被認(rèn)為是文件系統(tǒng)Bug响禽。但是我個人認(rèn)為徒爹,如果有Bug的話,那只能是遍歷軟件的Bug(比如芋类,find命令隆嗅、不帶-1和-f標(biāo)識的ls命令以及各種備份軟件),而不是文件系統(tǒng)的Bug梗肝。
注意順序
在上面的測試中還有一點(diǎn)蹊蹺之處榛瓮。ls命令將文件名按照字母數(shù)字順序排好了序,但是ls -1 -f命令的輸出是以什么順序呢巫击?看起來比較隨機(jī)禀晓。頭三個文件是test_file_a_2531963、test_file_a_467778和test_file_a_2677947坝锰。這個次序與文件創(chuàng)建時間粹懒、修改時間、inode編號或者其它順序都不符合顷级。那到底是怎么回事凫乖?
測試使用的是EXT4文件系統(tǒng)。EXT4和EXT3文件系統(tǒng)使用一種叫做HTree的哈希算法來存儲文件名弓颈。當(dāng)讀取目錄時帽芽,文件名是以任意順序返回的。因此翔冀,ls -1 -f的輸出結(jié)果看起來混在一起不是有序的导街。
EXT2文件系統(tǒng)的行為有時候像本文中所說的EXT3和EXT4一樣(比如在Fedora 16系統(tǒng)上),有時又不是(比如在Red Hat 4系統(tǒng)上)纤子,而是按照文件創(chuàng)建時間返回文件搬瑰。Solaris UFS文件系統(tǒng)也一樣款票。
進(jìn)程卡住泽论?看看它在干什么
當(dāng)ls -1 -f命令不可用時艾少,可以用strace(Linux系統(tǒng))或者truss(Solaris系統(tǒng))命令來監(jiān)視進(jìn)程來發(fā)現(xiàn)有用信息。在我同事Neil Dixon使用strace來監(jiān)視ls進(jìn)程時翼悴,我才發(fā)現(xiàn)這一巧妙方法缚够。在終端中執(zhí)行:
cd verybigdir
ls -l > /dev/null
然后獲取ls進(jìn)程的PID,在另一個終端中監(jiān)視它抄瓦,就可以看到ls正在獲取每個文件的元信息:
[root@saturn ~]# strace -p 3963 2>&1 | grep lstat
lstat64(“test_file_a_2433028″, {st_mode=S_IFREG|0664, st_size=0, …}) = 0
lstat64(“test_file_a_2047256″, {st_mode=S_IFREG|0664, st_size=0, …}) = 0
lstat64(“test_file_a_1201573″, {st_mode=S_IFREG|0664, st_size=0, …}) = 0
這樣就能立即看到文件名了潮瓶。如果你想刪掉它們,將上述strace的輸出重定向到AWK處理钙姊。
我猜同樣的方法可以用于監(jiān)視其它進(jìn)程。有人用這個方法查看過備份進(jìn)程埂伦,或者大型find/cpio任務(wù)煞额,甚至cp的進(jìn)展程度嗎?