Encuentra y elimina archivos grandes que están abiertos pero que han sido eliminados


120

¿Cómo se encuentran archivos grandes que se han eliminado pero que todavía están abiertos en una aplicación? ¿Cómo se puede eliminar dicho archivo, a pesar de que un proceso lo tiene abierto?

La situación es que estamos ejecutando un proceso que está llenando un archivo de registro a una velocidad excelente. Sé el motivo y puedo solucionarlo. Hasta entonces, me gustaría rm o vaciar el archivo de registro sin cerrar el proceso.

Simplemente hacerlo rm output.logelimina solo las referencias al archivo, pero continúa ocupando espacio en el disco hasta que finaliza el proceso. Peor aún: rm¡ahora no tengo forma de encontrar dónde está el archivo o qué tan grande es! ¿Hay alguna forma de encontrar el archivo y posiblemente vaciarlo, aunque todavía esté abierto en otro proceso?

Me refiero específicamente a los sistemas operativos basados ​​en Linux como Debian o RHEL.


2
Si conoce el pid, puede usarlo lsof -p <pid>para enumerar sus archivos abiertos y sus tamaños. El archivo eliminado tendrá un al (deleted)lado. El archivo eliminado se vinculará /proc/<pid>/fd/1probablemente. No sé cómo hacer que un proceso deje de escribir en su descriptor de archivo sin terminarlo. Creo que eso dependería del proceso.
donothings exitosamente

Gracias. ¿Cómo podría uno obtener los PID de todos los rmarchivos ed que todavía están abiertos?
dotancohen

@donothingsuccessfully La etiqueta "eliminada" informada por lsof es específica de Solaris, de hecho Solaris 10 o posterior solamente. El OP no especificó qué sistema operativo está usando. @dotancohen En Solaris puede canalizar la salida de lsof para buscar eliminadas, por ejemplo lsof | grep "(deleted)". Cuando no haya más procesos que mantengan abierto un archivo eliminado, el núcleo liberará el inodo y los bloques de disco. Los procesos no tienen "controladores" mediante los cuales se les puede notificar que un archivo abierto, esencialmente bloqueado, se ha eliminado del disco.
Johan

2
@Johan, también lsof | grep '(deleted)'funciona en Linux. En Linux, puede recibir notificaciones de eliminación de archivos (incluso archivos que ya no tienen ninguna entrada en ningún directorio que no sea / proc / some-pid / fd) con el mecanismo de inotificación (evento IN_DELETE_SELF)
Stéphane Chazelas

Lo creé somefiley lo abrí en VIM, luego lo rmedité en otro proceso bash. Luego ejecuto lsof | grep somefiley no está allí, a pesar de que el archivo está abierto en VIM.
dotancohen

Respuestas:


141

Si no puede eliminar su aplicación, puede truncar en lugar de eliminar el archivo de registro para recuperar el espacio. Si el archivo no estaba abierto en modo de agregado (con O_APPEND), el archivo aparecerá tan grande como antes la próxima vez que la aplicación lo escriba (aunque con la parte inicial dispersa y pareciendo que contenía bytes NUL), pero el espacio habrá sido reclamado (eso no se aplica a los sistemas de archivos HFS + en Apple OS / X que no admiten archivos dispersos).

Para truncarlo:

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

Si ya se eliminó, en Linux, aún puede truncarlo haciendo:

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

