¿Qué son los directorios, si todo en Linux es un archivo?

17

Muy a menudo los principiantes escuchan una frase "Todo es un archivo en Linux / Unix". Sin embargo, ¿cuáles son los directorios entonces? ¿Cómo son diferentes de los archivos?

Sergiy Kolodyazhnyy
fuente

Respuestas:

22

Nota: originalmente esto se escribió para respaldar mi respuesta para ¿Por qué el directorio actual en el lscomando se identifica como vinculado a sí mismo? pero sentí que este es un tema que merece ser independiente, y de ahí este Q&A .

Comprensión del sistema de archivos y archivos de Unix / Linux: todo es un inodo

Esencialmente, un directorio es solo un archivo especial, que contiene una lista de entradas y su ID.

Antes de comenzar la discusión, es importante hacer una distinción entre algunos términos y comprender qué representan realmente los directorios y archivos. Es posible que haya escuchado la expresión "Todo es un archivo" para Unix / Linux. Bueno, lo que los usuarios a menudo entienden como archivo es esto: /etc/passwd- Un objeto con una ruta y un nombre. En realidad, un nombre (ya sea un directorio o archivo, o cualquier otra cosa) es solo una cadena de texto, una propiedad del objeto real. Ese objeto se llama inodo o número I, y se almacena en el disco en la tabla de inodo. Los programas abiertos también tienen tablas de inodo, pero esa no es nuestra preocupación por ahora.

La noción de Unix de un directorio es como Ken Thompson lo expresó en una entrevista de 1989 :

... Y luego algunos de esos archivos, eran directorios que solo contenían el nombre y el número I.

Se puede hacer una observación interesante de la charla de Dennis Ritchie en 1972 que

"... el directorio no es más que un archivo, pero su contenido está controlado por el sistema y los contenidos son nombres de otros archivos. (Un directorio a veces se denomina catálogo en otros sistemas)".

... pero no hay mención de inodes en ninguna parte de la charla. Sin embargo, el manual de 1971 sobre format of directoriesestados:

El hecho de que un archivo sea un directorio se indica mediante un bit en la palabra de bandera de su entrada i-node.

Las entradas del directorio tienen 10 bytes de longitud. La primera palabra es el nodo i del archivo representado por la entrada, si no es cero; si es cero, la entrada está vacía.

Así que ha estado allí desde el principio.

El emparejamiento de directorios e inodos también se explica en ¿Cómo se almacenan las estructuras de directorios en el sistema de archivos UNIX? . un directorio en sí es una estructura de datos, más específicamente: una lista de objetos (archivos y números de inodo) que apuntan a listas sobre esos objetos (permisos, tipo, propietario, tamaño, etc.). Entonces cada directorio contiene su propio número de inodo, y luego los nombres de archivo y sus números de inodo. El más famoso es el inodo # 2, que es el /directorio . (Tenga en cuenta, sin embargo, que /devy /runson los sistemas de ficheros virtuales, por lo que ya que son carpetas raíz de su sistema de archivos, sino que también tienen ínodo 2; es decir, un inodo es único en su propio sistema de archivos, pero con varios sistemas de archivos conectados, tiene inodos no únicos). El diagrama tomado de la pregunta vinculada probablemente lo explica más sucintamente:

Directorio-Nodo-Bloque

Se puede acceder a toda esa información almacenada en el inodo a través de stat()llamadas al sistema, según Linux man 7 inode:

Cada archivo tiene un inodo que contiene metadatos sobre el archivo. Una aplicación puede recuperar estos metadatos usando stat (2) (o llamadas relacionadas), que devuelve una estructura estadística, o statx (2), que devuelve una estructura statx.

¿Es posible acceder a un archivo solo conociendo su número de inodo ( ref1 , ref2 )? En algunas implementaciones de Unix es posible, pero pasa por alto los permisos y las verificaciones de acceso, por lo que en Linux no está implementado, y debe atravesar el árbol del sistema de archivos (a través de, find <DIR> -inum 1234por ejemplo) para obtener un nombre de archivo y su inodo correspondiente.

En el nivel del código fuente, se define en la fuente del kernel de Linux y también es adoptado por muchos sistemas de archivos que funcionan en sistemas operativos Unix / Linux, incluidos los sistemas de archivos ext3 y ext4 (predeterminado de Ubuntu). Lo interesante: dado que los datos son solo bloques de información, Linux en realidad tiene la función inode_init_always que puede determinar si un inodo es una tubería ( inode->i_pipe). Sí, los enchufes y tuberías son técnicamente también archivos: archivos anónimos, que pueden no tener un nombre de archivo en el disco. Los sockets FIFO y Unix-Domain tienen nombres de archivo en el sistema de archivos.

Los datos en sí pueden ser únicos, pero los números de inodo no son únicos. Si tenemos un enlace duro a foo llamado foobar, eso también apuntará al inodo 123. Este inodo en sí contiene información sobre qué bloques reales de espacio en disco están ocupados por ese inodo. Y así es técnicamente cómo puede haber .sido vinculado al nombre de archivo del directorio. Bueno, casi: no puede crear enlaces duros a directorios en Linux a sí mismo , pero los sistemas de archivos puede permitir enlaces duros a directorios de una manera muy disciplinada, lo que hace que una restricción de tener sólo .y ..como enlaces duros.

