SSD Nodes Learn Hosting plans →
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-08-27

علت پر بودن دیسک در df و خالی بودن در du

دستور df فضای دیسک را پر نشان می‌دهد اما du خیر؟ این مشکل به دلیل فایل‌های حذف شده‌ای است که توسط پردازش‌ها باز مانده‌اند. با دستور lsof فایل‌های باز را پیدا و آزاد کنید.

Verified Every command ran end-to-end on a fresh Ubuntu 24.04 server, August 15, 2026.

چرا df فضای دیسک را پر نشان می‌دهد اما du خیر

دستور df گزارش می‌دهد که دیسک پر است، در حالی که du نمی‌تواند این فضا را پیدا کند؛ دلیل این است که یک پردازش هنوز فایلی را که حذف شده، باز نگه داشته است. حذف یک فایل، نام آن را از دایرکتوری پاک می‌کند. بلوک‌های داده تنها زمانی آزاد می‌شوند که آخرین file descriptor که به آن inode اشاره دارد، بسته شود. دستور du نام‌ها را پیمایش می‌کند، بنابراین فایلی که نام ندارد را نمی‌شمارد. دستور df از فایل‌سیستم می‌پرسد که چه تعداد بلوک تخصیص داده شده است، بنابراین همچنان فایلی که دیگر نامی ندارد را در محاسبات خود لحاظ می‌کند.

این راهنما این وضعیت را روی یک Ubuntu VPS معمولی با ابزارهای از پیش نصب‌شده بازسازی می‌کند، پردازش نگهدارنده فایل را از طریق /proc پیدا می‌کند و بدون نیاز به reboot، فضا را آزاد می‌کند. سایر دلایل بروز این نشانه عبارتند از: جدول inode بدون ورودی آزاد، فایل‌هایی که زیر یک mount point مخفی شده‌اند و بلوک‌هایی که برای root رزرو شده‌اند.

هر دستور را اجرا کنید و خروجی خود را بخوانید. مقادیر به دیسک شما بستگی دارند، بنابراین به جای مقایسه با ارقام چاپ‌شده در راهنما، وضعیت قبل و بعد را روی ماشین خودتان مقایسه کنید.

تفاوت آنچه df و du محاسبه می‌کنند

df (disk free) از هر فایل‌سیستمِ mount شده، آمار خودش را می‌پرسد: چه تعداد بلاک وجود دارد، چه تعداد تخصیص یافته و چه تعداد آزاد است. این دستور هرگز دایرکتوری‌ها را باز نمی‌کند. پاسخ ارائه‌شده شامل تمام بلاک‌های تخصیص‌یافته است، حتی بلاک‌هایی که متعلق به فایلی هستند که هیچ ورودی دایرکتوری به آن اشاره نمی‌کند.

du (disk usage) برعکس عمل می‌کند. این دستور از مسیری که به آن می‌دهید شروع می‌کند، دایرکتوری‌ها را می‌خواند، وضعیت (stat) هر ورودی که پیدا می‌کند را بررسی کرده و بلاک‌ها را با هم جمع می‌زند. فایلی که نامی ندارد برای این دستور نامرئی است. همچنین هر دایرکتوری که اجازه خواندن آن را نداشته باشد نادیده گرفته می‌شود؛ به همین دلیل است که یک کاربر معمولی مجموع کمتری نسبت به root دریافت می‌کند. پیش از آنکه بر اساس مقایسه نتیجه‌گیری کنید، du را با دسترسی sudo اجرا کنید.

هنگام مقایسه این دو، دو گزینه همیشه اهمیت دارند:

  • -x باعث می‌شود du فقط روی یک فایل‌سیستم باقی بماند. بدون این گزینه، du / وارد تمام فایل‌سیستم‌های mount شده در زیرمجموعه / می‌شود و مجموعه‌ای تولید می‌کند که df / هرگز آن را اندازه‌گیری نمی‌کرده است.
  • -s به جای چاپ یک خط برای هر دایرکتوری، فقط یک خط خلاصه برای هر آرگومان چاپ می‌کند.

این موارد جفت‌دستوری را برای اجرا در کنار هم روی فایل‌سیستم مورد نظرتان فراهم می‌کند.

