Saltar al contenido

¿En qué se diferencia una FIFO (tubería con nombre) de una tubería normal (tubería sin nombre)?

Hacemos una verificación profunda cada noticias en nuestra página web con la meta de mostrarte en todo momento la información más veraz y actual.

Solución:

“Pipa con nombre” es en realidad un muy nombre exacto para lo que es: es como una tubería normal, excepto que tiene un nombre (en un sistema de archivos).

Una tubería: la normal, sin nombre (“anónimo”) que se usa en some-command | grep pattern es un tipo especial de archivo. Y me refiero a archivo, lo lees y escribes como lo haces con cualquier otro archivo. A Grep realmente no le importa¹ que esté leyendo desde una tubería en lugar de una terminal³ o un archivo ordinario.

Técnicamente, lo que sucede detrás de escena es que stdin, stdout y stderr son tres archivos abiertos (descriptores de archivo) que se pasan a cada ejecución de comando. Los descriptores de archivo (que se utilizan en cada llamada al sistema para leer / escribir / etc. Archivos) son solo números; stdin, stdout y stderr son descriptores de archivo 0, 1 y 2. Entonces, cuando su shell se configura some-command | grep lo que hace es algo esto:

  1. Pide al kernel una canalización anónima. No hay nombre, así que esto no se puede hacer con open como para un archivo normal, en su lugar, se hace con pipe o pipe2, que devuelve dos descriptores de archivo.

  2. Bifurca un proceso hijo (fork() crea una copia del proceso principal; ambos lados de la tubería están abiertos aquí), copia el lado de escritura de la tubería en fd 1 (stdout). El kernel tiene una llamada al sistema para copiar los números de descriptor de archivos; es dup2() o dup3(). Luego cierra el lado de lectura y otra copia del lado de escritura. Finalmente, usa execve ejecutar some-command. Dado que la tubería es fd 1, stdout de some-command es la pipa.

  3. Bifurcaciones de otro proceso hijo. Esta vez, duplica el lado de lectura de la tubería a fd 0 (stdin) y ejecuta grep. Entonces grep leerá de la tubería como stdin.

  4. Luego espera a que ambos niños salgan.

  5. En este punto, el kernel nota que la tubería ya no está abierta y la basura la recoge. Eso es lo que realmente destruye la tubería.

Una tubería con nombre simplemente le da un nombre a esa tubería anónima colocándola en el sistema de archivos. Y ahora ningún proceso, en cualquier momento en el futuro, puede obtener un descriptor de archivo para la tubería mediante el uso de un ordinario open syscall. Conceptualmente, la tubería no se destruirá hasta que todos los lectores / escritores la hayan cerrado y esté unlinked desde el sistema de archivos²

Por cierto, así es como funcionan los archivos en general en Unix. unlink (la llamada al sistema detrás rm) simplemente elimina uno de los nombres del archivo; solo cuando se eliminen todos los nombres y no haya nada abierto el archivo, se eliminará realmente. Un par de respuestas aquí exploran esto:

  • ¿Por qué los enlaces duros parecen ocupar el mismo espacio que los originales?
  • ¿Cómo puede un programa de registro continuar registrándose en un archivo eliminado?
  • ¿Qué está haciendo Linux de manera diferente que me permite eliminar / reemplazar archivos donde Windows se quejaría de que el archivo está actualmente en uso?

NOTAS AL PIE

  1. Técnicamente, esto probablemente no sea true – Probablemente sea posible hacer algunas optimizaciones sabiendo, y las implementaciones reales de grep a menudo se han optimizado en gran medida. Pero conceptualmente no le importa (y de hecho, una implementación sencilla de grep no lo haría).
  2. Por supuesto, el kernel no mantiene todas las estructuras de datos en la memoria para siempre, sino que las recrea, de forma transparente, cada vez que el primer programa abre la tubería nombrada (y luego las mantiene mientras esté abierta). Así que es como si existieran tanto tiempo como el nombre.
  3. Terminal no es un lugar común para leer desde grep, pero es el stdin predeterminado cuando no especifica otro. Entonces, si escribe solo grep pattern en tu caparazón, grep estará leyendo desde la terminal. El único uso que se le ocurre a esto es si está a punto de pegar algo en la terminal.
  4. En Linux, las canalizaciones anónimas en realidad se crean en un sistema de archivos especial, pipefs. Consulte Cómo funcionan las canalizaciones en Linux para obtener más detalles. Tenga en cuenta que este es un detalle de implementación interna de Linux.

Creo que estas consiguiendo mixed entre la sintaxis de shell para canalizaciones frente a la programación de sistemas Unix subyacente. Un pipe / FIFO es un tipo de archivo que no se almacena en el disco, sino que pasa datos de un escritor a un lector a través de un búfer en el kernel.

Una tubería / FIFO funciona de la misma manera si el escritor y el lector se conectaron haciendo llamadas al sistema como open("/path/to/named_pipe", O_WRONLY);, o con un pipe(2) para crear una nueva canalización anónima y devolver descriptores de archivos abiertos a los lados de lectura y escritura.

fstat(2) en el descriptor de archivo de tubería le dará sb.st_mode & S_IFMT == S_IFIFO de cualquier manera.


Cuando corres foo | bar:

  • El shell se bifurca como de costumbre para cualquier comando no incorporado
  • Luego hace un pipe(2) llamada al sistema para obtener dos descriptores de archivo: la entrada y la salida de una tubería anónima.
  • Entonces se bifurca de nuevo.
  • El niño (donde fork() devuelto 0)
    • cierra el lado de lectura de la tubería (dejando abierto el fd de escritura)
    • y redireccionamientos stdout a la escritura fd con dup2(pipefd[1], 1)
    • entonces hace execve("/usr/bin/foo", ...)
  • El padre (donde fork() devolvió el PID secundario distinto de 0)
    • cierra el lado de escritura de la tubería (dejando abierto el fd de lectura)
    • y redireccionamientos stdin de la lectura fd con dup2(pipefd[0], 0)
    • entonces hace execve("/usr/bin/bar", ...)

Te pones en una situación muy similar si corres foo > named_pipe & bar < named_pipe.

Una tubería con nombre es un punto de encuentro para que los procesos establezcan tuberías entre sí.


La situación es similar a los archivos tmp anónimos frente a los archivos con nombres. Usted puede open("/path/to/dir", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR); para crear un archivo temporal sin nombre (O_TMPFILE), como si hubieras abierto "/path/to/dir/tmpfile" con O_CREAT y luego lo desvinculó, dejándolo con un descriptor de archivo a un archivo eliminado.

Utilizando linkat, incluso puede vincular ese archivo anónimo al sistema de archivos, dándole un nombre, si fue creado con O_TMPFILE. (Sin embargo, no puede hacer esto en Linux para archivos que creó con un nombre y luego eliminó).

Comentarios y valoraciones del post

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)


Tags : / /

Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *