Encontre e remova arquivos grandes que estão abertos, mas foram excluídos

120

Como encontrar arquivos grandes que foram excluídos, mas ainda estão abertos em um aplicativo? Como remover um arquivo desse tipo, mesmo que um processo o abra?

A situação é que estamos executando um processo que está preenchendo um arquivo de log a uma taxa fantástica. Eu sei o motivo e posso corrigi-lo. Até então, eu gostaria de rm ou esvaziar o arquivo de log sem desligar o processo.

Simplesmente rm output.logremove apenas referências ao arquivo, mas continua ocupando espaço no disco até que o processo seja finalizado. Pior: depois de rming agora não tenho como descobrir onde o arquivo está ou qual o tamanho dele! Existe alguma maneira de encontrar o arquivo e possivelmente esvaziá-lo, mesmo que ainda esteja aberto em outro processo?

Refiro-me especificamente a sistemas operacionais baseados em Linux, como Debian ou RHEL.

dotancohen
fonte
2
Se você conhece o pid, pode lsof -p <pid>listar seus arquivos abertos e seus tamanhos. O arquivo excluído terá um (deleted)próximo a ele. O arquivo excluído será /proc/<pid>/fd/1provavelmente vinculado . Não sei como fazer com que um processo pare de gravar no descritor de arquivo sem finalizá-lo. Eu pensaria que isso dependeria do processo.
precisa saber é o seguinte
Obrigado. Como obter os PIDs de todos os rmarquivos ed que ainda estão abertos?
dotancohen
@donothingsuccessfully A tag "excluída" relatada por lsof é específica do Solaris, de fato, apenas o Solaris 10 ou posterior. O OP não especificou qual sistema operacional ele está usando. @dotancohen No Solaris, você pode canalizar a saída de lsof para procurar por excluídos, por exemplo lsof | grep "(deleted)". Quando não houver mais processos mantendo um arquivo excluído aberto, o kernel liberará os blocos de inode e disco. Os processos não têm "manipuladores" pelos quais eles podem ser notificados de que um arquivo aberto, essencialmente bloqueado, foi removido do disco.
27413 Johan Johan
2
@Johan, também lsof | grep '(deleted)'funciona no Linux. No Linux, você pode ser notificado sobre a exclusão de arquivos (mesmo os arquivos que já não têm mais nenhuma entrada em nenhum diretório que não seja o / proc / some-pid / fd) com o mecanismo inotify (evento IN_DELETE_SELF)
Stéphane Chazelas
Criei somefilee abri no VIM e rmeditei em outro processo do bash. Em seguida, corro lsof | grep somefilee ele não está lá, mesmo que o arquivo esteja aberto no VIM.
dotancohen

Respostas:

141

Se você não conseguir eliminar seu aplicativo, poderá truncar em vez de excluir o arquivo de log para recuperar o espaço. Se o arquivo não foi aberto no modo de acréscimo (com O_APPEND), o arquivo aparecerá tão grande quanto antes na próxima vez que o aplicativo gravar nele (embora com a parte principal esparsa e parecendo conter NUL bytes), mas o espaço terá sido recuperado (que não se aplica aos sistemas de arquivos HFS + no Apple OS / X que ainda não suportam arquivos esparsos).

Para truncá-lo:

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

Se já foi excluído, no Linux, você ainda pode truncá-lo fazendo:

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

