¿Qué significa en Linux que todo es archivo?
En un viejo artículo, hay una presentación en la que mencioné una frase bastante habitual en el mundo Unix/Linux: «Todo es archivo». Vamos a explicar qué significa realmente esa frase.
Todo es archivo
En realidad, ese dicho no es del todo correcto. Sin embargo, proviene de lo que hoy tal vez podríamos llamar la filosofía Unix. Probablemente, nadie dijo esa frase de manera literal en los primeros años de esa familia de sistemas operativos. Aun así, ideas de ese estilo estaban en la mente desde los comienzos y en los trabajos de por ejemplo, Bill Joy (pionero de TCP/IP). Linus Torvalds, no estuvo ausente en esta discusión. Y mucho más cerca en el tiempo, Lennart Poettering, retoma esa idea una vez más para defender el trabajo realizado en systemd. Por si no se dieron cuenta dice Poettering la interfaz de cgroups están expuesta en un pseudo-sistema de archivos...
Sin embargo, es más apropiado decir que todo es un descriptor de archivo o que todo puede tener un descriptor de archivo. En Linux un descriptor de archivo es un identificador único de proceso para un archivo u otro recurso de entrada o salida. Por ejemplo, no existe necesariamente un archivo para una interfaz de red. Sin embargo, existen sockets relacionados con ellas.
En el siguiente ejemplo podemos ver los descriptores de archivos para la dirección ip 127.0.0.1 de la interfaz lo.
sudo lsof -n -i@127.0.0.1 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME chronyd 1337 chrony 5u IPv4 8171 0t0 UDP 127.0.0.1:323 cupsd 1538 root 7u IPv4 14727 0t0 TCP 127.0.0.1:ipp (LISTEN) qemu-syst 1774 qemu 32u IPv4 19480 0t0 TCP 127.0.0.1:rfb (LISTEN) syncthing 2620 sergio 19u IPv4 22825 0t0 TCP 127.0.0.1:8384 (LISTEN)
En este caso, vemos los descriptores de archivos de lectura y escritura 5u, 7u, 32u y 19u.
Para ver la relación entre descriptores y sockets podemos hacer un ping a una determinada dirección ip:
ping -c 10000 9.9.9.9 &> /dev/null
Si buscamos los descriptores de archivos con lsfd, mostrará el siguiente resultado:
lsfd -p 914323 -Q '(FD >= 0)' COMMAND PID USER ASSOC XMODE TYPE SOURCE MNTID INODE NAME ping 914323 sergio 0 rw-D-- CHR pts:6 36 9 /dev/pts/6 ping 914323 sergio 1 -w---- CHR mem:3 34 4 /dev/null ping 914323 sergio 2 -w---- CHR mem:3 34 4 /dev/null ping 914323 sergio 3 rw---m PING sockfs 9 30437500 state=close id=35 laddr=0.0.0.0 ping 914323 sergio 4 rw---- PINGv6 sockfs 9 30437501 socket:[30437501]
Analicemos cada descriptor de archivo:
- 0: Es la "standard input" que apunta a una pseudoterminal (dispositivo de caracteres), al tratarlo como un archivo tiene permisos: de lectura y escritura. Usa un sistema de archivos y como tal también maneja inodos.
- 1: Es la "standard output" que apunta a /dev/null. Solamente tiene permisos de escritura, el sistema de archivos proviene de la memoria principal y tiene permisos solamente de escritura.
- 2: Es el "standard error" que apunta también a /dev/null.
- 3: Apunta a un socket de tipo IPv4, y tiene permisos de lectura y además está siendo utilizado por un evento de multiplexación.
- 4: Finalmente tenemos el descriptor de 5 que apunta a un tipo de socket que usa IPv6.
Dentro del directorio /proc podemos ver que los descriptores 1 (salida estándar) y 2 (error estándar) se redirigen al archivo /dev/null, mientras tanto, 3 y 4 apuntan a sockets:
ls -l /proc/914323/fd total 0 lrwx------ 1 sergio sergio 64 may 19 17:33 0 -> '/dev/pts/6 (deleted)' l-wx------ 1 sergio sergio 64 may 19 17:33 1 -> /dev/null l-wx------ 1 sergio sergio 64 may 19 17:33 2 -> /dev/null lrwx------ 1 sergio sergio 64 may 19 17:33 3 -> 'socket:[30437500]' lrwx------ 1 sergio sergio 64 may 19 17:33 4 -> 'socket:[30437501]'
Podemos hacer otra prueba, en este caso usaremos solamente ping sobre ipv6 y en lugar de redireccionar a /dev/null, lo haremos redirigiendo stdin y stderr a archivos regulares distintos:
ping6 -c 10000 ::1 > out.txt 2> errors.txt &
Aquí solamente usará un socket (ya que no usa ipv4):
lsfd -p 926928 -Q '(FD >= 0)' COMMAND PID USER ASSOC XMODE TYPE SOURCE MNTID INODE NAME ping6 926928 sergio 0 rw---- CHR pts:7 36 10 /dev/pts/7 ping6 926928 sergio 1 -w---- REG 0:34 52 38207299 /tmp/out.txt ping6 926928 sergio 2 -w---- REG 0:34 52 38207300 /tmp/errors.txt ping6 926928 sergio 3 rw---- PINGv6 sockfs 9 31193485 state=close id=44 laddr=::
¿Qué sucede si se borra /tmp/errors.txt?
lsfd -p 926928 -Q '(FD >= 0)' COMMAND PID USER ASSOC XMODE TYPE SOURCE MNTID INODE NAME ping6 926928 sergio 0 rw---- CHR pts:7 36 10 /dev/pts/7 ping6 926928 sergio 1 -w---- REG 0:34 52 38207299 /tmp/out.txt ping6 926928 sergio 2 -w-D-- REG 0:34 52 38207300 /tmp/errors.txt ping6 926928 sergio 3 rw---- PINGv6 sockfs 9 31193485 state=close id=44 laddr=::
Bueno, veremos que el archivo aparece con el flag D indicando que borró del respectivo sistema de archivos.
Por lo tanto, si bien literalmente no todo es archivo, podemos afirmar que en general podríamos mediante los descriptores de archivos establecer un puente entre un proceso y los recursos a los que está accediendo. En definitiva, un descriptor de archivos, permite al proceso comunicarse con el recurso subyacente de manera eficiente y uniforme, independientemente de si el recurso es un archivo en el sistema de archivos, un dispositivo de hardware o un socket de red.
Enlaces útiles
- Everything is a file, artículo de Wikipedia y es interesante leer la discusión sobre ese artículo, allí se menciona a Bill Joy
- Patrones de diseño en la historia de Unix
- Una charla en la que alguien manifiesta sus experiencias trabajando con Linux y el concepto de "todo es archivo", el expositor agrega además, experiencias sobre otros temas menos técnicos
- Linus Torvalds dando su punto de vista sobre el tema
- El creador de systemd haciendo mención a esta famosa tradición Unix
Comentarios
Comments powered by Disqus