GNU/Linux >> Tutoriales Linux >  >> Linux

El puntero del archivo C cambia después de la bifurcación y ejecución (fallida)

Gracias a Jonathan Leffler por orientarnos en la dirección correcta.

Aunque su programa no me produce el mismo comportamiento inesperado en CentOS 7/GCC 4.8.5/GLIBC 2.17, es plausible que observe un comportamiento diferente. El comportamiento de su programa es, de hecho, indefinido de acuerdo con POSIX (en el que confía para fork ). Aquí hay algunos extractos de la sección relevante (énfasis agregado):

Se puede acceder a una descripción de archivo abierto a través de un descriptor de archivo, que se crea usando funciones como open() o pipe() , o a través de una secuencia, que se crea mediante funciones como fopen() o popen() .Ya sea un descriptor de archivo o una secuencia se denomina "manejador" en la descripción del archivo abierto al que se refiere; una descripción de archivo abierto puede tener varios identificadores.

[...]

El resultado de las llamadas a funciones que involucran cualquier identificador (el "identificador activo") se define en otra parte de este volumen de POSIX.1-2017, pero si se usan dos o más identificadores, y cualquiera de ellos es un flujo, la aplicación debe garantizar que su las acciones se coordinan como se describe a continuación. Si no se hace esto, el resultado es indefinido .

[...]

Para que un identificador se convierta en identificador activo, la aplicación debe asegurarse de que las siguientes acciones se realicen entre el último uso del identificador (el identificador activo actual) y el primer uso del segundo identificador (el identificador activo futuro). El segundo identificador se convierte en el identificador activo. [...]

No es necesario que los identificadores estén en el mismo proceso para que se apliquen estas reglas.

Tenga en cuenta que después de un fork() , existen dos identificadores donde existía uno antes. La aplicación debe garantizar que, si se puede acceder a ambos identificadores, ambos se encuentran en un estado en el que el otro podría convertirse en el identificador activo primero. [Cuando esté sujeta a la calificación anterior, la] solicitud se preparará para un fork() exactamente como si se tratara de un cambio de controlador activo. (Si la única acción realizada por uno de los procesos es una de las funciones ejecutivas o _exit() (no exit() ), nunca se accede al identificador en ese proceso. )

Para el primer identificador, se aplica la primera condición aplicable a continuación. [Una lista impresionantemente larga de alternativas que no se aplican a la situación del OP...]

  • Si la transmisión está abierta con un modo que permite la lectura y la descripción del archivo abierto subyacente se refiere a un dispositivo que es capaz de buscar, la aplicación deberá realizar un fflush() , o la transmisión se cerrará.

Para el segundo mango:

  • Si cualquier identificador activo anterior ha sido utilizado por una función que cambió explícitamente el desplazamiento del archivo, excepto como se requiere anteriormente para el primer identificador, la aplicación realizará un lseek() o fseek() (según sea apropiado para el tipo de manija) a una ubicación adecuada.

Por lo tanto, para que el programa del OP acceda a la misma transmisión tanto en el padre como en el hijo, POSIX exige que el padre fflush() stdin antes de bifurcar, y que el niño fseek() después de empezar. Luego, después de esperar a que el niño termine, el padre debe fseek() la corriente. Sin embargo, dado que sabemos que el ejecutivo del niño fallará, el requisito de enjuagar y buscar puede evitarse haciendo que el niño use _exit() (que no accede a la transmisión) en lugar de exit() .

Cumplir con las disposiciones de POSIX produce lo siguiente:

Cuando se siguen estas reglas, independientemente de la secuencia de identificadores utilizados, las implementaciones deben garantizar que una aplicación, incluso una que consta de varios procesos, arroje resultados correctos:no se perderán ni duplicarán datos al escribir, y todos los datos se escribirán en orden, excepto como solicitado por seek.

Vale la pena señalar, sin embargo, que

Está definido por la implementación si, y bajo qué condiciones, todas las entradas se ven exactamente una vez.

Entiendo que puede ser algo insatisfactorio escuchar simplemente que sus expectativas para el comportamiento del programa no están justificadas por los estándares relevantes, pero eso es todo lo que hay. Los procesos padre e hijo tienen algunos datos compartidos relevantes en forma de una descripción de archivo abierto común (con los que tienen identificadores asociados separados), y parece probable que sea el vehículo para el comportamiento inesperado (e indefinido), pero no hay base para predecir el comportamiento específico que ve, ni el comportamiento diferente que veo para el mismo programa.


Linux
  1. ¿Descriptores de archivos y secuencias de comandos de Shell?

  2. ¿Permisos de archivo y ahorro?

  3. ¿Redirigir y canalizar la salida?

  4. Evitar la herencia de descriptores de archivos durante la bifurcación de Linux

  5. unix - cabeza Y cola del archivo

Aprenda y use las llamadas al sistema fork(), vfork(), wait() y exec() en todos los sistemas Linux

Tuberías, dup2 y exec()

Linux ext4 restaurar archivos y derechos de acceso al directorio después de una mala copia de seguridad/restauración

vmlinuz e initrd no encontrados después de compilar el kernel?

El sistema rechaza SSH y se atasca en 'arrancar' después de la instalación de systemd

Archivo de unidad Systemd - WantedBy y After