df -h /
sudo du -xhs / 2>/dev/null

df بلافاصله پاسخ می‌دهد. du در یک فایل‌سیستم بزرگ ممکن است دقایق زیادی طول بکشد، زیرا در طول مسیر وضعیت تک‌تک فایل‌ها را بررسی می‌کند. زمانی که مجموع این دو با هم اختلاف زیادی دارند و du با دسترسی root و گزینه -x اجرا شده است، فضای گمشده به چیزی تخصیص یافته که نامی ندارد.

بازتولید عمدی عدم تطابق

این کار را روی یک VPS آزمایشی انجام دهید. تمام موارد زیر bash و coreutils هستند، بنابراین نیازی به نصب هیچ بسته‌ای نیست.

وضعیت اولیه فایل‌سیستمی که /var/tmp را نگه می‌دارد، ثبت کنید.

cd /var/tmp
df -h .
df --output=used -B1 .

دستور دوم، تعداد بایت‌های استفاده‌شده را بدون گرد کردن چاپ می‌کند که باعث می‌شود بررسی نهایی دقیق باشد.

اکنون یک فایل ایجاد کنید. اندازه آن از فضای خالی که خودِ ماشین گزارش می‌دهد گرفته شده است، بنابراین این نمایش با هر دیسکی که دارید سازگار است.

free=$(df --output=avail -B1 . | tail -n 1)
fallocate -l $((free / 10)) ghost.bin
ls -l ghost.bin
df -h .

$(...) جایگزینی دستور (command substitution) است: شل دستور داخل آن را اجرا می‌کند و خروجی به مقدار free تبدیل می‌شود. اگر این نحو برای شما جدید است، جایگزینی دستور در bash آن را به‌طور کامل پوشش می‌دهد. fallocate بلوک‌های واقعی را بدون نوشتن در آن‌ها رزرو می‌کند، به همین دلیل است که فوراً به پایان می‌رسد. در فایل‌سیستمی که از این قابلیت پشتیبانی نمی‌کند، دستور با خطا مواجه می‌شود و head -c $((free / 10)) /dev/zero > ghost.bin همان کار را با نوشتن بایت‌ها انجام می‌دهد.

این df -h . را با موردی که ثبت کردید مقایسه کنید. ستون استفاده‌شده (used) افزایش یافته و ستون موجود (available) کاهش یافته است.

اکنون فایل را از طریق یک پردازش دیگر باز نگه دارید و سپس آن را حذف کنید.

sleep infinity < ghost.bin &
holder=$!
rm ghost.bin
ls -l ghost.bin
df -h .
sudo du -xhs . 2>/dev/null

این تغییر مسیر (redirection) تمام ترفند کار است. sleep infinity < ghost.bin & یک پردازش پس‌زمینه را شروع می‌کند که ورودی استاندارد آن همان فایل است، بنابراین شل فایل را باز کرده و توصیف‌گر (descriptor) آن را به sleep می‌دهد که آن را باز نگه می‌دارد. $! شناسه پردازش (PID) آن کار پس‌زمینه را نگه می‌دارد. سپس rm نام فایل را حذف می‌کند در حالی که توصیف‌گر هنوز باز است.

خروجی را بخوانید. ls نمی‌تواند فایل را پیدا کند، زیرا نام آن از بین رفته است. du به نزدیکی جایی که شروع شده بود بازگشته است، زیرا نام‌ها را پیمایش می‌کند. df تغییری نکرده است، زیرا بلوک‌ها هنوز تخصیص‌یافته باقی مانده‌اند. فایل‌سیستم و درخت دایرکتوری اکنون با هم اختلاف دارند و شکاف بین آن‌ها، همان فایلی است که به‌تازگی حذف کردید.

یافتن پردازشی که فایل حذف‌شده را در اختیار دارد

هر توصیف‌گر فایل (file descriptor) باز، در مسیر /proc/<pid>/fd/ به شکل یک لینک نمادین به فایلی که به آن اشاره دارد، ظاهر می‌شود. هنگامی که فایل unlinked می‌شود، هسته سیستم‌عامل مقصد آن لینک را به عنوان حذف‌شده علامت‌گذاری می‌کند. بنابراین، یافتن پردازش درگیر به معنای یافتن لینکی است که مقصد آن دارای این علامت باشد.

