Znajdź i usuń duże pliki, które są otwarte, ale zostały usunięte

119

Jak znaleźć duże pliki, które zostały usunięte, ale nadal są otwarte w aplikacji? Jak usunąć taki plik, nawet jeśli proces go otworzył?

Sytuacja polega na tym, że uruchamiamy proces, który wypełnia plik dziennika w niesamowitym tempie. Znam przyczynę i mogę to naprawić. Do tego czasu chciałbym rm lub opróżnić plik dziennika bez zamykania procesu.

Po prostu rm output.logusuwa tylko odniesienia do pliku, ale nadal zajmuje miejsce na dysku, dopóki proces się nie zakończy. Gorzej: po rming nie mogę teraz znaleźć, gdzie jest plik ani jak duży jest! Czy jest jakiś sposób, aby znaleźć plik i ewentualnie go opróżnić, nawet jeśli nadal jest otwarty w innym procesie?

Mam na myśli szczególnie systemy operacyjne oparte na Linuksie, takie jak Debian lub RHEL.

dotancohen
źródło
2
Jeśli znasz pid, możesz użyć, lsof -p <pid>aby wyświetlić listę jego otwartych plików i ich rozmiarów. Usunięty plik będzie miał (deleted)obok niego. Usunięte pliki zostaną prawdopodobnie połączone /proc/<pid>/fd/1. Nie wiem, jak sprawić, aby proces przestał zapisywać do swojego deskryptora pliku, nie przerywając go. Myślę, że to zależy od procesu.
darowizny z powodzeniem
Dzięki. Jak uzyskać PID wszystkich rmplików ed, które są nadal otwarte?
dotancohen
@donothingsuccessfully Tag „usunięty” zgłoszony przez lsof jest specyficzny dla systemu Solaris, w rzeczywistości tylko dla systemu Solaris 10 lub nowszego. OP nie określił, jakiego systemu operacyjnego używa. @dotancohen W systemie Solaris można przesyłać dane wyjściowe polecenia lsof w celu wyszukiwania usuniętych, np lsof | grep "(deleted)". Gdy nie będzie już żadnych procesów, w których usunięty plik jest otwarty, jądro zwolni bloki i-węzła i dysku. Procesy nie mają „programów obsługi”, za pomocą których można ich powiadomić, że otwarty, zasadniczo zablokowany plik, został usunięty z dysku.
Johan
2
@Johan, lsof | grep '(deleted)'działa również w systemie Linux. W systemie Linux możesz zostać powiadomiony o usunięciu pliku (nawet pliki, które nie mają już żadnego wpisu w katalogu innym niż / proc / some-pid / fd) za pomocą mechanizmu inotify (zdarzenie IN_DELETE_SELF)
Stéphane Chazelas
Stworzyłem somefilei otworzyłem go w VIM, a następnie rmedytowałem w innym procesie bash. Następnie uruchamiam lsof | grep somefilei nie ma go tam, mimo że plik jest otwarty w VIM.
dotancohen

Odpowiedzi:

141

Jeśli nie możesz zabić aplikacji, możesz skrócić zamiast usuwać plik dziennika, aby odzyskać miejsce. Jeśli plik nie był otwarty w trybie dołączania (z O_APPEND), plik będzie wyglądał na tak duży, jak przed następnym razem, gdy aplikacja do niego napisze (choć z wiodącą częścią rzadką i wyglądającą, jakby zawierała NUL bajtów), ale spacja zostanie odzyskany (nie dotyczy to systemów plików HFS + w systemie Apple OS / X, które nie obsługują plików rzadkich).

Aby go skrócić:

: > /path/to/the/file.log

Jeśli został już usunięty, w systemie Linux nadal można go obciąć, wykonując:

: > "/proc/$pid/fd/$fd"

Gdzie $pidjest identyfikator procesu, w którym plik jest otwarty, i $fdjeden deskryptor pliku, pod którym jest otwarty (pod którym można to sprawdzić) lsof -p "$pid".

Jeśli nie znasz pid i szukasz usuniętych plików, możesz:

lsof -nP | grep '(deleted)'

lsof -nP +L1, jak wspomniano w @ user75021, jest jeszcze lepszą (bardziej niezawodną i przenośną) opcją (lista plików, które mają mniej niż 1 link).

Lub (w systemie Linux):

