排查file-max limit 65536 reached錯誤
近期遇到伺服器宕機,重啟後排查系統日誌,看到/var/log/messages中在宕機前出現大量file-max limit 65536 reached錯誤:
報file-max limit reached,說明機器上檔案控制代碼的使用量超過了設定值,以下是排查過程的整理。先說怎麼看檔案控制代碼數,這個資料記在/proc/sys/fs/file-nr中:
圖中1056表示當前已開啟檔案控制代碼數量,65536表示最大數量。最大值由/proc/sys/fs/file-max控制,可以通過echo 100000 > /proc/sys/fs/file-max來修改。由於出現宕機前新部署了應用,因此懷疑與它有關,通過不斷重新整理,發現此應用執行時確實導致檔案控制代碼上升,一段時間後上升至6000多。在linux下很多操作都會佔用檔案控制代碼,比如開啟檔案、開啟目錄、socket、pipe、共享記憶體等,首先懷疑有檔案open後沒有close,用lsof查下:
可以看到數量很少,排查系統內其它程序,lsof -n | awk '{print $2}' | sort | uniq -c | sort -nr | head,看到沒有哪個程序佔用大量檔案,那多出的5000多個控制代碼應該不是檔案,往共享記憶體方向排查,用pmap看看程序是否有mmap相關操作:
5392,這下對上了,用pmap進一步分析,發現有大量config.dat存在:
找到開發排查原始碼,發現對此檔案的使用涉及了mmap操作,先open檔案,然後用mmap把檔案對映到記憶體,再close檔案,結果用完後沒有munmap操作,導致檔案控制代碼不斷上漲。
問題定位了,翻回來看為什麼檔案close了依然會導致檔案控制代碼上漲。每個開啟的檔案都會有一個控制代碼用於描述檔案屬性,程序中用一個整數型的檔案描述符指向具體的控制代碼。由於linux核心中對檔案描述符是使用引用計數管理,mmap後,引用計數+1,此時儘管close了檔案描述符,但引用計數不為0,核心不會回收檔案控制代碼。
寫個小程式驗證一下:
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/mman.h> int main() { int i; for(i = 0;i < 1000;++i) { int fd; char * addr; fd = open("/dev/zero", O_RDONLY); //開啟檔案 if(!fd) exit(1); addr= mmap(NULL, 4096, PROT_READ, MAP_PRIVATE, fd, 0); //對映 //munmap(addr, 4096); //此行不執行就會導致控制代碼上漲 close(fd); //關閉檔案 } pause(); }
重複1000次,每次open和close是匹配的,但mmap沒有munmap,執行後的效果:
和預期一致,加上munmap後,對比測試則不會再出現這1000個控制代碼佔用。