Tengo un ejemplo completo mínimo donde el comportamiento de la API de Linux parece ser un poco ambiguo. La salida estándar se cierra, se vuelve a abrir y se utiliza como descriptor de un archivo normal:
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> int main(void) { system("echo > testfile"); fclose(stdout); int fd = open("testfile", O_WRONLY); if (fd < 0) { perror("open():"); return -1; } dprintf(fd, "Test\n"); system("echo Test2"); system("echo TestFFFFFFFFFFFF >> /proc/$$/fd/1"); // problem line sleep(5); dprintf(fd, "Test4\n"); system("echo Test5"); return 0; }
Antes de dormir en el archivo de testfile
veo
Test Test2 TestFFFFFFFFFFFF
Pero después de dormir, esta línea se sobrescribe:
Test Test2 Test4 Test5 FFFF
Parece que para los archivos regulares este comportamiento es extraño: si escribimos algo en el archivo, cambiamos su posición de manera apropiada (hasta el final).
Pero para stdout
parece bastante razonable: si escribimos en stdout
, rebobinamos su posición después de terminar de escribir (hasta el principio).
¿Es esto un error o la forma correcta?
PD Además del bash
, también probé dash
y ksh
, lo mismo.
La pregunta es:
¿Es esto un error o la forma correcta?
Es la manera correcta.
/proc/$$/fd/1
es un enlace simbólico a testfile
, entonces >> /proc/$$/fd/1
es lo mismo que >> testfile
. Es un proceso secundario separado que escribe en testfile
. Cuando finaliza este proceso secundario, verá las cosas que ha escrito en el archivo. No tiene ningún efecto en su proceso principal.
STDOUT_FILENO
no es especial, es como cualquier otro descriptor de archivo.
¿Por qué los procesos secundarios afectan la posición del descriptor del archivo del proceso principal? Porque los dup
de archivo duplicados se refieren a la misma descripción de archivo abierto . Léalo dos veces - "descriptor de archivo" != "descripción de archivo". Es como un doble mapeo. Decimos que los descriptores de archivo no se copian , se duplican , comparten el desplazamiento del archivo y los indicadores de estado del archivo, porque comparten la descripción del archivo. Ver man 2 dup
y man 2 open
.
Cuando un proceso separado abre el mismo archivo, tiene una descripción de archivo separada, por lo que no afecta la descripción de su archivo de proceso.
(Tiene un nombre tan malo. Mentalmente llamo "descriptor de archivo" como "manejador del sistema" (porque puede referirse a cualquier cosa que el kernel quiera) y llamo "descripción de archivo" como "datos de archivo" (si es un archivo, puede ser cualquier cosa que el núcleo quiera). Entonces está claro: este "identificador del sistema" se refiere a estos "datos de archivo" y el "identificador del sistema" se duplica entre procesos, lo que significa que dos "identificadores del sistema" se refieren a los mismos "datos de archivo" abiertos. "datos de archivo" almacena la posición del archivo y los bloqueos de archivos y demás. Los procesos tienen "identificadores", no "datos". Los "datos de archivo" se eliminan automáticamente, cuando se elimina el último "identificador del sistema" (el man 2 unlink
). )