sudo find /proc/[0-9]*/fd -lname '*(deleted)' -printf '%p -> %l\n' 2>/dev/null

find /proc//fd -lname ' (deleted)' 2>/dev/null

-lname مقصد لینک نمادین را به جای نام آن مطابقت می‌دهد، %p مسیر توصیف‌گر را چاپ می‌کند و %l مقصدی که لینک به آن اشاره دارد را نمایش می‌دهد. شناسه پردازش (PID) دومین عنصر در مسیری است که چاپ می‌شود. این دستور را با sudo اجرا کنید، زیرا در غیر این صورت فقط می‌توانید /proc/<pid>/fd را برای پردازش‌های متعلق به خودتان بخوانید. تغییر مسیر stderr (خطا) باعث حذف پیام‌های اضافی از پردازش‌هایی می‌شود که در حین اجرای find خاتمه می‌یابند.

یک سرور پرمشغله در هر لحظه چندین فایل حذف‌شده را در اختیار دارد که اکثر آن‌ها کوچک و بی‌خطر هستند. آن‌ها را بر اساس اندازه مرتب کنید تا فقط موارد مهم در صدر لیست باقی بمانند.

sudo bash -c 'for fd in /proc/[0-9]*/fd/*; do
  target=$(readlink "$fd" 2>/dev/null) || continue
  case "$target" in
    *"(deleted)") echo "$(stat -Lc %s "$fd" 2>/dev/null) $fd $target" ;;
  esac
done' | sort -rn | head

find /proc//fd -lname ' (deleted)' -printf '%p %s\n' 2>/dev/null | sort -nk 2

stat -L لینک را تا رسیدن به خود inode دنبال می‌کند، بنابراین %s اندازه فایلی که دیگر نامی ندارد را گزارش می‌دهد. مرتب‌سازی بر اساس این عدد، بزرگ‌ترین فایل را در ابتدا قرار می‌دهد.

سپس پردازش پشت توصیف‌گر برتر را شناسایی کنید. مسیری که در بالای آن لیست قرار دارد، هر دو عددی که نیاز دارید را در خود دارد؛ پس آن‌ها را ابتدا در متغیرها قرار دهید و PID و N را با مقادیری که خروجی شما چاپ کرده است، جایگزین کنید.

pid=PID
n=N
ps -o pid,user,etime,args -p "$pid"
sudo stat -L "/proc/$pid/fd/$n"

ps -p PID -o comm=; stat -c 'Size: %s, Blocks: %b' /proc/PID/fd/N

ps نام برنامه را مشخص می‌کند و نشان می‌دهد چه مدت در حال اجرا بوده است. stat -L اندازه و تعداد بلوک‌های تخصیص‌یافته به inode حذف‌شده را چاپ می‌کند. این دو در کنار هم به پرسش اصلی پاسخ می‌دهند: کدام سرویس این فایل را زنده نگه داشته است.

اگر ماشین از قبل دارای lsof باشد، sudo lsof +L1 فایل‌های بازی را که تعداد لینک آن‌ها به صفر رسیده است فهرست می‌کند و اندازه‌ها را در یک جدول نشان می‌دهد. این ابزار در ایمیج‌های مینیمال Ubuntu موجود نیست و نصب یک پکیج روی فایل‌سیستمی که فضای خالی ندارد، خود ممکن است با شکست مواجه شود؛ بنابراین روش پیمایش /proc نسخه‌ای است که همیشه کار می‌کند.

آزادسازی فضا بدون راه‌اندازی مجدد

راه‌اندازی مجدد (reboot) مشکل را حل می‌کند، اما اولین اقدام اشتباهی است: سرویس را از دسترس خارج کرده و شواهد را از بین می‌برد. چهار گزینه ملایم‌تر وجود دارد که باید به ترتیب امتحان شوند.

ابتدا، اگر هنوز به داده‌ها نیاز دارید، آن‌ها را کپی کنید. خواندن مسیر توصیف‌گر (descriptor path)، inode زنده را می‌خواند.

sudo cp /proc/<pid>/fd/<n> /root/recovered.log