Árbol de directorios

Los sistemas de archivos implementan un árbol de directorios como una de las estructuras de datos del árbol. En particular,

  • ext3 y ext4 usan HTree
  • xfs usa B + Tree
  • zfs usa hash tree

El punto clave aquí es que los directorios en sí mismos son nodos en un árbol, y los subdirectorios son nodos hijos, y cada hijo tiene un enlace de regreso al nodo padre. Por lo tanto, para un enlace de directorio, el recuento de inodos es mínimo 2 para un directorio simple (enlace al nombre del directorio /home/example/y enlace a uno mismo /home/example/.), y cada subdirectorio adicional es un enlace / nodo adicional:

# new directory has link count of 2
$ stat --format=%h .
2
# Adding subdirectories increases link count
$ mkdir subdir1
$ stat --format=%h .
3
$ mkdir subdir2
$ stat --format=%h .
4
# Count of links for root
$ stat --format=%h /
25
# Count of subdirectories, minus .
$ find / -maxdepth 1 -type d | wc -l
24

El diagrama que se encuentra en la página del curso de Ian D. Allen muestra un diagrama simplificado muy claro:

WRONG - names on things      RIGHT - names above things
=======================      ==========================

    R O O T            --->         [etc,bin,home]   <-- ROOT directory
   /   |   \                         /    |      \
etc   bin   home       --->  [passwd]  [ls,rm]  [abcd0001]
 |   /   \    \                 |      /    \       |
 |  ls   rm  abcd0001  --->     |  <data>  <data>  [.bashrc]
 |               |              |                   |
passwd       .bashrc   --->  <data>                <data>

Lo único en el diagrama DERECHO que es incorrecto es que técnicamente no se considera que los archivos estén en el árbol de directorios: agregar un archivo no tiene efectos en el recuento de enlaces:

$ mkdir subdir2
$ stat --format=%h .
4
# Adding files doesn't make difference
$ cp /etc/passwd passwd.copy
$ stat --format=%h .
4

Accediendo a directorios como si fueran archivos

Para citar a Linus Torvalds :

El punto principal con "todo es un archivo" no es que tenga algún nombre de archivo aleatorio (de hecho, los enchufes y las tuberías muestran que "archivo" y "nombre de archivo" no tienen nada que ver entre sí), sino el hecho de que puede usar herramientas para operar en diferentes cosas.

Teniendo en cuenta que un directorio es solo un caso especial de un archivo, naturalmente tiene que haber API que nos permitan abrir / leer / escribir / cerrar de manera similar a los archivos normales.

Ahí es donde dirent.hentra en juego la biblioteca C, que define la direntestructura, que puede encontrar en man 3 readdir :

   struct dirent {
       ino_t          d_ino;       /* Inode number */
       off_t          d_off;       /* Not an offset; see below */
       unsigned short d_reclen;    /* Length of this record */
       unsigned char  d_type;      /* Type of file; not supported
                                      by all filesystem types */
       char           d_name[256]; /* Null-terminated filename */
   };

Por lo tanto, en el código C tiene que definir struct dirent *entry_p, y cuando abrimos un directorio con opendir()y empezar a leer con readdir(), vamos a almacenar cada elemento en esa entry_pestructura. Por supuesto, cada elemento contendrá los campos definidos en la plantilla que se direntmuestra arriba.

El ejemplo práctico de cómo funciona esto se puede encontrar en mi respuesta sobre Cómo enumerar archivos y sus números de inodo en el directorio de trabajo actual .

Tenga en cuenta que el manual POSIX en fdopen establece que "[l] a entradas de directorio para punto y punto-punto son opcionales" y que los estados del manual readdir struct dirent solo se requieren para tener d_namey d_inocampos.

Nota sobre "escribir" en directorios: escribir en un directorio es modificar su "lista" de entradas. Por lo tanto, crear o eliminar un archivo está directamente asociado con los permisos de escritura del directorio , y agregar / eliminar archivos es la operación de escritura en dicho directorio.

Sergiy Kolodyazhnyy
fuente
2
Me niego a aceptar que los sockets son archivos;) ¿Sería "más accesible todo como un archivo"?
Rinzwind
@Rinzwind Bueno, la frase "todo es accesible como un archivo" es precisa. Los archivos normales tienen open()y los read()sockets tienen connect()y read()también. Lo que sería más exacto es que el "archivo" es realmente "datos" organizados almacenados en el disco o la memoria, y algunos archivos son anónimos, no tienen nombre de archivo. Por lo general, los usuarios piensan en los archivos en términos de ese icono en el escritorio, pero eso no es lo único que existe. Ver también unix.stackexchange.com/a/116616/85039
Sergiy Kolodyazhnyy
Bueno, la pregunta era más sobre si un directorio era un archivo. Y es. Los zócalos casi podrían ser una pregunta separada junto con las tuberías con nombre FIFO.
WinEunuuchs2Unix
Bueno, hasta ahora tengo una respuesta sobre las tuberías: askubuntu.com/a/1074550/295286 Quizás FIFOs será el próximo
Sergiy Kolodyazhnyy