Trova e rimuovi file di grandi dimensioni aperti ma che sono stati eliminati


120

Come si trovano file di grandi dimensioni che sono stati eliminati ma che sono ancora aperti in un'applicazione? Come si può rimuovere un tale file, anche se un processo lo ha aperto?

La situazione è che stiamo eseguendo un processo che sta riempiendo un file di registro a una velocità eccezionale. Conosco il motivo e posso risolverlo. Fino ad allora, vorrei rm o svuotare il file di registro senza chiudere il processo.

Semplicemente rm output.logrimuove solo i riferimenti al file, ma continua a occupare spazio sul disco fino al termine del processo. Peggio ancora: dopo rming non ho più modo di trovare dove sia il file o quanto sia grande! C'è un modo per trovare il file e possibilmente svuotarlo, anche se è ancora aperto in un altro processo?

Mi riferisco specificamente a sistemi operativi basati su Linux come Debian o RHEL.


2
Se conosci il pid, puoi usarlo lsof -p <pid>per elencare i suoi file aperti e le loro dimensioni. Il file eliminato avrà un (deleted)accanto. Il file eliminato sarà /proc/<pid>/fd/1probabilmente collegato . Non so come interrompere la scrittura di un processo nel suo descrittore di file senza interromperlo. Penserei che dipenderebbe dal processo.
donothings successo

Grazie. Come si possono ottenere i PID di tutti i rmfile ed che sono ancora aperti?
dotancohen,

@donothingsuccessfully Il tag "cancellato" riportato da lsof è specifico di Solaris, in realtà solo Solaris 10 o versioni successive. L'OP non ha specificato quale sistema operativo sta utilizzando. @dotancohen Su Solaris è possibile reindirizzare l'output di lsof per cercare l'eliminazione, ad es lsof | grep "(deleted)". Quando non ci sono più processi che tengono aperto un file cancellato, il kernel libererà i blocchi di inode e disco. I processi non hanno "gestori" con cui possono essere avvisati che un file aperto, essenzialmente bloccato, è stato rimosso dal disco.
Johan

2
@Johan, lsof | grep '(deleted)'funziona anche su Linux. Su Linux, puoi essere avvisato della cancellazione dei file (anche i file che non hanno già alcuna voce in alcuna directory diversa da / proc / some-pid / fd) con il meccanismo di inotify (evento IN_DELETE_SELF)
Stéphane Chazelas

L'ho creato somefilee aperto in VIM, quindi l' rmho modificato in un altro processo bash. Poi corro lsof | grep somefilee non è lì, anche se il file è aperto in VIM.
dotancohen,

Risposte:


141

Se non è possibile uccidere l'applicazione, è possibile troncare invece di eliminare il file di registro per recuperare lo spazio. Se il file non era aperto in modalità append (con O_APPEND), il file apparirà grande come prima la prossima volta che l'applicazione vi scrive (sebbene con la parte iniziale sparsa e guardando come se contenesse byte NUL), ma lo spazio sarà stato recuperato (che non si applica ai file system HFS + su Apple OS / X che non supportano i file sparsi).

Per troncarlo:

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

Se è già stato eliminato, su Linux, puoi ancora troncarlo facendo:

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

Dove si $pidtrova l'id del processo in cui è aperto il file e in cui è stato aperto $fdun descrittore di file (con il quale è possibile verificare lsof -p "$pid".

Se non conosci il pid e stai cercando file eliminati, puoi fare:

lsof -nP | grep '(deleted)'

lsof -nP +L1, come menzionato da @ user75021 è un'opzione ancora migliore (più affidabile e più portatile) (elenca i file che hanno meno di 1 collegamento).

Oppure (su Linux):

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

O per trovare quelli grandi con zsh:

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

Un'alternativa, se l'applicazione è collegata dinamicamente, è quella di collegare un debugger e farla chiamare close(fd)seguita da una nuova open("the-file", ....).


1
C'è anche un truncatecomando che fa la stessa cosa in modo più esplicito.
Tobu,

1
@dotancohen Stephane è stato modificato per includere informazioni su come eseguire questa operazione quando il pid non è noto.
Didi Kohen,

1
@OlivierDulac, lsofprobabilmente sarà il più vicino a una soluzione portatile che puoi ottenere per elencare i file aperti. anche l'approccio del debugger per chiudere il file fd sotto i piedi dell'applicazione dovrebbe essere abbastanza portatile.
Stéphane Chazelas,

2
@StephaneChazelas: grazie. Ho trovato un modo per elencare tutti i PID che hanno un file aperto su ciascuna partizione: df -k | awk 'NR>1 { print $NF }' | xargs fuser -Vud (e quindi facile inviare segnali ai trasgressori per costringerli a rilasciare il fd)
Olivier Dulac

6
Puoi anche usare lsof +L1. Dalla pagina man lsof: "Una specifica del modulo +L1selezionerà i file aperti che sono stati scollegati. Una specifica del modulo +aL1 <file_system>selezionerà i file aperti non collegati sul file system specificato.". Dovrebbe essere un po 'più affidabile del grepping.
Synchro,

31

Dai un'occhiata alla guida rapida qui: lsofGuida rapida

Sono sorpreso che nessuno abbia menzionato il file di avvio rapido di lsof (incluso in lsof). La sezione "3.a" mostra come trovare file aperti e non collegati:

lsof -a +L1 *mountpoint*

Per esempio:

[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)

Sui sistemi Red Hat per trovare la copia locale del file di avvio rapido, di solito faccio questo:

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

... o questo:

[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

1

Spetta al driver del file system liberare effettivamente lo spazio allocato, e ciò accade di solito solo dopo il rilascio di tutti i descrittori di file che fanno riferimento a quel file. Quindi non puoi davvero recuperare lo spazio, a meno che tu non faccia chiudere l'applicazione al file. Il che significa o terminarlo o giocarci "un po '" in un debugger (ad es. Chiudere il file e assicurarsi che non venga aperto / scritto di nuovo, o /dev/nullinvece aprirlo ). Oppure potresti hackerare il kernel, ma ti sconsiglio.

Troncare il file come suggerisce Stephane potrebbe aiutare, ma il risultato reale dipenderà anche dal tuo file system (ad esempio i blocchi pre-allocati verranno probabilmente liberati solo dopo aver chiuso il file in ogni caso).

La logica alla base di questo comportamento è che il kernel non saprebbe cosa fare con le richieste di dati (sia in lettura che in scrittura, ma la lettura è in realtà più critica) indirizzata a tale file.


2
Poiché Linux supporta file sparsi sulla maggior parte dei file system, il comportamento è ben definito e il driver del disco può davvero liberare spazio su disco. L'ho testato per ext3 ed ext4, e funziona come ha scritto Stephane.
jofel

1
Cosa ti fa dire che troncare un file non recupererà i blocchi preallocati? Troncare ha lo scopo di deallocare i dati, non ho alcuna ambiguità con quello.
Stéphane Chazelas,

1
Il file system può mantenere i blocchi allocati per risparmiare tempo in seguito (soprattutto se il file rimane ancora aperto), specialmente quando era abbastanza grande prima di troncarsi. Almeno questo è ciò che sembra fare XFS.
peterph

Grazie Peter. Sono lieto che tu abbia affrontato il "perché" in questo post.
dotancohen,

2
Per quanto ne so, il troncamento dei file aperti consente di recuperare spazio anche su XFS. Testato con file normale e file allocato con fallocatesu Linux 4.9. Potete chiarire in quali file system e condizioni il troncamento di un file non richiede spazio?
Stéphane Chazelas,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.