این تنها موردی است که بازگرداندن فایل حذف‌شده آسان است؛ به همین دلیل بازیابی فایل‌های حذف‌شده با rm -rf با پرسش درباره اینکه آیا فرآیندی هنوز فایل را باز نگه داشته است یا خیر، شروع می‌شود. به محض بسته شدن آخرین توصیف‌گر، این مسیر از بین می‌رود.

دوم، فایل را از طریق توصیف‌گر خالی کنید. مسیر /proc به همان inode اشاره دارد، بنابراین truncating آن باعث آزاد شدن بلوک‌ها می‌شود در حالی که فرآیند همچنان در حال اجرا باقی می‌ماند.

sudo truncate -s 0 "/proc/$pid/fd/$n"
df -h /

این روش زمانی به خوبی کار می‌کند که نویسنده، فایل را در حالت append باز کرده باشد، زیرا هر عملیات نوشتن به انتهای فعلی فایل منتقل می‌شود. در غیر این صورت، فرآیند آفست (offset) قدیمی خود را حفظ می‌کند، بنابراین نوشتن بعدی در عمق فایل انجام شده و فایل را با یک حفره در ابتدا بازسازی می‌کند. حفره تخصیص داده نمی‌شود، بنابراین بلوک‌ها آزاد می‌مانند و df فضایی را که به‌تازگی بازگردانده است، حفظ می‌کند. آنچه بازمی‌گردد فقط اندازه فایل است: پس از اینکه فرآیند دوباره نوشت، sudo stat -L "/proc/$pid/fd/$n" را اجرا کنید؛ این دستور اندازه قدیمی را در کنار تعداد بلوکی که دیگر با آن مطابقت ندارد، گزارش می‌دهد. اگر می‌خواهید اندازه فایل نیز از صفر شروع شود، فرآیند را مجدداً راه‌اندازی کنید.

سوم، از سرویس بخواهید فایل‌های لاگ خود را دوباره باز کند. دیمونی که فایل لاگ آن در زیرمجموعه‌اش حذف شده، نسخه واقعی و رایج این مشکل است. بسیاری از دیمون‌ها فایل‌های لاگ خود را با دریافت یک سیگنال دوباره باز می‌کنند: nginx از SIGUSR1 و rsyslog از SIGHUP استفاده می‌کند. به‌جای حدس زدن، مستندات دیمون مورد نظر خود را بررسی کنید، زیرا ارسال سیگنال اشتباه به دیمون اشتباه، باعث توقف آن می‌شود.

sudo systemctl kill -s USR1 nginx

این دستور سیگنال را به هر فرآیندی که systemd به عنوان فرآیند اصلی واحد (unit) ثبت کرده است می‌فرستد، بنابراین واحدی که Type= اشتباه برای نحوه شروع دیمون اعلام کرده باشد، ممکن است سیگنال شما را به فرآیندی برساند که هرگز فایل حذف‌شده را در اختیار نداشته است و فضا همچنان اشغال باقی می‌ماند.

چهارم، واحد را مجدداً راه‌اندازی کنید. sudo systemctl restart <unit> تمام توصیف‌گرهایی را که فرآیند قدیمی در اختیار داشت می‌بندد، بنابراین بلوک‌ها با قطعیت بازمی‌گردند. برای نمایش بالا، نگهدارنده یک sleep است که خودتان شروع کرده‌اید، بنابراین پایان دادن به آن کافی است.

kill $holder
df -h .
df --output=used -B1 .
sudo find /proc/[0-9]*/fd -lname '*(deleted)' -printf '%p -> %l\n' 2>/dev/null

بایت‌های استفاده‌شده را با مقداری که قبل از ایجاد فایل ثبت کرده بودید مقایسه کنید. آن‌ها دوباره مطابقت دارند و find دیگر توصیف‌گر شما را گزارش نمی‌کند. عادت به تأیید با همان دستوری که مشکل را پیدا کرده است، ارزشمند است.

مشاهده تغییر آن مقدار، آسان‌تر از اجرای دستی df به دفعات مکرر است. watch یک دستور را در فواصل زمانی ثابت تکرار می‌کند و خروجی را در همان محل چاپ می‌کند، بنابراین watch df -h / تغییر ستون استفاده‌شده را همزمان با بازگشت فضا نشان می‌دهد.

