اگر سرویس شما با خطای Too many open files از کار میافتد، اول Limit را بالا نبرید. ابتدا مشخص کنید مشکل از سقف همان Process است (EMFILE) یا سقف کل سیستم (ENFILE)، بعد تعداد File Descriptorهای باز، RLIMIT_NOFILE، تنظیم LimitNOFILE در systemd و وضعیت /proc/sys/fs/file-max را بررسی کنید. بالا بردن Limit بدون پیدا کردن علت رشد FD میتواند فقط نشتی یا مشکل برنامه را پنهان کند.
خلاصه سریع عیبیابی Too many open files
اگر نام سرویس را میدانید، مثلاً nginx:
systemctl status nginx --no-pager
journalctl -u nginx -n 100 --no-pager
PID=$(systemctl show -p MainPID --value nginx)
echo "PID=$PID"
cat /proc/$PID/limits | grep -i "open files"
ls /proc/$PID/fd | wc -l
cat /proc/sys/fs/file-nr
cat /proc/sys/fs/file-max
cat /proc/sys/fs/nr_open
systemctl show nginx -p LimitNOFILE
اگر تعداد FDهای باز به Soft Limit نزدیک است، قبل از تغییر Limit بفهمید چه نوع Descriptorهایی رشد کردهاند. اگر خطا system-wide است، file-nr و لاگ Kernel را بررسی کنید.
Too many open files دقیقاً یعنی چه؟ EMFILE و ENFILE
در Linux، File Descriptor فقط «فایل روی دیسک» نیست. Socket شبکه، Pipe، Log، فایل تنظیمات، اتصال دیتابیس و بسیاری از منابع I/O یک FD مصرف میکنند.
دو حالت مهم داریم:
- EMFILE: همان Process به سقف
RLIMIT_NOFILEرسیده است. - ENFILE: سقف system-wide فایلهای باز در Kernel رسیده است.
طبق مستندات Linux، RLIMIT_NOFILE عددی یک واحد بزرگتر از بیشترین File Descriptor قابل باز شدن برای Process است. سقف system-wide نیز جداگانه با fs.file-max کنترل میشود.
مرحله 1: خطا را در Log همان سرویس تأیید کنید
اول وضعیت و لاگ سرویس را ببینید:
systemctl status SERVICE --no-pager
journalctl -u SERVICE -n 200 --no-pager
برای جستجوی عمومی:
journalctl -b --no-pager | grep -Ei "too many open files|EMFILE|ENFILE|file-max"
dmesg -T | grep -Ei "file-max|file handle|ENFILE"
اگر سرویس Crash-loop شده است، قبل از Reset کردن وضعیت systemd علت Crash را پیدا کنید. برای مسیر کامل تشخیص Crash-loop میتوانید راهنمای رفع خطای start request repeated too quickly در systemd را ببینید.
مرحله 2: PID واقعی سرویس و Limit آن را پیدا کنید
برای سرویس systemd:
PID=$(systemctl show -p MainPID --value SERVICE)
echo "$PID"
cat /proc/$PID/limits | grep -i "open files"
خروجی نمونه:
Max open files 1024 524288 files
عدد اول Soft Limit و عدد دوم Hard Limit است.
برای Shell فعلی نیز:
ulimit -Sn
ulimit -Hn
نکته مهم: مقدار ulimit -n در Shell شما الزاماً Limit سرویس systemd نیست. برای سرویس، /proc/PID/limits و systemctl show مرجع عملی بهتری هستند.
مرحله 3: ببینید Process چند FD باز دارد
روش کمهزینه:
ls /proc/$PID/fd | wc -l
برای دیدن نمونه Descriptorها:
ls -l /proc/$PID/fd | head -n 30
و برای تحلیل بیشتر:
sudo lsof -p "$PID" | head -n 50
sudo lsof -p "$PID" | awk 'NR>1 {print $5}' | sort | uniq -c | sort -nr | head
اگر با lsof آشنا نیستید، مقاله راهنمای نصب و استفاده از lsof در لینوکس نقطه شروع مناسبی است.
مرحله 4: نوع FDهایی که رشد میکنند را پیدا کنید
یک Snapshot بگیرید:
date
ls /proc/$PID/fd | wc -l
sleep 60
date
ls /proc/$PID/fd | wc -l
اگر بدون افزایش طبیعی Load، تعداد FD مرتب بالا میرود و پایین نمیآید، احتمال Leak یا Connectionهایی که بسته نمیشوند وجود دارد.
برای Socketها:
sudo ss -s
sudo ss -tanp | grep "pid=$PID," | head
برای فایلها و Pipeها:
sudo lsof -p "$PID" | grep -E "REG|FIFO|PIPE" | head -n 50
قبل از افزایش Limit، علت رشد را در Application، Connection Pool، Log handler، Socket handling یا Worker lifecycle بررسی کنید.
مرحله 5: LimitNOFILE سرویس systemd را بررسی کنید
systemctl show SERVICE -p LimitNOFILE
systemctl cat SERVICE
systemd اجازه میدهد Soft و Hard Limit برای Processهای سرویس با LimitNOFILE= تعیین شوند.
برای Override امن:
sudo systemctl edit SERVICE
سپس:
[Service]
LimitNOFILE=65536
بعد:
sudo systemctl daemon-reload
sudo systemctl restart SERVICE
و Verify:
PID=$(systemctl show -p MainPID --value SERVICE)
cat /proc/$PID/limits | grep -i "open files"
systemctl show SERVICE -p LimitNOFILE
عدد 65536 مثال است، نه مقدار پیشنهادی برای همه سرویسها. مقدار درست باید بر اساس Concurrency، مستندات برنامه، تعداد Workerها و اندازه واقعی مصرف تعیین شود.
چرا ویرایش limits.conf ممکن است برای سرویس systemd اثر نکند؟
فایلهای /etc/security/limits.conf و limits.d معمولاً در Sessionهایی که از PAM عبور میکنند اعمال میشوند. سرویسهایی که توسط systemd اجرا میشوند باید با تنظیمات Unit و LimitNOFILE= بررسی شوند.
برای Session کاربر:
ulimit -Sn
ulimit -Hn
نمونه تنظیم:
# /etc/security/limits.d/90-myapp.conf
myuser soft nofile 65536
myuser hard nofile 65536
پس از تغییر، Session جدید بسازید و دوباره ulimit را Verify کنید. تغییر Limit در یک Shell موجود معمولاً به Processهای از قبل اجراشده منتقل نمیشود.
مرحله 6: سقفهای Kernel را فقط در صورت نیاز بررسی کنید
cat /proc/sys/fs/file-nr
cat /proc/sys/fs/file-max
cat /proc/sys/fs/nr_open
fs.file-max سقف system-wide فایلهای باز است. fs.nr_open نیز سقفی برای مقداری است که RLIMIT_NOFILE میتواند تا آن بالا برود.
اگر فقط یک Process به Soft Limit خودش رسیده، افزایش fs.file-max مشکل اصلی را حل نمیکند.
اگر شواهد واقعی نشان میدهد سقف system-wide محدودکننده است، مقدار فعلی و Peak مصرف را ثبت کنید و سپس تغییر را کنترلشده انجام دهید. از کپی کردن اعداد بسیار بزرگ بدون Capacity Planning خودداری کنید.
مرحله 7: بعد از اصلاح، تست و Verify واقعی انجام دهید
بعد از Restart یا Deploy:
PID=$(systemctl show -p MainPID --value SERVICE)
watch -n 2 "ls /proc/$PID/fd | wc -l"
در پنجره دیگر:
journalctl -u SERVICE -f
در زمان Load واقعی یا تست کنترلشده بررسی کنید:
- خطای
Too many open filesبرنمیگردد؛ - FD count بهصورت بدون سقف رشد نمیکند؛
- Latency و Error rate طبیعی است؛
- اتصالهای شبکه و Workerها پس از اتمام کار آزاد میشوند.
سناریوی واقعی: NGINX یا Reverse Proxy به سقف FD میرسد
فرض کنید NGINX زیر Load بالا خطای Open File میدهد. ابتدا:
PID=$(systemctl show -p MainPID --value nginx)
cat /proc/$PID/limits | grep -i "open files"
ls /proc/$PID/fd | wc -l
sudo ss -s
سپس تنظیم Unit و تنظیمات خود NGINX را با هم بررسی کنید. بالا بردن فقط یکی از Limitها کافی نیست اگر برنامه سقف داخلی دیگری داشته باشد.
برای تنظیمات و Performance وبسرور، مقاله تنظیم و بهینهسازی وبسرور NGINX نیز مرتبط است.
خطاهای رایج در رفع Too many open files
- فقط اجرای ulimit -n 65536: ممکن است فقط Shell فعلی را تغییر دهد و روی سرویس systemd اثری نداشته باشد.
- بالا بردن Limit بدون بررسی Leak: زمان Crash را عقب میاندازد ولی علت را حل نمیکند.
- تغییر file-max برای خطای EMFILE: اگر Process به RLIMIT خودش رسیده، سقف system-wide مسئله اصلی نیست.
- Restart بدون ثبت شواهد: Restart شمارنده FD را صفر میکند و سرنخ Leak را از بین میبرد. قبل از Restart، PID، limits، FD count و نمونه lsof را ذخیره کنید.
- قرار دادن Limit غیرمنطقی: هر FD منابع Kernel مصرف میکند و برنامه نیز باید توان مدیریت آن Concurrency را داشته باشد.
Rollback و Recovery
اگر بعد از تغییر LimitNOFILE یا تنظیمات برنامه مشکل جدید ایجاد شد:
sudo systemctl revert SERVICE
sudo systemctl daemon-reload
sudo systemctl restart SERVICE
اگر Override شما تنظیمات دیگری هم دارد، بهجای Revert کورکورانه ابتدا آن را ببینید:
systemctl cat SERVICE
پس از Rollback:
systemctl status SERVICE --no-pager
systemctl show SERVICE -p LimitNOFILE
PID=$(systemctl show -p MainPID --value SERVICE)
cat /proc/$PID/limits | grep -i "open files"
چکلیست نهایی
systemctl status SERVICE --no-pager
journalctl -u SERVICE -n 200 --no-pager
PID=$(systemctl show -p MainPID --value SERVICE)
cat /proc/$PID/limits | grep -i "open files"
ls /proc/$PID/fd | wc -l
systemctl show SERVICE -p LimitNOFILE
cat /proc/sys/fs/file-nr
cat /proc/sys/fs/file-max
cat /proc/sys/fs/nr_open
اگر روی یک VPS لینوکسی چند سرویس پرترافیک اجرا میکنید، علاوه بر افزایش Limit باید RAM، CPU، Connection Pool، Workerها و ظرفیت Kernel را هم متناسب با Load واقعی بررسی کنید.
منابع فنی
- getrlimit(2) — Linux manual page
- proc_sys_fs(5) — file-max و nr_open
- systemd.exec — LimitNOFILE
- systemd-system.conf — DefaultLimitNOFILE