Rechercher et supprimer des fichiers volumineux ouverts mais supprimés

120

Comment trouver des fichiers volumineux qui ont été supprimés mais qui sont encore ouverts dans une application? Comment peut-on supprimer un tel fichier, même si un processus l'a ouvert?

La situation est que nous exécutons un processus qui remplit un fichier journal à un rythme effarant. Je connais la raison et je peux y remédier. Jusque-là, j'aimerais supprimer ou vider le fichier journal sans arrêter le processus.

Cela ne rm output.logsupprime que les références au fichier, mais celui-ci continue à occuper de l'espace sur le disque jusqu'à la fin du processus. Pire: rmje n'ai plus aucun moyen de trouver où se trouve le fichier ni quelle est sa taille! Est-il possible de trouver le fichier et éventuellement de le vider, même s'il est toujours ouvert dans un autre processus?

Je fais spécifiquement référence aux systèmes d'exploitation basés sur Linux tels que Debian ou RHEL.

dotancohen
la source
2
Si vous connaissez le pid, vous pouvez utiliser lsof -p <pid>pour répertorier ses fichiers ouverts et leurs tailles. Le fichier supprimé aura un à (deleted)côté de lui. Le fichier supprimé sera lié à /proc/<pid>/fd/1probablement. Je ne sais pas comment faire en sorte qu'un processus arrête d'écrire dans son descripteur de fichier sans le terminer. Je pense que cela dépend du processus.
Donothingsu succés
Merci. Comment peut-on obtenir les PID de tous les rmfichiers ed qui sont encore ouverts?
dotancohen
@donothingsuccessfully Le tag "supprimé" signalé par lsof est spécifique à Solaris, en fait Solaris version 10 ou ultérieure uniquement. Le PO n'a pas précisé le système d'exploitation qu'il utilise. @ docancohen Sous Solaris, vous pouvez diriger la sortie de lsof pour rechercher les éléments supprimés, par exemple lsof | grep "(deleted)". Lorsqu'il n'y a plus de processus contenant un fichier supprimé ouvert, le noyau libère les blocs inode et disque. Les processus ne disposent pas de "gestionnaires" grâce auxquels ils peuvent être informés qu'un fichier ouvert, essentiellement verrouillé, a été supprimé du disque.
Johan
2
@Johan, cela lsof | grep '(deleted)'fonctionne aussi sous Linux. Sous Linux, vous pouvez être averti de la suppression de fichier (même les fichiers qui ne possèdent déjà aucune entrée dans un répertoire autre que / proc / some-pid / fd) avec le mécanisme inotify (événement IN_DELETE_SELF)
Stéphane Chazelas
Je l'ai créé somefileet ouvert dans VIM, puis rmédité dans un autre processus bash. Je lance alors lsof | grep somefileet il n'est pas là, même si le fichier est ouvert dans VIM.
dotancohen

Réponses:

141

Si vous ne pouvez pas tuer votre application, vous pouvez tronquer au lieu de supprimer le fichier journal pour récupérer de l'espace. Si le fichier n'était pas ouvert en mode d'ajout (avec O_APPEND), il apparaîtra aussi grand qu'avant la prochaine fois que l'application l'écrira (bien que la partie principale soit clairsemée et ressemble à si elle contenait des octets NUL), mais l'espace auront été récupérés (cela ne s'applique pas aux systèmes de fichiers HFS + sur Apple OS / X qui ne prennent pas en charge les fichiers fragmentés).

Pour le tronquer:

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

S'il a déjà été supprimé, sous Linux, vous pouvez toujours le tronquer en procédant comme suit:

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

$pidest l'id du processus dans lequel le fichier est ouvert et $fdun descripteur de fichier sous lequel il est ouvert (avec lequel vous pouvez vérifier lsof -p "$pid".

Si vous ne connaissez pas le pid et recherchez des fichiers supprimés, vous pouvez effectuer les opérations suivantes:

lsof -nP | grep '(deleted)'

lsof -nP +L1, comme mentionné par @ user75021, est une option encore meilleure (plus fiable et plus portable) (liste les fichiers qui ont moins d’un lien).

Ou (sous Linux):

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

Ou trouver les grands avec zsh:

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