زمانی که مجموع مقادیر همخوانی دارند اما دیسک همچنان پر است

اگر df و یک du -x روت با یکدیگر همخوانی دارند، هیچ فایل حذف‌شده‌ای در میان نیست. دلایل باقی‌مانده ماهیت متفاوتی دارند و هر کدام بررسی خاص خود را می‌طلبند.

اتمام اینودها، نه بلوک‌ها

یک اینود (inode) متادیتای مربوط به یک فایل را در خود نگه می‌دارد. سیستم فایل ext4 هنگام ایجاد، تعداد ثابتی اینود تولید می‌کند؛ بنابراین ممکن است سیستم فایل در حالی که هنوز بلوک‌های خالی دارد، با کمبود اینود مواجه شود. در این حالت، ایجاد فایل‌های جدید با خطا مواجه می‌شود، حتی اگر df -h فضای خالی را نشان دهد.

df -h /
df -i /

دستور اول بلوک‌ها و دستور دوم اینودها را می‌شمارد. ستون استفاده (use) در هر کدام را مقایسه کنید. اگر میزان استفاده از بلوک پایین و استفاده از اینود در حد نهایی باشد، مشکل وجود تعداد بسیار زیادی فایل کوچک است.

df اجازه استفاده همزمان از -i و --output را در یک فراخوانی نمی‌دهد. بنابراین زمانی که می‌خواهید تعداد خام را برای خواندن یا ارسال به دستور دیگری داشته باشید، فیلدهای اینود را با نام انتخاب کنید و -i را حذف نمایید.

df --output=itotal,iused,iavail,ipcent /

آن ستون‌ها همان محاسباتی را ارائه می‌دهند که df -i چاپ می‌کند، با این تفاوت که به شکلی است که می‌توانید آن را تفکیک کنید.

فایل‌ها را با شمارش ورودی‌ها به جای بایت‌ها پیدا کنید.

sudo du --inodes -x -d 1 / 2>/dev/null | sort -rn | head

همین دستور را یک سطح پایین‌تر در دایرکتوری که بیشترین تعداد را دارد تکرار کنید تا به شاخه‌ای برسید که فایل‌ها را ایجاد می‌کند. اگر du شما از --inodes پشتیبانی نمی‌کند، sudo find /var -xdev -type f | wc -l زیرشاخه را به روشی کندتر می‌شمارد.

راه حل، حذف یا انتقال آن فایل‌ها است. شما نمی‌توانید به یک سیستم فایل ext4 موجود، اینود اضافه کنید، زیرا تعداد آن‌ها در زمان mkfs ثابت شده است؛ بنابراین افزایش آن مستلزم ایجاد مجدد سیستم فایل و بازیابی از نسخه پشتیبان است. سیستم فایل XFS اینودها را در صورت نیاز تخصیص می‌دهد، بنابراین با چنین سقف ثابتی مواجه نمی‌شود. ماشینی که کانتینرها را اجرا می‌کند، سریع‌تر از سایرین به هر دو محدودیت می‌رسد، زیرا لایه‌های ایمیج حاوی فایل‌های کوچک بسیاری هستند. در چنین ماشینی، پاکسازی فضای دیسک Docker در VPS راه حل اختصاصی است و فضای بسیار بیشتری نسبت به جستجوی کلی در سیستم فایل آزاد می‌کند.

فضای پنهان‌شده زیر یک mount point

یک دایرکتوری می‌تواند پیش از آنکه چیزی روی آن mount شود، حاوی فایل‌هایی باشد. اگر یک فایل‌سیستم را روی آن دایرکتوری mount کنید، فایل‌های زیرین دقیقاً در همان‌جایی که بودند باقی می‌مانند: همچنان تخصیص‌یافته هستند، همچنان توسط df شمرده می‌شوند، اما دیگر با نام قابل دسترسی نیستند. du نمی‌تواند آن‌ها را ببیند زیرا mount روی آن‌ها را پوشانده است.

این موضوع را با tmpfs نشان می‌دهیم که نیازی به دیسک اضافی ندارد. این بخش به ماشینی نیاز دارد که در آن اجازه mount کردن داشته باشید، بنابراین روی یک KVM VPS کار می‌کند.

