Hay tres cosas en un "archivo" en los sistemas de archivos POSIX:
- El conjunto de bloques de datos: el contenido real del archivo.
- El inode que es una estructura que contiene la lista de dichos bloques, y algunos metadatos (tamaño, propiedad, permisos, recuento de enlaces y algunos otros).
- Uno o más entradas de directorio que contienen un nombre y un número de inodo (y otras cosas)
Lo que se ve cuando se ejecuta ls
o en los exploradores de archivos son las entradas del directorio, organizadas en un árbol de directorios y archivos. Cada una de las entradas de 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 comprobar los permisos, etc.)
Cuando se crea un archivo, se crea un inodo con una cuenta inicial de enlaces de uno, y se establece una entrada de directorio con el nombre especificado, apuntando a ese inodo.
Si crea un duro se crea una segunda entrada en el directorio con el nombre elegido, pero apuntando al mismo inodo - ambas entradas de directorio se refieren al mismo inodo (es decir, ahora tiene dos nombres que se refieren al mismo archivo). La cuenta de enlaces del inodo se incrementa por cada nuevo enlace duro.
Cuando un proceso abre un archivo, utilizando un nombre de archivo, el núcleo hace la búsqueda de la entrada de directorio, encuentra el inodo y devuelve un descriptor de archivo que "hace referencia" al inodo, no a la entrada de directorio. La entrada del directorio es irrelevante una vez que el archivo ha sido abierto - es sólo una forma conveniente de localizar el inodo correcto.
Cuando se elimina un archivo (por ejemplo, utilizando rm
), en realidad no estás borrando el archivo, sino la entrada del directorio. El núcleo disminuye la cuenta de enlaces del inodo, pero no borra el inodo (y recupera espacio) a menos que:
- que la entrada del directorio era la última que apuntaba a él (es decir, el recuento de enlaces se reduce a cero - esto es lo que
lsof +L1
listas: abrir archivos completamente desvinculados)
- no quedan descriptores de archivo abiertos que hagan referencia a él
Así que los procesos pueden seguir operando en ese archivo, incluso si no hay manera de volver a él desde la exploración del sistema de archivos. Y puedes obtener aparentes inconsistencias de la salida de df
y du
por ejemplo:
df
interroga al sistema de archivos para ver cuántos bloques libres tiene. Los bloques de datos de los archivos "ocultos" sin más entradas en el directorio no están libres (todavía hay procesos que pueden leerlos/escribirlos), por lo que siguen ocupando espacio y seguirán ocupando ese espacio hasta que se cierre el último descriptor de archivo que hace referencia a ellos
du
enumera las entradas del directorio y suma los tamaños. No puede ver estos archivos no enlazados, y por lo tanto devolverá menos espacio utilizado que el sistema de archivos.
Si los archivos están en discos tradicionales, siguen ocupando espacio en el disco como archivos normales, aún vinculados. El IO se produce con normalidad. No tiene más requerimientos de memoria principal/empieza a comer RAM.
Si los archivos desvinculados pero abiertos están en un sistema de archivos respaldado por la memoria RAM, entonces siguen ocupando memoria, como lo hacían antes de ser desvinculados. (En ambos casos, los archivos también pueden crecer/reducirse).
El espacio se recuperará sólo cuando se cierre el último descriptor de archivo abierto. (Tenga en cuenta que los descriptores de archivo aún abiertos se cierran cuando un proceso sale o se termina de otra manera).
Si adjuntas un depurador a un programa que está usando archivos no enlazados, no verás nada particularmente interesante. Las llamadas de IO de los archivos se verán exactamente igual que las de los archivos normales, aún enlazados. No ocurre nada especial. Inspeccionando lo que se lee/escribe puedes obtener algunas ideas sobre para qué está usando el proceso estos archivos, pero eso es todo.
En cuanto al acceso a estos archivos, me temo que no conozco OS X lo suficiente como para decir si hay una manera fácil. El fdesc
pseudo-sistema de archivos parece que podría ser útil, pero aparentemente sólo le da acceso a los archivos del proceso actual.
Un ejemplo sencillo de cómo un proceso puede hacer esto, en perl. (Se puede hacer con casi cualquier lenguaje, incluyendo scripts).
Función de configuración y 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);
Resultado esperado:
$ 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.
Puedes mover el desenlace un poco (antes o después de las impresiones), no cambiará nada. No hay nada especial en el manejador de archivo después de la desvinculación, puede ser utilizado como cualquier otro manejador de archivo (siempre y cuando se mantenga abierto).