[原創(chuàng)文章,轉(zhuǎn)載請注明出處]
前兩天在調(diào)查一個"too many open files"的問題,和之前一樣,自然而然的用到了lsof,加上一堆漂亮的命令組合來查看哪些程序打開了很多文件。
lsof | awk '{print $2}' | uniq -c | sort -rnk1 | head
啪的一敲回車,很順利的定位到幾個docker內(nèi)運行的Java進程排在最前,分別都是幾萬的結(jié)果,懷疑是不是docker的問題。很不幸的是,這個調(diào)查結(jié)果是錯的。。。
先說真實的情況,后面再展開分析:
- 真實的元兇,是一個并沒有在上面的命令結(jié)果中排在最前面的進程,由于編程的bug,不斷的打開同樣的文件沒有關(guān)閉,真正的占用了很多fd。
- CentOS 7中的lsof是按PID/TID/file的組合顯示結(jié)果的,上面lsof組合命令顯示“打開”了很文件的進程,只是因為進程運行了N個線程,而每個線程都“用到”了M個jar包,并且FD一欄分別為mem和具體fd號都分別顯示了一次,就出現(xiàn)了2*N*M——上萬條結(jié)果。
結(jié)論一:
使用lsof查看fd數(shù)是不正確的。
盡管網(wǎng)上很多文章教人這么用,但實際上不應(yīng)該這么做。
這是因為:
- lsof的結(jié)果包含了并非以fd形式打開的文件,比如用mmap方式訪問文件(FD一欄顯示為mem),實際并不占用fd。
其中包括了像.so這樣的文件。從結(jié)果看.jar文件也是以FD為mem和具體fd編號分別打開了一次。 - CentOS 7的lsof(我這里lsof -v的版本號是4.87)是按PID/TID/file的組合對應(yīng)一行,不是一行一個fd。同一個進程如果多個線程訪問同一個文件通常只需要打開一次、占用一個fd,但在lsof中就顯示多行。
如果用lsof -p <pid>,則不按TID顯示,結(jié)果數(shù)少很多。但仍包含了沒有使用fd的文件。
結(jié)論二:
準確的查看fd使用總數(shù)的命令是:
cat /proc/sys/fs/file-nr
或者(結(jié)果多的時候運行需要一段時間)
sudo find /proc -print | grep -P '/proc/\d+/fd/'| wc -l
注意如果用
sudo ls -l /proc/*/fd/* | wc -l
結(jié)果是不對的,比上面的命令返回結(jié)果少很多。原因是實際執(zhí)行是會把*擴充成具體的目錄作為參數(shù),而這個參數(shù)長度有限制。
查看具體一個進程號的fd數(shù)量是:
ls -l /proc/<pid>/fd | wc -l
查看哪個進程使用的fd最多(再來一路組合拳):
sudo find /proc -print | grep -P '/proc/\d+/fd/'| awk -F '/' '{print $3}' | uniq -c | sort -rn | head
但還要注意上面的命令返回的是系統(tǒng)的fd使用情況,而ulimit的配置是針對單用戶的,兩者是有區(qū)別的。
結(jié)論三:
不同版本的lsof輸出結(jié)果不同。
CentOS 7.3的lsof (我這里是4.87),按PID/TID/file顯示。CentOS 6.6的lsof(我這里是4.82),按PID/file顯示。結(jié)果數(shù)相差很大。但lsof -p <pid>的結(jié)果是一致的。
這在容器OS版本和宿主機OS版本不同時就需要注意了,在容器里和宿主機上用lsof查看同一進程的結(jié)果會很不同,我碰到的就是這種情況。
</br>
之前使用lsof(或lsof -n不解析協(xié)議主機名)的相關(guān)命令和真實意義:
打開文件總記錄數(shù)(沒太大意義):
lsof | wc -l
或
lsof -n | wc -l #不解析協(xié)議主機名
查看哪些pid使用文件數(shù)量最多(其實也沒有太大意義):
lsof | awk '{print $2}' | uniq -c | sort -rnk1 | head