sudo mkdir -p /srv/covered
sudo cp /etc/services /srv/covered/
ls /srv/covered
sudo mount -t tmpfs tmpfs /srv/covered
ls /srv/covered
sudo umount /srv/covered
ls /srv/covered

خروجی میانی ls یک دایرکتوری خالی را نشان می‌دهد. کپی فایل‌ها هرگز به جایی نرفته است: آن‌ها همچنان روی فایل‌سیستم ریشه (root) قرار دارند و لحظه‌ای که unmount کنید، دوباره ظاهر می‌شوند. حالا سرویسی را تصور کنید که یک ماه در آن مسیر لاگ نوشته است، پیش از آنکه کسی یک volume را روی آن mount کند.

برای پیدا کردن فایل‌های واقعی روی یک سرور در حال اجرا، فایل‌سیستم ریشه را برای بار دوم در جای دیگری mount کنید. یک bind mount، یک فایل‌سیستم را بدون فایل‌سیستم‌هایی که درون آن mount شده‌اند، نمایش می‌دهد.

sudo mkdir -p /mnt/rootcheck
sudo mount --bind / /mnt/rootcheck
sudo du -xhs /mnt/rootcheck/* 2>/dev/null | sort -h
sudo umount /mnt/rootcheck

هر چیزی که در آن لیست ظاهر شود اما در مسیر عادی دیده نشود، زیر یک mount point دفن شده است. پس از اتمام کار، bind mount را unmount کنید، در غیر این صورت یک du بعدی بدون -x، همان فایل‌ها را دو بار می‌شمارد.

بلوک‌های رزرو شده برای root

فایل‌سیستم ext4 بخشی از بلوک‌های خود را برای کاربر root رزرو می‌کند تا در صورت پر شدن دیسک، امکان ورود root به سیستم و تعمیر آن سلب نشود. فرآیندی که با دسترسی کاربر عادی اجرا می‌شود، زودتر با این محدودیت مواجه می‌گردد، در حالی که df همچنان فضای کمی را آزاد نشان می‌دهد. به‌جای فرض کردن مقادیر پیش‌فرض، تنظیمات فایل‌سیستم خود را بررسی کنید.

dev=$(df --output=source / | tail -n 1)
sudo tune2fs -l "$dev" | grep -i 'block count'

tune2fs -l /dev/sda1 | grep -E 'Block count|Reserved block count'

این دستور تعداد کل بلوک‌ها و تعداد بلوک‌های رزرو شده را با واحدهای یکسان چاپ می‌کند، بنابراین نسبت بین آن‌ها مستقیماً قابل محاسبه است. دستور df ستون available را به عنوان فضایی که یک کاربر عادی مجاز به استفاده از آن است گزارش می‌دهد؛ به همین دلیل مجموع فضای استفاده‌شده و فضای موجود، کمتر از اندازه کل دیسک است. این اختلاف همان فضای رزرو شده است.

این مقدار را با sudo tune2fs -m <percent> "$dev" تغییر دهید. تغییرات بلافاصله اعمال می‌شوند و نیازی به remount کردن نیست. کاهش فضای رزرو شده در یک فایل‌سیستم داده‌ای مجزا منطقی است. اما در فایل‌سیستم root، مقداری فضا باقی بگذارید تا root همچنان امکان نوشتن داشته باشد، زیرا فایل‌سیستمی که هیچ فضای خالی ندارد، بسیار سخت‌تر تعمیر می‌شود. این فضا همچنین مانع از قفل شدن شما در سیستم می‌شود: اگر کلید جدیدی به authorized_keys در فایل‌سیستمی که هیچ فضای خالی ندارد اضافه شود، ممکن است ناقص نوشته شود یا اصلاً ذخیره نگردد؛ در نتیجه تلاش بعدی برای ورود با خطای Permission denied (publickey) مواجه می‌شود که دلیل آن هیچ ارتباطی به خودِ کلید ندارد. دستور tune2fs روی ext2، ext3 و ext4 کار می‌کند. فایل‌سیستم XFS تنظیم مشابهی ندارد.

مواردی که du شما را به اشتباه می‌اندازد

چهار عادت du باعث می‌شود مجموع‌هایی که می‌بینید نادرست به نظر برسند.

  • لینک‌های سخت (Hard links): ابزار du هر inode را تنها یک‌بار می‌شمارد، حتی اگر چندین نام به آن اشاره کنند؛ بنابراین در درختی پر از لینک‌های سخت، مجموع گزارش‌شده کمتر از مجموع واقعی فایل‌ها خواهد بود.
  • فایل‌های پراکنده (Sparse files): ابزار du بلوک‌های واقعاً تخصیص‌یافته را گزارش می‌کند، در حالی که ls -l اندازه ظاهری فایل را نشان می‌دهد. برای مشاهده عدد دیگر، از --apparent-size استفاده کنید.
  • مجوزها (Permissions): اگر دستور را با یک کاربر معمولی اجرا کنید، du فایل‌هایی که اجازه خواندن آن‌ها را ندارد نادیده می‌گیرد و مقدار کمتری را گزارش می‌کند. خطاهایی که این ابزار چاپ می‌کند همان‌هایی هستند که کاربران معمولاً به /dev/null هدایت (redirect) کرده و دیگر آن‌ها را نمی‌خوانند.
  • مرزهای سیستم‌فایل (Filesystem boundaries): بدون استفاده از -x، ابزار du / تمام سیستم‌فایل‌های mount شده در زیرمجموعه / را می‌شمارد، بنابراین مجموع آن می‌تواند از آنچه df / گزارش می‌کند بیشتر باشد.

ابزار df نیز یک عادت دارد که دانستن آن مفید است. این ابزار هر سیستم‌فایل را به‌طور جداگانه گزارش می‌کند، بنابراین آن را دقیقاً روی مسیری اجرا کنید که عملیات نوشتن در آن با خطا مواجه شده است. یک /boot جداگانه با تجمع بسته‌های هسته (kernel packages) طبق زمان‌بندی خود پر می‌شود و حذف هسته‌های قدیمی در اوبونتو کاری متفاوت از آزادسازی فضا در / است.

ترتیب عملیاتی برای یک حادثه واقعی

  1. دستورات df -h <path> و df -i <path> را روی فایل‌سیستمی اجرا کنید که عملیات نوشتن ناموفق در آن رخ داده است، نه اینکه از روی عادت فقط / را بررسی کنید.
  2. دستور sudo du -xh -d 1 <mountpoint> 2>/dev/null | sort -h را اجرا کنید و سپس وارد بزرگترین دایرکتوری شوید.
  3. اگر du نمی‌تواند فضای گزارش‌شده توسط df را توجیه کند، /proc را برای یافتن فایل‌های حذف‌شده‌ای که هنوز باز هستند، جستجو کنید.
  4. اگر خروجی هر دو ابزار با هم مطابقت داشت، فایل‌سیستم را در جای دیگری bind mount کنید و به دنبال فایل‌هایی بگردید که زیر یک mount point پنهان شده‌اند.
  5. اگر محدودیت مربوط به inode است، به جای بایت‌ها، تعداد فایل‌ها را بشمارید.

هر مرحله شامل دستوری است که می‌توانید خروجی آن را بخوانید. این تفاوت بین رفع اصولی مشکل و حدس زدن علت آن است.

FAQ

چرا df دیسک را پر نشان می‌دهد در حالی که du فضای بسیار کمتری را گزارش می‌کند؟

دلیل معمول این است که فایلی حذف شده اما یک پردازش همچنان آن را باز نگه داشته است. حذف فایل، ورودی آن را از دایرکتوری پاک می‌کند، بنابراین du نامی برای پیمایش ندارد و شمارش آن را متوقف می‌کند. inode و بلاک‌های مربوط به آن تا زمانی که آخرین توصیف‌گر (descriptor) بسته نشود، در حالت تخصیص‌یافته باقی می‌مانند و df بلاک‌های تخصیص‌یافته را می‌شمارد. در /proc/<pid>/fd به دنبال لینک‌های نمادینی بگردید که هدف آن‌ها به عنوان حذف‌شده علامت‌گذاری شده است؛ به این ترتیب هم فایل و هم پردازشی که آن را نگه داشته است، پیدا می‌شوند. پیش از اعتماد به این مقایسه، اطمینان حاصل کنید که du را با دسترسی root و با فلگ -x اجرا کرده‌اید، زیرا کاربر معمولی دایرکتوری‌هایی را که اجازه خواندن آن‌ها را ندارد، بدون اطلاع قبلی نادیده می‌گیرد.

چگونه بدون استفاده از lsof، یک فایل حذف‌شده که همچنان باز است را پیدا کنم؟

از سوابق توصیف‌گرهای باز که در هسته (kernel) موجود است استفاده کنید. sudo find /proc/[0-9]*/fd -lname '*(deleted)' -printf '%p -> %l\n' 2>/dev/null تمام توصیف‌گرهایی که به فایلی بدون نام اشاره می‌کنند را فهرست می‌کند و شناسه پردازش (PID) در مسیری که چاپ می‌شود، قرار دارد. اجرای sudo stat -Lc %s روی یکی از آن مسیرهای توصیف‌گر، اندازه آن را گزارش می‌دهد، بنابراین می‌توانید آن‌ها را مرتب کرده و مورد مهم را انتخاب کنید. این روش به هیچ بسته‌ای نیاز ندارد، که این موضوع در مواقعی که نصب بسته جدید روی فایل‌سیستمی که فضای خالی ندارد با شکست مواجه می‌شود، اهمیت دارد.