¿Dónde $pidestá la identificación del proceso que tiene el archivo abierto, y $fdun descriptor de archivo con el que lo tiene abierto (con el que puede consultar lsof -p "$pid".

Si no conoce el pid y está buscando archivos eliminados, puede hacer lo siguiente:

lsof -nP | grep '(deleted)'

lsof -nP +L1, como lo menciona @ user75021 es una opción aún mejor (más confiable y más portátil) (enumere los archivos que tienen menos de 1 enlace).

O (en Linux):

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

O para encontrar los grandes con zsh:

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

Una alternativa, si la aplicación está vinculada dinámicamente es adjuntarle un depurador y hacer que llame close(fd)seguido de un nuevo open("the-file", ....).


1
También hay un truncatecomando que hace lo mismo de manera más explícita.
Tobu

1
@dotancohen Stephane editó para incluir información sobre cómo hacer esto cuando no se conoce el pid.
Didi Kohen

1
@OlivierDulac, lsofprobablemente será la solución más cercana a una solución portátil que pueda obtener para enumerar los archivos abiertos. el enfoque del depurador para cerrar el fd debajo de los pies de la aplicación también debería ser bastante portátil.
Stéphane Chazelas

2
@StephaneChazelas: gracias. Encontré una manera de enumerar todos los PID que tienen un archivo abierto en cada partición: df -k | awk 'NR>1 { print $NF }' | xargs fuser -Vud (y luego es fácil enviar señales a los delincuentes para obligarlos a liberar el fd)
Olivier Dulac

66
También puedes usar lsof +L1. Desde la página de manual de lsof: "Una especificación del formulario +L1seleccionará los archivos abiertos que se han desvinculado. Una especificación del formulario +aL1 <file_system>seleccionará los archivos abiertos no vinculados en el sistema de archivos especificado". Eso debería ser un poco más confiable que grepping.
Synchro

31

Mira el inicio rápido aquí: lsofInicio rápido

Me sorprende que nadie haya mencionado el archivo de inicio rápido lsof (incluido con lsof). La sección "3.a" muestra cómo encontrar archivos abiertos no vinculados:

lsof -a +L1 *mountpoint*

P.ej:

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

En los sistemas Red Hat para encontrar la copia local del archivo de inicio rápido, generalmente hago esto:

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

... o esto:

[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

Depende del controlador del sistema de archivos liberar el espacio asignado, y eso generalmente sucederá solo una vez que se liberen todos los descriptores de archivo que se refieren a ese archivo. Por lo tanto, realmente no puede reclamar el espacio, a menos que haga que la aplicación cierre el archivo. Lo que significa terminarlo o jugar con él "un poco" en un depurador (por ejemplo, cerrar el archivo y asegurarse de que no se abre / escribe de nuevo, o se abre en su /dev/nulllugar). O podrías hackear el kernel, pero te aconsejaría que no.

Truncar el archivo como sugiere Stephane podría ayudar, pero el resultado real también dependerá de su sistema de archivos (por ejemplo, los bloques preasignados probablemente se liberarán solo después de cerrar el archivo en cualquier caso).

La razón detrás de este comportamiento es que el núcleo no sabría qué hacer con las solicitudes de datos (tanto de lectura como de escritura, pero la lectura es realmente más crítica) dirigida a dicho archivo.


2
Como Linux admite archivos dispersos en la mayoría de los sistemas de archivos, el comportamiento está bien definido y el controlador de disco realmente puede liberar espacio en disco. Lo he probado para ext3 y ext4, y funciona como escribió Stephane.
jofel

1
¿Qué te hace decir que truncar un archivo no reclamará bloques preasignados? Truncar está destinado a desasignar datos, no creo que haya ninguna ambigüedad con eso.
Stéphane Chazelas

1
El sistema de archivos puede mantener los bloques asignados para ahorrar tiempo más tarde (especialmente si el archivo aún permanece abierto), especialmente cuando era lo suficientemente grande antes de truncar. Al menos eso es lo que XFS parece estar haciendo.
Peter

Gracias Peter Me alegra que abordes el "por qué" en esta publicación.
dotancohen

2
Por lo que puedo decir, truncar los archivos abiertos también reclama espacio en XFS. Probado con el archivo normal y el archivo asignado con fallocateLinux 4.9. ¿Puede aclarar bajo qué sistema de archivos y condición truncar un archivo no reclama espacio?
Stéphane Chazelas
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.