df komutu dolu diyor ama du komutu boş gösteriyor
Disk dolu görünmesine rağmen dosya bulunamıyorsa silinen ancak süreçler tarafından tutulan dosyaları lsof ile tespit edin. Inode ve mount noktası sorunlarını çözün.
df komutu neden dolu diyor ve du komutu neden farklı sonuç veriyor
df disk alanını dolu olarak raporlarken, du bir süreç hala silinmiş bir dosyayı tuttuğu için bu alanı bulamaz. Bir dosyayı silmek, dosya adını dizinden kaldırır. Veri blokları, yalnızca o inode'u işaret eden son açık dosya tanımlayıcısı kapatıldığında serbest bırakılır. du dosya adlarını tarar, bu yüzden hiçbir şeyi saymaz. df dosya sistemine kaç bloğun tahsis edildiğini sorar, bu nedenle artık adı olmayan dosyayı saymaya devam eder.
Bu kılavuz, durumu halihazırda yüklü araçlarla standart bir Ubuntu VPS üzerinde yeniden oluşturur, tutan süreci /proc aracılığıyla bulur ve yeniden başlatma gerektirmeden alanı boşaltır. Aynı belirtinin diğer nedenleri şunlardır: boş girişi kalmamış bir inode tablosu, bir bağlama noktasının (mount point) altında gizli kalan dosyalar ve root kullanıcısı için ayrılmış bloklar.
Her komutu çalıştırın ve kendi çıktınızı okuyun. Değerler diskinize bağlıdır; bu nedenle bir kılavuzda basılı bir rakamla karşılaştırmak yerine, kendi makinenizdeki öncesi ve sonrası durumlarını karşılaştırın.
df neyi sayar, du neyi sayar
df (disk free), her bir bağlı dosya sistemine kendi muhasebesini sorar: kaç blok var, kaçı tahsis edilmiş, kaçı boş. Asla bir dizini açmaz. Yanıt, hiçbir dizin girdisinin işaret etmediği bir dosyaya ait bloklar da dahil olmak üzere, tahsis edilmiş her bloğu kapsar.
du (disk usage) ise tam tersini yapar. Verdiğiniz bir yoldan başlar, dizinleri okur, bulduğu her girdinin istatistiklerini alır ve blokları toplar. İsmi olmayan bir dosya, bu komut için görünmezdir. Okuma izni olmayan dizinler de aynı şekildedir; bu yüzden normal bir kullanıcı, root kullanıcısından daha küçük bir toplam elde eder. Karşılaştırmadan herhangi bir sonuç çıkarmadan önce du komutunu sudo altında çalıştırın.
İkisini karşılaştırırken her seferinde iki seçenek önem taşır.
-x,duişlemini tek bir dosya sistemi üzerinde tutar. Bu seçenek olmadandu /,/altına bağlı her dosya sistemine girer vedf /aracının asla ölçmediği bir toplam üretir.-s, her dizin için bir satır yerine, her argüman için tek bir özet satırı yazdırır.
Bu, ilgilendiğiniz dosya sistemi üzerinde yan yana çalıştırılacak ikiliyi sağlar.
df -h /
sudo du -xhs / 2>/dev/nulldf anında yanıt verir. du ise büyük bir dosya sisteminde dakikalar sürer, çünkü yol üzerindeki her dosyanın istatistiğini alır. İki toplam birbirinden çok uzaksa ve du komutu root yetkileriyle -x kullanılarak çalıştırılmışsa, eksik alan ismi olmayan bir şeye tahsis edilmiştir.
Uyumsuzluğu kasten oluşturma
Bunu bir test VPS üzerinde gerçekleştirin. Aşağıdaki işlemlerin tamamı bash ve coreutils ile yapıldığından, herhangi bir kurulum gerektirmez.
/var/tmp dizinini barındıran dosya sisteminin başlangıç durumunu kaydedin.
cd /var/tmp
df -h .
df --output=used -B1 .İkinci komut, kullanılan bayt miktarını yuvarlama yapmadan yazdırır; bu sayede sondaki kontrol kesin sonuç verir.
Şimdi bir dosya oluşturun. Dosya boyutu, makinenin kendi raporladığı boş alan miktarına göre belirlenir, böylece gösterim sahip olduğunuz her türlü disk üzerinde çalışır.
free=$(df --output=avail -B1 . | tail -n 1)
fallocate -l $((free / 10)) ghost.bin
ls -l ghost.bin
df -h .$(...) bir komut ikamesidir (command substitution): kabuk, içindeki komutu çalıştırır ve çıktısını free değerine atar. Bu sözdizimi sizin için yeniyse, bash'te komut ikamesi konusu bunu detaylıca açıklar. fallocate, verileri yazmadan gerçek blokları rezerve eder; bu yüzden işlem anında tamamlanır. Bunu desteklemeyen bir dosya sisteminde komut başarısız olur; bu durumda head -c $((free / 10)) /dev/zero > ghost.bin aynı işi baytları yazarak gerçekleştirir.
Bu df -h . çıktısını kaydettiğiniz değerle karşılaştırın. Kullanılan alan sütununun arttığını, kullanılabilir alan sütununun ise azaldığını göreceksiniz.
Şimdi dosyayı başka bir süreç üzerinden açık tutun ve ardından silin.
sleep infinity < ghost.bin &
holder=$!
rm ghost.bin
ls -l ghost.bin
df -h .
sudo du -xhs . 2>/dev/nullYönlendirme (redirection), işlemin püf noktasıdır. sleep infinity < ghost.bin &, standart girdisi ilgili dosya olan bir arka plan süreci başlatır; kabuk dosyayı açar ve dosya tanımlayıcısını (descriptor), dosyayı açık tutan sleep sürecine iletir. $!, bu arka plan işinin süreç kimliğini (PID) tutar. rm ise dosya tanımlayıcısı hala açıkken dosya ismini siler.
Çıktıyı okuyun. ls dosya ismini bulamaz çünkü isim silinmiştir. du, isimleri taradığı için başlangıç değerine yakın bir yerdedir. df ise bloklar hala tahsis edilmiş durumda olduğundan değişmemiştir. Dosya sistemi ile dizin ağacı artık uyumsuzdur; aradaki fark, az önce sildiğiniz dosyadır.
Silinmiş dosyayı tutan süreci bulma
Her açık dosya tanımlayıcısı, /proc/<pid>/fd/ altında ilgili dosyaya giden sembolik bir bağlantı olarak görünür. Dosya silindiğinde (unlink), çekirdek bu bağlantının hedefini silinmiş olarak işaretler. Dolayısıyla, dosyayı tutan süreci bulmak, hedefi bu işareti taşıyan bir bağlantıyı bulmak anlamına gelir.
sudo find /proc/[0-9]*/fd -lname '*(deleted)' -printf '%p -> %l\n' 2>/dev/null-lname, sembolik bağlantının adını değil hedefini eşleştirir, %p tanımlayıcı yolunu yazdırır ve %l ise bağlantının işaret ettiği yeri gösterir. Süreç kimliği (PID), yazdırılan yolun ikinci öğesidir. Komutu sudo ile çalıştırın; aksi takdirde yalnızca kendi süreçleriniz için /proc/<pid>/fd dizinini okuyabilirsiniz. stderr yönlendirmesi, find tarama yaparken sonlanan süreçlerden kaynaklanan gürültüyü engeller.
Yoğun bir sunucuda her an silinmiş durumda olan birçok dosya bulunur ve bunların çoğu küçük ve zararsızdır. Bunları boyuta göre sıralayarak yalnızca önemli olanların en üstte kalmasını sağlayın.
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 | headstat -L, bağlantıyı doğrudan inode'un kendisine kadar takip eder, böylece %s artık bir adı olmayan dosyanın boyutunu raporlar. Bu sayıya göre sıralama yapmak, en büyük dosyayı en başa getirir.
Ardından, kazanan tanımlayıcının arkasındaki süreci tanımlayın. Listenin en üstündeki yol, ihtiyacınız olan her iki sayıyı da içerir; bu nedenle PID ve N değerlerini kendi listenizde çıkan sonuçlarla değiştirerek değişkenlere atayın.
pid=PID
n=N
ps -o pid,user,etime,args -p "$pid"
sudo stat -L "/proc/$pid/fd/$n"ps, programın adını belirtir ve ne kadar süredir çalıştığını gösterir. stat -L, silinmiş inode'un boyutunu ve ayrılmış blok sayısını yazdırır. Bu bilgiler birlikte, hangi servisin bu dosyayı canlı tuttuğu sorusunu yanıtlar.
Eğer makinede halihazırda lsof yüklüyse, sudo lsof +L1 bağlantı sayısı sıfıra düşmüş açık dosyaları listeler ve boyutlarını tek bir tabloda gösterir. Bu araç minimal bir Ubuntu imajında bulunmaz ve boş alanı olmayan bir dosya sistemine paket kurmak başarısız olabilir; bu nedenle /proc ile yapılan tarama, her durumda çalışan yöntemdir.
Yeniden başlatmadan disk alanını boşaltma
Yeniden başlatma işlemi sorunu çözer ancak bu ilk başvurulacak yöntem olmamalıdır: servis kesintiye uğrar ve kanıtlar yok olur. Daha güvenli dört seçenek mevcuttur; bunları sırasıyla deneyebilirsiniz.
Öncelikle, veriye hala ihtiyacınız varsa veriyi kopyalayın. Tanımlayıcı yolunu okumak, canlı inode verisini okur.
sudo cp /proc/<pid>/fd/<n> /root/recovered.logSilinen bir dosyanın geri getirilmesinin kolay olduğu tek durum budur; bu yüzden rm -rf ile silinen dosyaları kurtarma süreci, bir sürecin dosyayı hala açık tutup tutmadığını sorgulayarak başlar. Son tanımlayıcı kapandığında, bu yöntem artık kullanılamaz hale gelir.
İkinci olarak, dosyayı tanımlayıcı üzerinden boşaltın. /proc yolu aynı inode'a işaret eder, bu nedenle dosyayı kırpmak (truncate), süreç çalışmaya devam ederken blokları serbest bırakır.
sudo truncate -s 0 "/proc/$pid/fd/$n"
df -h /Yazıcı süreç dosyayı ekleme (append) modunda açtıysa bu yöntem temiz bir şekilde çalışır, çünkü her yazma işlemi dosyanın güncel sonuna yapılır. Eğer durum böyle değilse, süreç eski yazma ofsetini korur; bu durumda bir sonraki yazma işlemi dosyanın çok ilerisine düşer ve dosyanın başında bir boşluk (hole) oluşturur. Boşluklar diskte yer kaplamaz, bu nedenle bloklar boş kalır ve df az önce geri kazandığı alanı korur. Geri gelen sadece dosya boyutudur: süreç yazma yaptıktan sonra sudo stat -L "/proc/$pid/fd/$n" komutunu tekrar çalıştırırsanız, dosya boyutu ile blok sayısı artık eşleşmeyecektir. Boyutun sıfırdan başlamasını istiyorsanız süreci yeniden başlatın.
Üçüncü olarak, servisten günlük dosyalarını yeniden açmasını isteyin. Günlük dosyası altından silinmiş bir daemon, bu sorunun gerçek hayattaki en yaygın örneğidir. Birçok daemon, bir sinyal aldığında günlük dosyalarını yeniden açar: nginx için SIGUSR1, rsyslog için SIGHUP kullanılır. Tahmin etmek yerine ilgili daemon'ın dokümantasyonunu kontrol edin; yanlış daemon'a gönderilen yanlış bir sinyal servisi durdurabilir.
sudo systemctl kill -s USR1 nginxBu komut, sinyali systemd tarafından birimin ana süreci olarak kaydedilen sürece gönderir. Bu nedenle, daemon'ın nasıl başladığına dair yanlış Type= tanımlayan bir birim, sinyali silinen dosyayı tutmayan bir sürece iletebilir ve disk alanı boşalmaz.
Dördüncü olarak, birimi yeniden başlatın. sudo systemctl restart <unit>, eski sürecin tuttuğu tüm tanımlayıcıları kapatır, böylece bloklar kesin olarak geri kazanılır. Yukarıdaki örnekte dosyayı tutan süreç kendi başlattığınız bir sleep olduğu için, süreci sonlandırmak yeterlidir.
kill $holder
df -h .
df --output=used -B1 .
sudo find /proc/[0-9]*/fd -lname '*(deleted)' -printf '%p -> %l\n' 2>/dev/nullKullanılan bayt miktarını, dosyayı oluşturmadan önce kaydettiğiniz değerle karşılaştırın. Değerler tekrar eşleşecektir ve find artık tanımlayıcınızı raporlamayacaktır. Sorunu bulan komutla doğrulama yapmak, edinilmesi gereken iyi bir alışkanlıktır.
Bu değeri sürekli elle df çalıştırarak izlemek yerine takip etmek daha kolaydır. watch, bir komutu belirli aralıklarla tekrarlar ve çıktıyı aynı satırda günceller; bu sayede watch df -h / ile alan geri kazanıldıkça kullanılan sütunun değişimini izleyebilirsiniz.
Toplamlar eşleştiğinde ve disk hala doluysa
Eğer df ve root du -x birbiriyle eşleşiyorsa, silinmiş bir dosya sorunu yoktur. Geriye kalan nedenler farklı türdedir ve her birinin kendi kontrol yöntemi bulunur.
Bloklar değil, inode'lar tükendi
Bir inode, tek bir dosyanın meta verilerini tutar. ext4, dosya sistemi oluşturulduğunda sabit sayıda inode yaratır; bu nedenle bir dosya sistemi, boş blokları olmasına rağmen inode'ları tüketebilir. Bu durumda df -h boş alan olduğunu gösterse bile yeni dosyalar oluşturulamaz.
df -h /
df -i /İlk komut blokları, ikincisi ise inode'ları sayar. Her birinin kullanım sütununu karşılaştırın. Blok kullanımı düşükken inode kullanımı sınıra ulaşmışsa, sorun çok sayıda küçük dosyadan kaynaklanıyor demektir.
df, aynı çağrıda -i ve --output parametrelerini kabul etmez. Bu yüzden ham sayıları okumak veya başka bir komuta aktarmak istediğinizde, inode alanlarını isimleriyle seçin ve -i parametresini kullanmayın.
df --output=itotal,iused,iavail,ipcent /Bu sütunlar, df -i çıktısıyla aynı muhasebe verilerini, ayrıştırılabilir bir biçimde sunar.
Dosyaları bayt yerine giriş sayılarını hesaplayarak bulun.
sudo du --inodes -x -d 1 / 2>/dev/null | sort -rn | headAynı komutu, en çok dosya barındıran dizin üzerinde bir alt seviyeye inerek, dosyaları oluşturan ağaç yapısına ulaşana kadar tekrarlayın. Eğer du sürümünüz --inodes parametresini desteklemiyorsa, sudo find /var -xdev -type f | wc -l komutu alt ağacı yavaş yöntemle sayacaktır.
Çözüm, bu dosyaları silmek veya taşımaktır. Mevcut bir ext4 dosya sistemine sonradan inode ekleyemezsiniz; çünkü sayı mkfs anında sabitlenmiştir. Sayıyı artırmak, dosya sistemini yeniden oluşturmayı ve yedekten geri yükleme yapmayı gerektirir. XFS, inode'ları ihtiyaç duydukça tahsis eder, bu nedenle benzer bir sabit sınıra takılmaz. Konteyner çalıştıran bir makine, imaj katmanları çok sayıda küçük dosya barındırdığı için her iki sınıra da normalden daha hızlı ulaşır. Bu tür bir makinede VPS üzerinde Docker disk kullanımını temizleme işlemi özel çözümdür ve dosya sisteminde yapılacak genel bir taramadan çok daha fazla alan kazandırır.
Bağlama noktası altında gizli alan
Bir dizin, üzerine herhangi bir şey bağlanmadan önce dosyaları barındırabilir. Bu dizin üzerine bir dosya sistemi bağladığınızda, alttaki dosyalar oldukları yerde kalır: hala tahsis edilmiş durumdadırlar, df tarafından hala sayılırlar ancak artık isimleriyle erişilemezler. du, bağlama noktası onları örttüğü için bu dosyaları göremez.
Bunu, boş disk alanı gerektirmeyen tmpfs ile gösterin. Bu bölüm, bağlama yapma izninizin olduğu bir makine gerektirir, bu nedenle bir KVM VPS üzerinde çalışır.
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/coveredOrtadaki ls boş bir dizin gösterir. Kopyalama işlemi hiçbir yere gitmemiştir: hala kök dosya sistemindedir ve bağlantıyı kaldırdığınız anda geri döner. Şimdi, birisi üzerine bir birim bağlamadan önce bir ay boyunca bu yola günlük kaydı yapan bir servis hayal edin.
Çalışan bir sunucuda gerçek dosyaları bulmak için, kök dosya sistemini başka bir yere ikinci kez bağlayın. Bir bind mount, içine bağlanmış diğer dosya sistemleri olmadan tek bir dosya sistemini gösterir.
sudo mkdir -p /mnt/rootcheck
sudo mount --bind / /mnt/rootcheck
sudo du -xhs /mnt/rootcheck/* 2>/dev/null | sort -h
sudo umount /mnt/rootcheckBu listede görünen ancak normal yolun altında görünmeyen her şey, bir bağlama noktasının altında gömülüdür. İşiniz bittiğinde bind mount bağlantısını kaldırın; aksi takdirde -x içermeyen bir du, aynı dosyaları iki kez sayacaktır.
root kullanıcısı için ayrılmış bloklar
ext4, disk tamamen dolduğunda root kullanıcısının sisteme giriş yapmasını ve onarım gerçekleştirmesini engellememek için bloklarının bir kısmını root kullanıcısına ayırır. Normal bir kullanıcı tarafından çalıştırılan bir süreç bu sınıra ilk ulaşan olurken, df hala küçük bir boşluk olduğunu gösterir. Varsayılan değerleri varsaymak yerine kendi dosya sisteminizdeki ayarı kontrol edin.
dev=$(df --output=source / | tail -n 1)
sudo tune2fs -l "$dev" | grep -i 'block count'Bu komut, toplam blok sayısını ve ayrılmış blok sayısını aynı birimlerle yazdırır, böylece aralarındaki oran doğrudan görülebilir. df, kullanılabilir sütununu normal bir kullanıcının hala kullanabileceği alan olarak raporlar; bu nedenle kullanılan alan ile kullanılabilir alanın toplamı, toplam boyuttan daha küçük çıkar. Aradaki fark, ayrılmış alandır.
Bu değeri sudo tune2fs -m <percent> "$dev" ile değiştirin. Değişiklik anında uygulanır ve yeniden mount işlemi gerektirmez. Ayrı bir veri dosya sisteminde ayrılmış alanı düşürmek makuldür. Kök dosya sisteminde ise root kullanıcısının yazmaya devam edebilmesi için yeterli alan bırakın; çünkü tamamen dolu bir kök dosya sistemini onarmak çok daha zordur. Bu aynı zamanda sistemden dışarı kilitlenmenizi engelleyen unsurdur: boş alanı kalmamış bir dosya sisteminde authorized_keys dosyasına eklenen bir anahtar eksik yazılabilir veya hiç yazılamayabilir; bu durumda bir sonraki giriş denemesi, anahtarın kendisiyle ilgisi olmayan bir nedenle Permission denied (publickey) hatası verir. tune2fs, ext2, ext3 ve ext4 üzerinde çalışır. XFS dosya sisteminde buna karşılık gelen bir ayar bulunmamaktadır.
du komutunun tek başına yanıltıcı olduğu durumlar
du kullanımındaki dört alışkanlık, yanlış görünen toplamlar üretir.
- Sabit bağlantılar (Hard links):
du, birden fazla isim aynı inode'u işaret etse bile onu yalnızca bir kez sayar; bu nedenle sabit bağlantılarla dolu bir dizin ağacı, dosyalarının toplamından daha düşük bir değer bildirir. - Seyrek dosyalar (Sparse files):
du, fiilen ayrılmış blokları bildirirkenls -lgörünen boyutu bildirir. Diğer değeri görmek için--apparent-sizeekleyin. - İzinler: Sıradan bir kullanıcı olarak çalıştırıldığında
du, okuyamadığı dosyaları atlar ve eksik raporlama yapar. Yazdırdığı hatalar, insanların/dev/nulldosyasına yönlendirip okumayı bıraktığı hatalardır. - Dosya sistemi sınırları:
-xkullanılmadığındadu /,/altında bağlı olan her dosya sistemini sayar; bu yüzden toplam değer,df /raporundan daha yüksek çıkabilir.
df komutunun da bilinmesi gereken bir alışkanlığı vardır. Her dosya sistemini ayrı ayrı raporlar; bu nedenle komutu, başarısız yazma işleminin hedeflediği tam yol üzerinde çalıştırın. Çekirdek paketleri biriktikçe ayrı bir /boot kendi zamanlamasına göre dolar ve Ubuntu üzerinde eski çekirdekleri kaldırmak, / üzerindeki alanı temizlemekten farklı bir işlemdir.
Gerçek bir olayda izlenecek çalışma düzeni
- Başarısız yazma işleminin hedeflediği dosya sistemi üzerinde, refleks olarak
/üzerinde değil,df -h <path>vedf -i <path>komutlarını çalıştırın. sudo du -xh -d 1 <mountpoint> 2>/dev/null | sort -hkomutunu çalıştırın, ardından en büyük dizinin içine girin.dftarafından rapor edilen kullanım miktarınıduaçıklayamıyorsa,/procüzerinde silinmiş ancak hala açık olan dosyaları arayın.- Eğer ikisi birbiriyle uyumluysa, dosya sistemini başka bir yere bind mount edin ve bir mount noktasının altında kalan dosyaları kontrol edin.
- Sınırda olan inode kullanımı ise, bayt yerine dosya sayısını esas alın.
Buradaki her adım, çıktısını okuyabileceğiniz bir komuttur. Sorunu çözmek ile tahmin yürütmek arasındaki fark budur.
FAQ
Why does df show the disk as full when du finds much less?
The usual reason is a file that was deleted while a process still had it open. Removing the file removes its directory entry, so du has no name to walk and stops counting it. The inode and its blocks stay allocated until the last descriptor closes, and df counts allocated blocks. Search /proc/<pid>/fd for symbolic links whose target is marked as deleted and you have both the file and the process holding it. Before trusting the comparison, confirm you ran du as root and with -x, because an ordinary user silently skips directories it cannot read.
How do I find a deleted file that is still open without lsof?
Use the kernel's own record of open descriptors. sudo find /proc/[0-9]*/fd -lname '*(deleted)' -printf '%p -> %l\n' 2>/dev/null lists every descriptor pointing at a file with no name, and the process ID sits inside the path it prints. sudo stat -Lc %s on one of those descriptor paths reports its size, so you can sort them and pick the one that matters. This needs no package at all, which matters because installing one on a filesystem with no free space can fail.
Can I free the space without killing the process?
Sometimes. sudo truncate -s 0 /proc/<pid>/fd/<n> reaches the same inode through the descriptor and releases its blocks while the process keeps running. That is cleanest when the process opened the file in append mode, because its writes always go to the current end. If it did not, the write offset stays where it was and the next write recreates the file with a hole at the front, so the reported size springs back while the blocks under the hole stay free. Restarting the unit, or signalling it to reopen its logs with the signal its documentation names, is the fix that leaves no sparse file behind.
df shows free space but writes still fail. What else can it be?
Check inodes with df -i on the same path, since a filesystem with free blocks and no free inodes rejects new files. Check whether the write runs as a non-root user against an ext4 filesystem where only the reserved blocks are left, which sudo tune2fs -l on the device will show. Check that you are reading the filesystem the write actually targets, because a separate /boot or /var fills independently of /.
Why does du report a bigger total than df?
du without -x crosses into every filesystem mounted under the path you gave it, so it adds up several filesystems while df describes one. Bind mounts make it worse, because the same files are counted once under each path they appear at. Add -x to keep du on a single filesystem, and give df the same path, so both commands are describing the same thing.