آیا می‌توانم بدون کشتن (kill) پردازش، فضا را آزاد کنم؟

گاهی اوقات. sudo truncate -s 0 /proc/<pid>/fd/<n> از طریق توصیف‌گر به همان inode دسترسی پیدا کرده و بلاک‌های آن را آزاد می‌کند در حالی که پردازش همچنان در حال اجراست. این کار زمانی تمیزترین حالت است که پردازش فایل را در حالت append باز کرده باشد، زیرا عملیات نوشتن همیشه به انتهای فعلی فایل می‌رود. اگر این‌طور نباشد، آفست نوشتن در همان جای قبلی باقی می‌ماند و عملیات نوشتن بعدی، فایل را با یک حفره در ابتدا بازسازی می‌کند؛ در نتیجه اندازه گزارش‌شده دوباره افزایش می‌یابد در حالی که بلاک‌های زیر حفره آزاد می‌مانند. راه‌حل نهایی که هیچ فایل پراکنده‌ای (sparse file) باقی نمی‌گذارد، راه‌اندازی مجدد unit یا ارسال سیگنال به پردازش برای باز کردن مجدد لاگ‌ها (طبق مستندات آن) است.

df فضای خالی را نشان می‌دهد اما عملیات نوشتن همچنان با خطا مواجه می‌شود. علت دیگر چیست؟

