ESC را فشار دهید تا بسته شود

رفع خطای Too many open files در لینوکس؛ عیب‌یابی File Descriptor و LimitNOFILE

فهرست

اگر سرویس شما با خطای 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 واقعی بررسی کنید.

منابع فنی

Rate this post
اشتراک گذاری نوشته در:

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *