你真知道“Too many open files”?
江湖上的人都碰到過Too many open files的錯誤(無論你是多線程,網絡socket,還是別的什么幺蛾子,這個錯誤很常見)。筆者一個朋友剛好也碰到過,經過一番搜索最終解決了問題。但是始終沒有搞清楚——“到底問題出在哪里?”。我當然也講不清楚(否則就不會有這篇文章咯)網上也找不到相關資料。作為一個有良知的自媒體公眾賬號,我決定要把替大家去深究一下這個問題。(花了兩個晚上~~,記得打賞我)
網上流傳的三種做法:
- 修改ulimit命令修改,這種修改只能在當前會話有效或者/etc/security/limits.conf設置hard soft nofile,可以一直有效
- sysctl修改fs.file-max
- 修改/proc/sys/fs/nr_open(可選)
還有一種傳說這是有優先級的——limit.conf < fs.file-max < nr_open
然而這都是扯淡,純粹的臆想。有良知的自媒體公眾賬號是講道理的,正所謂——沒代碼你說個屁啊!!!;所以我就順著Linux Kernel的代碼挖了下去。
Linux/Unix一個著名的哲學就是——“萬物皆文件”,無論是一個線程、socket、還是真正的文件都會被當做“文件”。Too may open files通常意味著“文件描述符”不足,它一般會發生在“創建線程”、“創建socket”、“打開文件”這種場景下。我選“創建socket”作為出發點
文件描述符的限制?不對!!
調用socket函數的時候內核會分兩步操作——填充數據結構,分配fd。我們重點看socket_map_fd
關鍵的地方來了,get_unused_fd_flags會嘗試分配一個fd,但是這個僅僅是fd——是一個數字而已;就是我們常說的——文件描述符。僅僅有一個數字并不代表什么,它相當于一個占位符,系統并沒有實際的分配資源。socket_alloc_file才是真正的建立文件結構(內核的數據結構:struct file)。打開get_unsed_fd_flags摸下去:
同志們,重點又來了。rlimit(RLIMIT_NOFILE)這個函數得到的是soft nofile,我們繼續看__alloc_fd
fd備用有三部分組成,進程當前預分配的(fd位圖中設置了標記,fdt->next_fd);進程當前可用的(fd位圖中沒有設置標記,fdt->max_fds);進程擴展的(fd位圖中都不存在,需要執行expand_files擴展fd位圖)所以__alloc_fd函數分為了三步嘗試分配fd。
- 嘗試“預分配”的fd(直接分配)
- 嘗試分配“可用的”的fd(需要填充位圖)
- 嘗試擴展fd位圖大小
如果fd超過soft nofile,這個函數會直接返回“錯誤”。所以soft nofile是fd大小限制的***道關卡,hard nofile全程沒用。soft nofile的準確而含義是——當前可以使用多少fd。
當前是跟“進程”有關系的,詳細內容請看***一部分。我們繼續看“擴充”fd:
fs.nr_open是文件描述符的***一道關卡,當我們嘗試擴充文件描述符的時候只要你不大于它系統就允許你擴充,它的***值是2147483584。
結論:
- soft nofile、fs.nr_open是用來控制文件描述符數量的
- soft nofile其實是linux的pam_limit模塊設置的如果你不啟用這個模塊,你只能通過ulimit命令調整。如果不調整它的值是4096(可以看***的代碼圖)
- nr_open表示文件描述符***數量。它的***值是2147483584(64位機器上2^31-64)。這也是是soft nofile、fs.nr_open可以設置的***值。
文件結構體
文件描述符在內核中其實是一個數字,它代表的是一個“索引”而索引的內容是“文件結構體”(內核數據結構 struct file)。內核分配資源的時候把“索引”和“內容”當做兩種資源來分配。先申請“索引”后申請“內容”。跳回sock_map_fd看第二步——分配文件結構,它調用了sock_alloc_file函數。
順著這個函數走下去你會發現——file-max(為了節省版面,完整的代碼圖我附在后面)
file-max是指struct file的上限。你可以把soft nofile、fs.nr_open設置成天文數字,但是不設置file-max就意味著沒法分配struct file,文件描述符就沒用了,依舊資源分配不成功。(像12306,你搶到票還不行還得“排隊”。搶到的僅僅是一個占位符,到***可能“沒票了”。對,我沒買到車票,等大家眾籌機票了。)
總結:
- fs.file-max是用來控制文件結構體數量的
等等,還沒結束
上面已經扒出了三個參數的真實意義,但是作為一個——有良知的自媒體公眾號必須把道理講清楚。所以我就挖出了soft nofile的前生今世。
PAM(Pluggable Authentication Modules)是Linux的認證框架,在系統啟動成功后無論是后臺服務進程還是bash都會通過setup_limits加載/etc/security/limit.conf文件然后調用setrlimit重新設置進程的rlimt——其中就包括了soft nofile。(pam_limit不在內核代碼中它有自己獨立的代碼倉庫,為了做有良知的自媒體我是不是特別拼?)
ulimit這個命令其實是系統的內部命令(不信你打which ulimit)它也是調用setrlimit完成的設置。二者的區別是pam_limit是自動加載的(屬于linux的“認證模塊”),ulimit你必須動手輸入命令。
【本文是51CTO專欄作者邢森的原創文章,轉載請聯系作者本人獲取授權】