Trên Linux, file descriptor không chỉ đại diện cho file trên ổ đĩa. Socket, pipe, terminal, eventfd và nhiều tài nguyên I/O khác cũng chiếm descriptor. Khi ứng dụng báo Too many open files, tăng giới hạn ngay có thể che triệu chứng mà không trả lời tải hợp lệ đang tăng hay chương trình làm rò tài nguyên.
Quy trình an toàn là xác định process, đo FD theo thời gian, phân loại tài nguyên, phân biệt giới hạn process với toàn hệ thống, sửa lifecycle rồi mới điều chỉnh capacity.
1. File descriptor là gì?
Kernel trả về một số nguyên không âm khi process mở tài nguyên. Descriptor 0, 1 và 2 lần lượt là standard input, output và error. File, kết nối mạng, pipe và descriptor duplicate đều dùng vị trí trong bảng FD của process.
Descriptor trỏ tới open file description cấp kernel, nơi giữ offset và flags. Nhiều descriptor có thể cùng tham chiếu một description sau dup hoặc fork; vì vậy số FD không luôn bằng số file handle toàn hệ thống.
Web server có thể hết “open files” chủ yếu vì socket, không phải file dữ liệu.
2. EMFILE khác ENFILE
| Lỗi | Phạm vi | Nơi kiểm tra |
|---|---|---|
EMFILE | Process chạm RLIMIT_NOFILE | /proc/PID/limits, prlimit, service |
ENFILE | Hệ thống chạm giới hạn handle | file-nr, file-max, kernel log |
Framework có thể ẩn errno gốc. Hãy xem system-call error hoặc tracing phù hợp thay vì đoán.
3. Đọc giới hạn của đúng PID
pgrep -a my-service
systemctl status my-service.service
cat /proc/12345/limits
prlimit --pid 12345 --nofileMax open files có soft và hard limit. Process có thể nâng soft tới hard khi được phép. RLIMIT_NOFILE là một lớn hơn số FD lớn nhất được mở.
ulimit -n trong terminal chỉ mô tả shell và process con tương lai; nó không tự đổi service systemd đang chạy. Luôn kiểm tra /proc/PID/limits của process thật.
4. Đếm và theo dõi FD
pid=12345
find "/proc/$pid/fd" -mindepth 1 -maxdepth 1 -printf '.' 2>/dev/null | wc -c
ls -l "/proc/$pid/fd" | head/proc/PID/fd chứa một symlink cho mỗi descriptor. Target có thể là pathname, socket:[inode], pipe:[inode] hoặc anon_inode. Vì FD thay đổi trong lúc quét, kết quả là snapshot gần đúng.
Một con số đơn lẻ chưa chứng minh leak. Lấy mẫu định kỳ, ghi PID và thời gian, rồi so với request rate, active connections, queue depth, deploy và restart. FD tăng theo traffic rồi giảm có thể bình thường; FD chỉ tăng khi tải ổn định là dấu hiệu đáng điều tra.
5. Phân loại descriptor
lsof -nP -p 12345
for fd in /proc/12345/fd/*; do readlink "$fd"; done | sort | uniq -c | sort -nr | head-nP tránh DNS và chuyển port sang tên service. Với process rất lớn, hãy lọc PID và lấy mẫu có kiểm soát.
- Nhiều socket: kiểm tra keep-alive, pool và trạng thái kết nối.
- Nhiều log: kiểm tra rotation, reopen và handler tạo lặp.
- File
(deleted): process vẫn giữ file đã unlink; dung lượng chỉ giải phóng khi đóng. - Nhiều pipe/anon inode: kiểm tra worker, subprocess, watcher và event loop.
6. Kiểm tra FD cụ thể
readlink /proc/12345/fd/42
cat /proc/12345/fdinfo/42fdinfo có thể hiện offset, flags, mount ID và dữ liệu riêng theo loại. Không đọc nội dung tùy tiện qua /proc/PID/fd/N: pipe, device hoặc stream có thể block hay ảnh hưởng luồng dữ liệu.
7. Giới hạn toàn hệ thống
cat /proc/sys/fs/file-nr
cat /proc/sys/fs/file-max
cat /proc/sys/fs/nr_open
journalctl -k --since '-30 min' | grep -i 'file-max\|file handle'file-max là số file handle tối đa kernel cấp toàn hệ thống. file-nr báo handle đã cấp và mức tối đa; trên Linux hiện đại trường unused thường bằng 0. nr_open là trần cho giới hạn FD của process, còn giới hạn hiệu lực phụ thuộc RLIMIT_NOFILE.
Không cộng mọi entry trong /proc/PID/fd để thay file-nr: descriptor có thể chia sẻ description, process thay đổi trong lúc quét và permission có thể che dữ liệu.
8. Tìm process dùng nhiều FD
for proc in /proc/[0-9]*; do
pid=$(basename "$proc")
count=$(find "$proc/fd" -mindepth 1 -maxdepth 1 -printf '.' 2>/dev/null | wc -c)
[ "$count" -gt 0 ] && printf '%8s %8s\n' "$count" "$pid"
done | sort -nr | headĐây là ảnh chụp gần đúng. Không kill process chỉ vì đứng đầu: proxy, database và broker hợp lệ có thể giữ nhiều socket. Dùng ps, cgroup và systemd để xác định owner rồi so với baseline.
9. Sửa leak trước
Nguyên nhân phổ biến gồm thiếu close trên nhánh exception, response body không đóng, pool không giới hạn, watcher tạo lại theo request hoặc child process giữ pipe. Gắn tài nguyên vào scope:
with open(path, "rb") as stream:
process(stream)
resource = open_resource()
try:
use(resource)
finally:
resource.close()Đặt upper bound cho pool, timeout kết nối rảnh và metric active/idle. Load test phải xác minh FD về gần baseline sau khi traffic dừng.
10. Cấu hình LimitNOFILE cho systemd
sudo systemctl edit my-service.service[Service]
LimitNOFILE=65536sudo systemctl daemon-reload
sudo systemctl restart my-service.service
systemctl show my-service.service -p LimitNOFILE
cat /proc/$(systemctl show -p MainPID --value my-service.service)/limitsDùng drop-in thay vì sửa unit của package. Restart có thể gây gián đoạn nên phải theo quy trình triển khai. Xác minh PID mới; wrapper, container hoặc code có thể làm giới hạn hiệu lực khác cấu hình nhìn thấy.
11. Shell, PAM, container và service
| Nơi khởi động | Điểm kiểm soát thường gặp |
|---|---|
| Interactive shell | ulimit, PAM/login |
| Systemd service | LimitNOFILE= trong unit/drop-in |
| Container | Runtime/orchestrator và host ceiling |
| Worker ứng dụng | Limit kế thừa trừ khi code đổi rlimit |
Không giả định /etc/security/limits.conf điều khiển systemd service.
12. Tăng limit có chi phí
Limit cao cho phép process dùng thêm kernel memory, socket và connection downstream. Nếu leak còn nguyên, tăng từ 1.024 lên 1.000.000 chỉ trì hoãn lỗi và có thể tăng phạm vi ảnh hưởng.
- Tính capacity từ connection, file, pipe và headroom.
- Cảnh báo theo tỷ lệ
open_fds / soft_limit. - Giới hạn pool và downstream concurrency.
- Canary thay đổi, theo dõi pressure và chuẩn bị rollback.
- Không vượt
fs.nr_openkhi chưa kiểm chứng.
Checklist xử lý
- Ghi errno, PID và thời điểm.
- Đọc soft/hard limit của PID.
- Lấy nhiều mẫu để xác định xu hướng.
- Phân loại file, socket, pipe và deleted file.
- So với traffic, deploy và baseline.
- Kiểm tra
file-nr,file-max,nr_open. - Sửa lifecycle, pool và timeout.
- Nếu tải hợp lệ vượt limit, tăng ở đúng lớp khởi động.
- Restart có kiểm soát và xác minh PID mới.
- Load test và kiểm tra FD giảm sau tải.
Tài liệu tham khảo
- Linux man-pages: open(2)
- Linux man-pages: prlimit(2)
- Linux man-pages: /proc/PID/fd
- Linux kernel: /proc/sys/fs
Kết luận
“Too many open files” là bài toán lifecycle và capacity, không chỉ là số ulimit. Hãy xác định EMFILE hay ENFILE, đo đúng PID, phân loại tài nguyên và tìm leak trước khi nâng giới hạn. Khi tải thật sự cần thêm FD, cấu hình tại đúng lớp—đặc biệt LimitNOFILE cho systemd—rồi xác minh process mới và theo dõi tỷ lệ sử dụng.




Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.