تعداد inodeها را با df -i در همان مسیر بررسی کنید، زیرا فایل‌سیستمی که بلاک‌های خالی دارد اما inode خالی ندارد، فایل‌های جدید را نمی‌پذیرد. بررسی کنید که آیا عملیات نوشتن توسط کاربری غیر از root روی یک فایل‌سیستم ext4 انجام می‌شود که فقط بلاک‌های رزرو شده در آن باقی مانده است؛ این موضوع را می‌توان با sudo tune2fs -l روی دستگاه مشاهده کرد. بررسی کنید که آیا در حال خواندن همان فایل‌سیستمی هستید که عملیات نوشتن واقعاً آن را هدف قرار داده است، زیرا یک /boot یا /var جداگانه، مستقل از / پر می‌شود.

چرا du مجموع بزرگ‌تری نسبت به df گزارش می‌کند؟

du بدون -x به هر فایل‌سیستمی که زیر مسیر داده‌شده mount شده باشد وارد می‌شود، بنابراین چندین فایل‌سیستم را با هم جمع می‌زند در حالی که df فقط یکی را توصیف می‌کند. Bind mountها این وضعیت را بدتر می‌کنند، زیرا فایل‌های یکسان در هر مسیری که ظاهر می‌شوند، دوباره شمرده می‌شوند. از -x استفاده کنید تا du را در یک فایل‌سیستم واحد نگه دارید و به df همان مسیر را بدهید تا هر دو دستور، یک چیز واحد را توصیف کنند.