科技 · 實用指南
ulimit 已提高仍出現 Too many open files?先證明 Linux 服務撞到哪層上限
目前 shell 的 ulimit 不能代表已執行服務的實際限制。用失敗程序 PID 區分 RLIMIT_NOFILE、系統檔案表與 inotify,再修改上限。

Linux 的「Too many open files」不能直接證明哪層上限失敗。EMFILE 通常指呼叫程序的 RLIMIT_NOFILE,ENFILE 指系統檔案表;inotify 也可能因使用者耗盡實例數而回傳 EMFILE。只看一句錯誤不能診斷。
目前 shell 中很高的 ulimit 也不能證明執行中的服務擁有相同限制。先固定失敗時間與 PID,讀取程序實際繼承的限制並量測描述符,再檢查系統與 inotify 分支。
1. 固定失敗程序,而不是查看自己的 shell
重新啟動前記錄錯誤時間與服務 PID。systemd 服務可執行 systemctl show UNIT -p MainPID -p LimitNOFILE,再讀 /proc/PID/limits 並統計 /proc/PID/fd。之後在管理 shell 執行的 ulimit -n 只描述該 shell。
分開記錄軟、硬限制,把描述符數與軟限制比較,並用 readlink 抽樣目標。不要把 lsof -u USER | wc -l 當作單一程序計數。
2. 區分 EMFILE、ENFILE 與 inotify
若應用程式提供 errno,EMFILE 常表示程序越過 RLIMIT_NOFILE,ENFILE 表示系統檔案表耗盡。系統分支需比較 file-nr 與 file-max,並查核心日誌的 file-max reached 訊號。
若失敗操作建立監看或日誌提到 watch,就測試 inotify 分支。inotify_init 的 EMFILE 可來自使用者實例上限或程序 FD 上限;max_user_instances 與 max_user_watches 必須分開檢查。
3. 追蹤服務從哪裡繼承限制
辨識程序管理器。PAM 登入限制、shell ulimit、容器、應用程式啟動器與 systemd 單元是不同繼承鏈。systemd 服務先檢查已載入單元與 drop-in;LimitNOFILE 可用單值或 soft:hard。
不要把巨大數字套到所有服務。systemd 警告使用 select 的程式無法處理 1023 以上描述符。確認軟體支援後才提高,使用本地 drop-in 並保留舊值回復。
4. 區分正常併發與洩漏
單次計數不能證明需求是否正常。在相似負載下重複取樣同一 PID,依 socket、pipe、一般檔案與匿名描述符分組,並與應用指標比較。工作完成後持續只增不減是洩漏線索。
重新啟動會暫時降低計數,卻不一定修好原因。保留重啟前證據並尋找有界穩態;若某類描述符異常成長,先修其擁有者。
5. 只改一個擁有者,再驗證新子程序
只修改證據確認的層:程序限制改服務或啟動器,洩漏改應用,系統檔案表需容量評估,inotify 要先找出消耗者。若需要新子程序,只重啟一次,再讀新 PID 與限制。
成功代表目標程序繼承目標限制、實際負載下仍有餘量、原錯誤消失。缺少 errno、擁有者不明、數值超過 nr_open 或可能掩蓋無限洩漏時應停止並升級。
