¿Cuál es el significado del comando lsof + L1?

2
lsof +L1

Tengo entendido que el comando anterior muestra los archivos que se han eliminado pero aún están abiertos. Estos archivos tienen un "recuento de enlaces" de 0, lo que significa que no hay ninguna entrada de directorio (ningún enlace, es decir, ningún nombre) que lleve a ellos, pero los datos del archivo aún existen. Los datos del archivo se eliminarán cuando se cierre el archivo. Por lo general, veo referencias a este problema cuando las personas hablan de los archivos de registro en los que el sistema está escribiendo. El archivo se puede eliminar, pero el proceso continúa escribiendo en el archivo y ocupa más y más memoria. Otras personas han identificado esto a veces como un comportamiento malicioso de malware que intenta ocultarse.

¿Estoy tratando de entender cómo puede pasar esto? Cuando abro un archivo para leer o escribir en Python, el archivo existe en el disco. Intenté leer los documentos relevantes de Xcode en el acceso a archivos, pero es muy denso. ¿Hay diferentes tipos de archivos: los de RAM y los de disco?

Estos son archivos con nombre, por lo que no es la memoria utilizada por la aplicación. Por ejemplo, ejecutar este comando en mi mac revela aproximadamente 300 de estos archivos. los procesos 'ofensivos' son cosas como loginwindow, dock, systemUIS, sharingd, CalendarA y muchos otros. Si esto es algo que un proceso está escribiendo en la RAM, ¿puedo 'adjuntarme' al proceso con gdb y ver qué está haciendo? ¿Puedo escribir estos archivos en el disco para leerlos?

    
pregunta aquagremlin 04.10.2014 - 22:47

1 respuesta

9

Hay tres cosas en un "archivo" en los sistemas de archivos POSIX:

  • El conjunto de bloques de datos: el contenido real del archivo.
  • El inodo , que es una estructura que contiene la lista de dichos bloques y algunos metadatos (tamaño, propiedad, permisos, recuento de enlaces y algunos otros).
  • Una o más entradas de directorio , que contienen un nombre y un número de inodo (y otras cosas)

Lo que ve cuando ejecuta ls o en los navegadores de archivos son las entradas de directorio, organizadas en un árbol de directorios y archivos. Cada una de las entradas del directorio asigna el nombre del archivo a un número de inodo. El número de inodo se utiliza para localizar el inodo, que se utiliza para localizar los bloques reales (y verificar los permisos, etc.)

Cuando crea un archivo, se crea un inodo con un recuento de enlace inicial de uno, y se configura una entrada de directorio con el nombre que especificó, apuntando a ese inodo.

Si crea un enlace hard , se crea una segunda entrada de directorio con el nombre que eligió, pero apuntando al inem same : ambas entradas de directorio se refieren a la misma inode (es decir, ahora tiene dos nombres que se refieren al mismo archivo). El recuento de enlaces del inodo se incrementa para cada nuevo enlace físico.

Cuando un proceso abre un archivo, utilizando un nombre de archivo, el núcleo realiza la búsqueda de entradas de directorio, encuentra el inodo y devuelve un descriptor de archivo que "se refiere" al inodo, no a la entrada de directorio. La entrada del directorio es irrelevante una vez que se ha abierto el archivo; es solo una forma conveniente de ubicar el inodo correcto.

Cuando eliminas un archivo (por ejemplo, utilizando rm ), en realidad no estás eliminando el archivo, estás eliminando la entrada del directorio. El kernel disminuye el número de enlaces del inodo, pero no elimina el inodo (y reclama espacio) a menos que:

  • la entrada del directorio fue la última que lo señaló (es decir, el recuento de enlaces se redujo a cero; esto es lo que lsof +L1 enumera: archivos abiertos que están completamente desvinculados)
  • no hay descriptores de archivo abiertos restantes que se refieran a él

Por lo tanto, los procesos pueden continuar operando en ese archivo, incluso si no hay manera de volver a él desde el sistema de archivos. Y puede obtener inconsistencias aparentes de la salida de df y du por ejemplo:

  • df interroga el sistema de archivos para ver cuántos bloques libres tiene. Los bloques de datos de los archivos "ocultos" que ya no tienen entradas en el directorio no son libres (todavía hay procesos que pueden leerlos / escribirlos), por lo que aún ocupan espacio y continuarán ocupando ese espacio hasta el último descriptor de archivo que se refiere a ellos. está cerrado
  • du enumera las entradas de directorio y resume los tamaños. No puede ver estos archivos no vinculados y, por lo tanto, devolverá menos espacio utilizado que el sistema de archivos.

Si los archivos están en discos tradicionales, continúan ocupando espacio en el disco al igual que los archivos normales, aún vinculados. IO sucede como normal. No tiene más requisitos de memoria principal / empieza a comer RAM.

Si los archivos no vinculados pero abiertos están en un sistema de archivos respaldado por RAM, entonces continúan ocupando memoria, como lo hicieron antes de ser desvinculados. (En ambos casos, los archivos también pueden crecer / reducirse).

El espacio se reclamará solo cuando se cierre el último descriptor de archivo abierto. (Tenga en cuenta que los descriptores de archivos aún abiertos se cierran cuando un proceso sale o finaliza de otro modo).

Si adjunta un depurador a un programa que utiliza archivos no vinculados, no verá nada particularmente interesante. Las llamadas de archivos IO se verán exactamente igual que los archivos normales, aún vinculados. No pasa nada especial allí. Al inspeccionar lo que está leído / escrito, puede obtener algunas ideas sobre para qué sirve el proceso de estos archivos, pero eso es todo.

En cuanto al acceso a estos archivos, me temo que no conozco OS X lo suficiente como para saber si hay una manera fácil. El pseudo-sistema de archivos se parece al fdesc podría ser útil, pero aparentemente solo le da acceso a los archivos del proceso actual.

Un ejemplo simple de cómo un proceso puede hacer esto, en perl. (Se puede hacer con casi cualquier idioma, incluidos los scripts de shell).

Configuración y función de ayuda:

#! /usr/bin/perl
use strict;
use warnings;
use Fcntl qw(SEEK_SET); # for rewinding

my $fh;                 # file descriptor/handle
my $test_file = "./test_file";

sub status {            # checks if the file is "visible"
  my @st = stat($test_file);
  if (@st) {
    print "$test_file: file exists\n";
  } else {
    print "$test_file: error: $!\n";
  }     
}

La parte principal:

# open file in read/write mode, creating it if it doesn't exist
# (overwriting it if it does)
if (!open($fh, '+>', $test_file)) {
  die "Failed to open $test_file: $!";
}
print $fh "Some data before unlink.\n";
status();
unlink($test_file);
status();
print $fh "Some data after unlink.\n";

# Rewind
seek($fh, 0, SEEK_SET);
# Print file contents
foreach my $line (<$fh>) {
  print "read: $line";
}
# Close
close($fh);

Salida esperada:

 $ perl test.pl
./test_file: file exists
./test_file: error: No such file or directory
read: Some data before unlink.
read: Some data after unlink.

Puede mover el desvío un poco (antes o después de las impresiones), no cambiará nada. No hay nada especial en el identificador de archivos después del desvincular, se puede usar como cualquier otro manejador de archivos (siempre que se mantenga abierto).

    
respondido por el Mat 05.10.2014 - 00:12

Lea otras preguntas en las etiquetas