Onde $pidestá a identificação do processo que abriu o arquivo e $fdum descritor de arquivo em que foi aberto (com o qual você pode verificar lsof -p "$pid".

Se você não conhece o pid e está procurando por arquivos excluídos, pode:

lsof -nP | grep '(deleted)'

lsof -nP +L1, como mencionado por @ user75021, é uma opção ainda melhor (mais confiável e mais portátil) (listar arquivos com menos de 1 link).

Ou (no Linux):

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

Ou para encontrar os grandes com zsh:

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

Uma alternativa, se o aplicativo estiver vinculado dinamicamente, é anexar um depurador a ele e fazer com que ele seja close(fd)seguido por um novo open("the-file", ....).

Stéphane Chazelas
fonte
11
Há também um truncatecomando que faz a mesma coisa mais explicitamente.
Tobu
11
@dotancohen Stephane editado para incluir informações sobre como fazer isso quando o pid não é conhecido.
precisa
11
@OlivierDulac, lsofprovavelmente será o mais próximo de uma solução portátil que você pode listar arquivos abertos. a abordagem do depurador para fechar o fd sob os pés do aplicativo também deve ser bastante portátil.
Stéphane Chazelas 20/03/2013
2
@StephaneChazelas: obrigado. Eu encontrei uma maneira para listar todos os PIDs que têm um arquivo aberto em cada partições: df -k | awk 'NR>1 { print $NF }' | xargs fuser -Vud (e então fácil para enviar sinais para os infratores para forçá-los a liberar a fd)
Olivier Dulac
6
Você também pode usar lsof +L1. Na página man lsof: "Uma especificação do formulário +L1selecionará arquivos abertos que foram desvinculados. Uma especificação do formulário +aL1 <file_system>selecionará arquivos abertos não vinculados no sistema de arquivos especificado.". Isso deve ser um pouco mais confiável que o grepping.
Synchro 23/10
31

Confira o início rápido aqui: lsofInício rápido

Estou surpreso que ninguém tenha mencionado o arquivo lsof quickstart (incluído no lsof). A seção "3.a" mostra como encontrar arquivos abertos e desvinculados:

lsof -a +L1 *mountpoint*

Por exemplo:

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

Nos sistemas Red Hat para encontrar a cópia local do arquivo de início rápido, geralmente faço o seguinte:

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

... ou isto:

[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
user75021
fonte
1

Depende do driver do sistema de arquivos liberar realmente o espaço alocado, e isso geralmente acontecerá apenas quando todos os descritores de arquivo referentes a esse arquivo forem liberados. Portanto, você não pode realmente recuperar o espaço, a menos que faça com que o aplicativo feche o arquivo. O que significa encerrá-lo ou brincar com ele "um pouco" em um depurador (por exemplo, fechando o arquivo e certificando-se de que ele não seja aberto / gravado novamente ou abrindo /dev/null). Ou você pode invadir o kernel, mas eu desaconselharia isso.

Truncar o arquivo como Stephane sugere pode ajudar, mas o resultado real também dependerá do seu sistema de arquivos (por exemplo, blocos pré-alocados provavelmente serão liberados somente depois que você fechar o arquivo, em qualquer caso).

A lógica por trás desse comportamento é que o kernel não saberia o que fazer com solicitações de dados (leitura e gravação, mas a leitura é realmente mais crítica) visando esse arquivo.

peterph
fonte
2
Como o Linux suporta arquivos esparsos na maioria dos sistemas de arquivos, o comportamento é bem definido e o driver de disco pode realmente liberar espaço em disco. Eu testei para ext3 e ext4, e funciona como Stephane escreveu.
Jofel
11
O que faz você dizer que truncar um arquivo não recuperará blocos pré-alocados? Truncar é destinado a desalocar dados, não acho que exista ambiguidade nisso.
Stéphane Chazelas 20/03/2013
11
O sistema de arquivos pode manter os blocos alocados para economizar tempo mais tarde (especialmente se o arquivo ainda permanecer aberto), especialmente quando ele era grande o suficiente antes de ser truncado. Pelo menos é o que o XFS parece estar fazendo.
Peterph
Obrigado Peter. Fico feliz que você abordou o "porquê" neste post.
dotancohen
2
Até onde eu sei, truncar arquivos abertos também recupera espaço no XFS. Testado com arquivos normais e alocados fallocateno Linux 4.9. Você pode esclarecer em que sistema de arquivos e condição o truncamento de um arquivo não recupera espaço?
Stéphane Chazelas