find /proc/*/fd -ls | grep  '(deleted)'

Lub znaleźć duże z zsh:

ls -ld /proc/*/fd/*(-.LM+1l0)

Alternatywą, jeśli aplikacja jest dynamicznie połączona, jest dołączenie do niej debuggera i wywołanie close(fd)nowego, a następnie wywołanie nowego open("the-file", ....).

Stéphane Chazelas
źródło
1
Istnieje również truncatepolecenie, które robi to samo wyraźniej.
Tobu,
1
@dotancohen Stephane edytowany, aby zawierał informacje o tym, jak to zrobić, gdy pid nie jest znany.
Didi Kohen
1
@OlivierDulac, lsofprawdopodobnie będzie najbliżej przenośnego rozwiązania, które można uzyskać, aby wyświetlić listę otwartych plików. podejście debugera do zamykania fd pod nóżkami aplikacji powinno być również dość przenośne.
Stéphane Chazelas,
2
@StephaneChazelas: dzięki. Znalazłem sposób na wylistowanie wszystkich PID, które mają plik otwarty na każdej partycji: df -k | awk 'NR>1 { print $NF }' | xargs fuser -Vud (a następnie łatwe wysyłanie sygnałów do przestępców w celu zmuszenia ich do wydania fd)
Olivier Dulac
6
Możesz także użyć lsof +L1. Na stronie podręcznika lsof: „Specyfikacja formularza +L1wybierze otwarte pliki, które zostały odłączone. Specyfikacja formularza +aL1 <file_system>wybierze otwarte pliki w określonym systemie plików.”. To powinno być nieco bardziej niezawodne niż grepowanie.
Synchro
30

Sprawdź szybki start tutaj: lsofSzybki start

Dziwi mnie, że nikt nie wspomniał o pliku szybkiego startu lsof (dołączonym do lsof). Sekcja „3.a” pokazuje, jak znaleźć otwarte, niepowiązane pliki:

lsof -a +L1 *mountpoint*

Na przykład:

[root@enterprise ~]# lsof -a +L1 /tmp
COMMAND   PID   USER   FD   TYPE DEVICE    SIZE NLINK  NODE NAME
httpd    2357 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
mysqld   2588  mysql    4u   REG 253,17      52     0  1495 /tmp/ibY0cXCd (deleted)
mysqld   2588  mysql    5u   REG 253,17    1048     0  1496 /tmp/ibOrELhG (deleted)
mysqld   2588  mysql    6u   REG 253,17       0     0  1497 /tmp/ibmDFAW8 (deleted)
mysqld   2588  mysql    7u   REG 253,17       0     0 11387 /tmp/ib2CSACB (deleted)
mysqld   2588  mysql   11u   REG 253,17       0     0 11388 /tmp/ibQpoZ94 (deleted)
httpd    3457   root   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8437 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8438 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8439 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8440 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8441 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8442 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8443 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8444 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   16990 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   19595 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   27495 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   28142 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   31478 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)

W systemach Red Hat, aby znaleźć lokalną kopię pliku szybkiego uruchamiania, zwykle robię to:

[root@enterprise ~]# locate -i quickstart |grep lsof
/usr/share/doc/lsof-4.78/00QUICKSTART

... albo to:

[root@enterprise ~]# rpm -qd lsof
/usr/share/doc/lsof-4.78/00.README.FIRST
/usr/share/doc/lsof-4.78/00CREDITS
/usr/share/doc/lsof-4.78/00DCACHE
/usr/share/doc/lsof-4.78/00DIALECTS
/usr/share/doc/lsof-4.78/00DIST
/usr/share/doc/lsof-4.78/00FAQ
/usr/share/doc/lsof-4.78/00LSOF-L
/usr/share/doc/lsof-4.78/00MANIFEST
/usr/share/doc/lsof-4.78/00PORTING
/usr/share/doc/lsof-4.78/00QUICKSTART
/usr/share/doc/lsof-4.78/00README
/usr/share/doc/lsof-4.78/00TEST
/usr/share/doc/lsof-4.78/00XCONFIG
/usr/share/man/man8/lsof.8.gz
użytkownik75021
źródło
1

To od sterownika systemu plików zależy, czy rzeczywiście zwolni przydzielone miejsce, i zwykle dzieje się to dopiero po zwolnieniu wszystkich deskryptorów plików odnoszących się do tego pliku. Tak więc nie można odzyskać miejsca, chyba że aplikacja zamknie plik. Co oznacza albo jego zakończenie, albo zabawę „trochę” w debuggerze (np. Zamknięcie pliku i upewnienie się, że nie zostanie ponownie otwarty / zapisany, lub otwarcie /dev/nullzamiast niego). Możesz też zhakować jądro, ale odradzałbym to.

Obcinanie pliku, jak sugeruje Stephane, może pomóc, ale rzeczywisty wynik zależy również od systemu plików (na przykład wstępnie przydzielone bloki zostaną prawdopodobnie zwolnione dopiero po zamknięciu pliku).

Uzasadnieniem tego zachowania jest to, że jądro nie wiedziałoby, co zrobić z żądaniami danych (zarówno do odczytu, jak i do zapisu, ale w rzeczywistości czytanie jest bardziej krytyczne) ukierunkowane na taki plik.

Peter
źródło
2
Ponieważ Linux obsługuje rzadkie pliki w większości systemów plików, zachowanie jest dobrze zdefiniowane, a sterownik dysku może naprawdę zwolnić miejsce na dysku. Przetestowałem to dla ext3 i ext4 i działa tak, jak napisał Stephane.
jofel
1
Co sprawia, że ​​mówisz, że obcięcie pliku nie spowoduje odzyskania wstępnie przydzielonych bloków? Obcinanie ma na celu dezalokację danych, nie sądzę, że jest w tym dwuznaczność.
Stéphane Chazelas
1
System plików może zachować przydzielone bloki, aby zaoszczędzić czas później (szczególnie jeśli plik nadal pozostaje otwarty), zwłaszcza gdy był wystarczająco duży przed obcięciem. Tak przynajmniej wygląda XFS.
peterph
Dziękuję Peter. Cieszę się, że w tym poście zwróciłeś się do „dlaczego”.
dotancohen
2
O ile mi wiadomo, obcinanie otwartych plików również zwalnia miejsce na XFS. Testowany z normalnym plikiem i plikiem przydzielonym fallocatew systemie Linux 4.9. Czy możesz wyjaśnić, w jakim systemie plików i warunkach obcinanie pliku nie zajmuje miejsca?
Stéphane Chazelas