Une autre solution, si l’application est liée dynamiquement, consiste à lui associer un débogueur et à lui faire appeler close(fd)suivi d’un nouveau open("the-file", ....).

Stéphane Chazelas
la source
1
Il existe également une truncatecommande qui fait la même chose de manière plus explicite.
Tobu
1
@dotancohen Stephane a édité pour inclure des informations sur la façon de procéder lorsque le pid n'est pas connu.
Didi Kohen
1
@OlivierDulac, lsofsera probablement la solution la plus proche d'une solution portable que vous pouvez obtenir pour lister les fichiers ouverts. l’approche du débogueur pour fermer le fd sous les pieds d’application devrait également être assez portable.
Stéphane Chazelas
2
@ StephaneChazelas: merci. J'ai trouvé un moyen de lister tous les PID qui ont un fichier ouvert sur chaque partition: df -k | awk 'NR>1 { print $NF }' | xargs fuser -Vud (et ensuite, d'envoyer facilement des signaux aux contrevenants pour les forcer à libérer le fd)
Olivier Dulac
6
Vous pouvez également utiliser lsof +L1. Dans la page de manuel lsof: "Une spécification du formulaire +L1sélectionnera les fichiers ouverts non liés. Une spécification du formulaire +aL1 <file_system>sélectionnera les fichiers ouverts non liés sur le système de fichiers spécifié.". Cela devrait être un peu plus fiable que grepping.
Synchro
31

Découvrez le quickstart ici: lsofQuickstart

Je suis surpris que personne n'ait mentionné le fichier de démarrage rapide lsof (inclus avec lsof). La section "3.a" montre comment trouver des fichiers ouverts et non liés:

lsof -a +L1 *mountpoint*

Par exemple:

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

Sur les systèmes Red Hat pour rechercher la copie locale du fichier de démarrage rapide, je procède généralement comme suit:

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

... ou ca:

[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
utilisateur75021
la source
1

Il appartient au pilote du système de fichiers de libérer l’espace alloué, ce qui ne se produit généralement que lorsque tous les descripteurs de fichier faisant référence à ce fichier sont publiés. Donc, vous ne pouvez pas vraiment récupérer l'espace, sauf si vous fermez le fichier par l'application. Ce qui signifie soit le terminer soit jouer avec "un peu" dans un débogueur (par exemple fermer le fichier et s'assurer qu'il n'est pas ouvert / écrit de nouveau, ou ouvrir à la /dev/nullplace). Ou vous pourriez pirater le noyau, mais je déconseillerais cela.

Tronquer le fichier comme suggéré par Stephane peut aider, mais le résultat réel dépendra également de votre système de fichiers (par exemple, les blocs pré-alloués ne seront probablement libérés qu'après la fermeture du fichier).

La raison derrière ce comportement est que le noyau ne sait pas quoi faire avec les demandes de données (en lecture et en écriture, mais la lecture est en réalité plus critique) en ciblant un tel fichier.

Peterph
la source
2
Comme Linux prend en charge les fichiers fragmentés sur la plupart des systèmes de fichiers, le comportement est bien défini et le pilote de disque peut réellement libérer de l'espace disque. Je l'ai testé pour ext3 et ext4, et cela fonctionne comme Stéphane l'a écrit.
Jofel
1
Qu'est-ce qui vous fait dire que tronquer un fichier ne récupérera pas les blocs préalloués? La troncature est destinée à désallouer des données, je ne pense pas qu'il y ait une ambiguïté avec cela.
Stéphane Chazelas
1
Le système de fichiers peut conserver les blocs alloués pour gagner du temps ultérieurement (en particulier si le fichier reste ouvert), en particulier s'il était suffisamment volumineux avant la troncature. Au moins c'est ce que XFS semble faire.
peterph
Merci Peter. Je suis heureux que vous abordiez le "pourquoi" dans ce post.
dotancohen
2
Autant que je sache, tronquer des fichiers ouverts permet également de récupérer de l'espace sur XFS. Testé avec le fichier normal et le fichier alloué avec fallocatesous Linux 4.9. Pouvez-vous clarifier sous quel système de fichiers et dans quelle condition tronquer un fichier ne récupère pas de l'espace?
Stéphane Chazelas